ADDED .dockerignore Index: .dockerignore ================================================================== --- .dockerignore +++ .dockerignore @@ -0,0 +1,26 @@ +_FOSSIL_ +.fslckout +ajax +art +autosetup +bld +compat +debian +fossil +fossil.exe +setup +src +test +tools +win +wbld +win +www +*.a +*.lib +*.log +*.manifest +*.o +*.obj +*.pdb +*.res Index: Dockerfile ================================================================== --- Dockerfile +++ Dockerfile @@ -2,21 +2,21 @@ # Dockerfile for Fossil ### FROM fedora:21 ### Now install some additional parts we will need for the build -RUN yum update -y && yum install -y gcc make zlib-devel openssl-devel tcl-devel tar && yum clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil +RUN yum update -y && yum install -y gcc make zlib-devel openssl-devel tcl tar && yum clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil -### If you want to build "trunk", change the next line accordingly. -ENV FOSSIL_INSTALL_VERSION release +### If you want to build "release", change the next line accordingly. +ENV FOSSIL_INSTALL_VERSION trunk RUN curl "http://www.fossil-scm.org/index.html/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx RUN cd fossil-src && ./configure --disable-lineedit --disable-fusefs --json --with-th1-docs --with-th1-hooks --with-tcl --with-tcl-stubs --with-tcl-private-stubs && make; RUN cp fossil-src/fossil /usr/bin && rm -rf fossil-src && chmod a+rx /usr/bin/fossil && mkdir -p /opt/fossil && chown fossil:fossil /opt/fossil ### Build is done, remove modules no longer needed -RUN yum remove -y gcc make zlib-devel openssl-devel tcl-devel tar && yum clean all +RUN yum remove -y gcc make zlib-devel openssl-devel tar && yum clean all USER fossil ENV HOME /opt/fossil ADDED skins/README.md Index: skins/README.md ================================================================== --- skins/README.md +++ skins/README.md @@ -0,0 +1,27 @@ +Built-in Skins +============== + +Each subdirectory under this folder describes a built-in "skin". +There are three files in each subdirectory for the CSS, the header, +and the footer for the skin. + +To improve an existing built-in skin, simply edit the appropriate +files and recompile. + +To add a new skin: + + 1. Create a new subdirectory under skins/. (The new directory is + called "skins/newskin" below but you should use a new original + name, of course.) + + 2. Add files skins/newskin/css.txt, skins/newskin/header.txt, + and skins/newskin/footer.txt. Be sure to "fossil add" these files. + + 3. Go to the src/ directory and rerun "tclsh makemake.tcl". This + step rebuilds the various makefiles so that they have dependencies + on the skin files you just installed. + + 4. Edit the BuiltinSkin[] array near the top of the src/skins.c source + file so that it describes and references the "newskin" skin. + + 5. Type "make" to rebuild. ADDED skins/black_and_white/css.txt Index: skins/black_and_white/css.txt ================================================================== --- skins/black_and_white/css.txt +++ skins/black_and_white/css.txt @@ -0,0 +1,174 @@ +/* General settings for the entire page */ +body { + margin:0px 0px 0px 0px; + padding:0px; + font-family:verdana, arial, helvetica, "sans serif"; + color:#333; + background-color:white; +} + +/* consistent colours */ +h2 { + color: #333; +} +h3 { + color: #333; +} + +/* The project logo in the upper left-hand corner of each page */ +div.logo { + display: table-cell; + text-align: left; + vertical-align: bottom; + font-weight: bold; + color: #333; + white-space: nowrap; +} + +/* The page title centered at the top of each page */ +div.title { + display: table-cell; + font-size: 2em; + font-weight: bold; + text-align: center; + color: #333; + vertical-align: bottom; + width: 100%; +} + +/* The login status message in the top right-hand corner */ +div.status { + display: table-cell; + padding-right: 10px; + text-align: right; + vertical-align: bottom; + padding-bottom: 5px; + color: #333; + font-size: 0.8em; + font-weight: bold; + white-space: nowrap; +} + +/* The header across the top of the page */ +div.header { + margin:10px 0px 10px 0px; + padding:1px 0px 0px 20px; + border-style:solid; + border-color:black; + border-width:1px 0px; + background-color:#eee; +} + +/* The main menu bar that appears at the top left of the page beneath +** the header. Width must be co-ordinated with the container below */ +div.mainmenu { + float: left; + margin-left: 10px; + margin-right: 10px; + font-size: 0.9em; + font-weight: bold; + padding:5px; + background-color:#eee; + border:1px solid #999; + width:8em; +} + +/* Main menu is now a list */ +div.mainmenu ul { + padding: 0; + list-style:none; +} +div.mainmenu a, div.mainmenu a:visited{ + padding: 1px 10px 1px 10px; + color: #333; + text-decoration: none; +} +div.mainmenu a:hover { + color: #eee; + background-color: #333; +} + +/* Container for the sub-menu and content so they don't spread +** out underneath the main menu */ +#container { + padding-left: 9em; +} + +/* The submenu bar that *sometimes* appears below the main menu */ +div.submenu, div.sectionmenu { + padding: 3px 10px 3px 10px; + font-size: 0.9em; + text-align: center; + border:1px solid #999; + border-width:1px 0px; + background-color: #eee; + color: #333; +} +div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, +div.sectionmenu>a.button:visited { + padding: 3px 10px 3px 10px; + color: #333; + text-decoration: none; +} +div.submenu a:hover, div.sectionmenu>a.button:hover { + color: #eee; + background-color: #333; +} + +/* All page content from the bottom of the menu or submenu down to +** the footer */ +div.content { + padding: 2ex 1ex 0ex 2ex; +} + +/* Some pages have section dividers */ +div.section { + margin-bottom: 0px; + margin-top: 1em; + padding: 1px 1px 1px 1px; + font-size: 1.2em; + font-weight: bold; + border-style:solid; + border-color:#999; + border-width:1px 0px; + background-color: #eee; + color: #333; + white-space: nowrap; +} + +/* The "Date" that occurs on the left hand side of timelines */ +div.divider { + background: #eee; + border: 2px #999 solid; + font-size: 1em; font-weight: normal; + padding: .25em; + margin: .2em 0 .2em 0; + float: left; + clear: left; + color: #333; + white-space: nowrap; +} + +/* The footer at the very bottom of the page */ +div.footer { + font-size: 0.8em; + margin-top: 12px; + padding: 5px 10px 5px 10px; + text-align: right; + background-color: #eee; + color: #555; +} + +/* blocks */ +pre.verbatim { + background-color: #f5f5f5; + padding: 0.5em; + white-space: pre-wrap; +} + +/* The label/value pairs on (for example) the ci page */ +table.label-value th { + vertical-align: top; + text-align: right; + padding: 0.2ex 2ex; +} ADDED skins/black_and_white/footer.txt Index: skins/black_and_white/footer.txt ================================================================== --- skins/black_and_white/footer.txt +++ skins/black_and_white/footer.txt @@ -0,0 +1,4 @@ + + ADDED skins/black_and_white/header.txt Index: skins/black_and_white/header.txt ================================================================== --- skins/black_and_white/header.txt +++ skins/black_and_white/header.txt @@ -0,0 +1,54 @@ + + + +$<project_name>: $<title> + + + + +
+ +
$</div> + <div class="status"><th1> + if {[info exists login]} { + puts "Logged in as $login" + } else { + puts "Not logged in" + } + </th1></div> +</div> +<div class="mainmenu"> +<th1> +html "<a href='$home$index_page'>Home</a>\n" +if {[anycap jor]} { + html "<a href='$home/timeline'>Timeline</a>\n" +} +if {[hascap oh]} { + html "<a href='$home/tree?ci=tip'>Files</a>\n" +} +if {[hascap o]} { + html "<a href='$home/brlist'>Branches</a>\n" + html "<a href='$home/taglist'>Tags</a>\n" +} +if {[hascap r]} { + html "<a href='$home/reportlist'>Tickets</a>\n" +} +if {[hascap j]} { + html "<a href='$home/wiki'>Wiki</a>\n" +} +if {[hascap s]} { + html "<a href='$home/setup'>Admin</a>\n" +} elseif {[hascap a]} { + html "<a href='$home/setup_ulist'>Users</a>\n" +} +if {[info exists login]} { + html "<a href='$home/login'>Logout</a>\n" +} else { + html "<a href='$home/login'>Login</a>\n" +} +</th1></ul></div> ADDED skins/default/css.txt Index: skins/default/css.txt ================================================================== --- skins/default/css.txt +++ skins/default/css.txt @@ -0,0 +1,145 @@ +/* General settings for the entire page */ +body { + margin: 0ex 1ex; + padding: 0px; + background-color: white; + font-family: sans-serif; +} + +/* The project logo in the upper left-hand corner of each page */ +div.logo { + display: table-cell; + text-align: center; + vertical-align: bottom; + font-weight: bold; + color: #558195; + min-width: 200px; + white-space: nowrap; +} + +/* The page title centered at the top of each page */ +div.title { + display: table-cell; + font-size: 2em; + font-weight: bold; + text-align: center; + padding: 0 0 0 1em; + color: #558195; + vertical-align: bottom; + width: 100%; +} + +/* The login status message in the top right-hand corner */ +div.status { + display: table-cell; + text-align: right; + vertical-align: bottom; + color: #558195; + font-size: 0.8em; + font-weight: bold; + min-width: 200px; + white-space: nowrap; +} + +/* The header across the top of the page */ +div.header { + display: table; + width: 100%; +} + +/* The main menu bar that appears at the top of the page beneath +** the header */ +div.mainmenu { + padding: 5px 10px 5px 10px; + font-size: 0.9em; + font-weight: bold; + text-align: center; + letter-spacing: 1px; + background-color: #558195; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + color: white; +} + +/* The submenu bar that *sometimes* appears below the main menu */ +div.submenu, div.sectionmenu { + padding: 3px 10px 3px 0px; + font-size: 0.9em; + text-align: center; + background-color: #456878; + color: white; +} +div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, +div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { + padding: 3px 10px 3px 10px; + color: white; + text-decoration: none; +} +div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { + color: #558195; + background-color: white; +} + +/* All page content from the bottom of the menu or submenu down to +** the footer */ +div.content { + padding: 0ex 1ex 1ex 1ex; + border: solid #aaa; + border-width: 1px; +} + +/* Some pages have section dividers */ +div.section { + margin-bottom: 0px; + margin-top: 1em; + padding: 1px 1px 1px 1px; + font-size: 1.2em; + font-weight: bold; + background-color: #558195; + color: white; + white-space: nowrap; +} + +/* The "Date" that occurs on the left hand side of timelines */ +div.divider { + background: #a1c4d4; + border: 2px #558195 solid; + font-size: 1em; font-weight: normal; + padding: .25em; + margin: .2em 0 .2em 0; + float: left; + clear: left; + white-space: nowrap; +} + +/* The footer at the very bottom of the page */ +div.footer { + clear: both; + font-size: 0.8em; + padding: 5px 10px 5px 10px; + text-align: right; + background-color: #558195; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + color: white; +} + +/* Hyperlink colors in the footer */ +div.footer a { color: white; } +div.footer a:link { color: white; } +div.footer a:visited { color: white; } +div.footer a:hover { background-color: white; color: #558195; } + +/* verbatim blocks */ +pre.verbatim { + background-color: #f5f5f5; + padding: 0.5em; + white-space: pre-wrap; +} + +/* The label/value pairs on (for example) the ci page */ +table.label-value th { + vertical-align: top; + text-align: right; + padding: 0.2ex 2ex; +} ADDED skins/default/footer.txt Index: skins/default/footer.txt ================================================================== --- skins/default/footer.txt +++ skins/default/footer.txt @@ -0,0 +1,6 @@ +<div class="footer"> +This page was generated in about +<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by +Fossil version $manifest_version $manifest_date +</div> +</body></html> ADDED skins/default/header.txt Index: skins/default/header.txt ================================================================== --- skins/default/header.txt +++ skins/default/header.txt @@ -0,0 +1,53 @@ +<html> +<head> +<base href="$baseurl/$current_page" /> +<title>$<project_name>: $<title> + + + + +
+ +
$
$</div> + <div class="status"><th1> + if {[info exists login]} { + puts "Logged in as $login" + } else { + puts "Not logged in" + } + </th1></div> +</div> +<div class="mainmenu"> +<th1> +html "<a href='$home$index_page'>Home</a>\n" +if {[anycap jor]} { + html "<a href='$home/timeline'>Timeline</a>\n" +} +if {[hascap oh]} { + html "<a href='$home/tree?ci=tip'>Files</a>\n" +} +if {[hascap o]} { + html "<a href='$home/brlist'>Branches</a>\n" + html "<a href='$home/taglist'>Tags</a>\n" +} +if {[hascap r]} { + html "<a href='$home/reportlist'>Tickets</a>\n" +} +if {[hascap j]} { + html "<a href='$home/wiki'>Wiki</a>\n" +} +if {[hascap s]} { + html "<a href='$home/setup'>Admin</a>\n" +} elseif {[hascap a]} { + html "<a href='$home/setup_ulist'>Users</a>\n" +} +if {[info exists login]} { + html "<a href='$home/login'>Logout</a>\n" +} else { + html "<a href='$home/login'>Login</a>\n" +} +</th1></div> ADDED skins/eagle/css.txt Index: skins/eagle/css.txt ================================================================== --- skins/eagle/css.txt +++ skins/eagle/css.txt @@ -0,0 +1,263 @@ +/* General settings for the entire page */ +body { + margin: 0ex 1ex; + padding: 0px; + background-color: #485D7B; + font-family: sans-serif; + color: white; +} + +/* The project logo in the upper left-hand corner of each page */ +div.logo { + display: table-cell; + text-align: center; + vertical-align: bottom; + font-weight: bold; + color: white; + padding: 5 0 5 0em; + white-space: nowrap; +} + +/* The page title centered at the top of each page */ +div.title { + display: table-cell; + font-size: 2em; + font-weight: bold; + text-align: left; + padding: 0 0 0 1em; + color: white; + vertical-align: bottom; + width: 100%; +} + +/* The login status message in the top right-hand corner */ +div.status { + display: table-cell; + text-align: right; + vertical-align: bottom; + color: white; + font-size: 0.8em; + font-weight: bold; + min-width: 200px; + white-space: nowrap; +} + +/* The header across the top of the page */ +div.header { + display: table; + width: 100%; +} + +/* The main menu bar that appears at the top of the page beneath +** the header */ +div.mainmenu { + padding: 5px 10px 5px 10px; + font-size: 0.9em; + font-weight: bold; + text-align: center; + letter-spacing: 1px; + background-color: #76869D; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + color: white; +} + +/* The submenu bar that *sometimes* appears below the main menu */ +div.submenu, div.sectionmenu { + padding: 3px 10px 3px 0px; + font-size: 0.9em; + font-weight: bold; + text-align: center; + background-color: #485D7B; + color: white; +} +div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, +div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { + padding: 3px 10px 3px 10px; + color: white; + text-decoration: none; +} +div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { + text-decoration: underline; +} + +/* All page content from the bottom of the menu or submenu down to +** the footer */ +div.content { + padding: 0ex 1ex 0ex 2ex; +} + +/* Some pages have section dividers */ +div.section { + margin-bottom: 0px; + margin-top: 1em; + padding: 1px 1px 1px 1px; + font-size: 1.2em; + font-weight: bold; + background-color: #485D7B; + color: white; + white-space: nowrap; +} + +/* The "Date" that occurs on the left hand side of timelines */ +div.divider { + background: #9DB0CC; + color: white; + border: 2px white solid; + font-size: 1em; font-weight: normal; + padding: .25em; + margin: .2em 0 .2em 0; + float: left; + clear: left; + white-space: nowrap; +} + +/* The footer at the very bottom of the page */ +div.footer { + clear: both; + font-size: 0.8em; + margin-top: 12px; + padding: 5px 10px 5px 10px; + text-align: right; + background-color: #485D7B; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + color: white; +} + +/* Hyperlink colors in the footer */ +a { color: white; } +a:link { color: white; } +a:visited { color: white; } +a:hover { color: #9DB0CC; } + +/* verbatim blocks */ +pre.verbatim { + background-color: #485D7B; + color: white; + padding: 0.5em; + white-space: pre-wrap; +} + +/* The label/value pairs on (for example) the ci page */ +table.label-value th { + vertical-align: top; + text-align: right; + padding: 0.2ex 2ex; +} + +/* The nomenclature sidebox for branches,.. */ +div.sidebox { + float: right; + background-color: #485D7B; + border-width: medium; + border-style: double; + margin: 10px; +} + +/* the format for the timeline data table */ +table.timelineTable { + cellspacing: 0; + border: 0; + cellpadding: 0; + font-family: "courier new"; +} + +/* 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; + white-space: pre; + width: 98%; + border: 1px #000 dashed; + margin-left: auto; + margin-right: auto; +} + +/* format for the layout table, used for the captcha display */ +table.captcha { + margin: auto; + padding: 10px; + border-width: 4px; + border-style: double; + border-color: white; +} + +/* format for the user list table on the user setup page */ +table.usetupUserList { + outline-style: double; + outline-width: 1px; + border-color: white; + padding: 10px; +} + +/* color for capabilities, inherited by reader */ +span.ueditInheritReader { + color: white; +} + +/* format for values on ticket display page */ +td.tktDspValue { + text-align: left; + vertical-align: top; + background-color: #485D7B; +} + +/* format for example table cells on the report edit page */ +td.rpteditex { + border-width: thin; + border-color: white; + border-style: solid; +} + +/* List of files in a timeline */ +ul.filelist { + margin-top: 3px; + line-height: 100%; +} + +/* side-by-side diff display */ +div.sbsdiff { + font-family: monospace; + font-size: smaller; + white-space: pre; +} + +/* context diff display */ +div.udiff { + font-family: monospace; + white-space: pre; +} + +/* changes in a diff */ +span.diffchng { + background-color: rgb(170, 170, 140); +} + +/* added code in a diff */ +span.diffadd { + background-color: rgb(100, 200, 100); +} + +/* deleted in a diff */ +span.diffrm { + background-color: rgb(230, 110, 110); +} + +/* suppressed lines in a diff */ +span.diffhr { + display: inline-block; + margin: .5em 0 1em; + color: rgb(150, 150, 140); +} + +/* line numbers in a diff */ +span.diffln { + color: white; +} + +#canvas { + background-color: #485D7B; +} ADDED skins/eagle/footer.txt Index: skins/eagle/footer.txt ================================================================== --- skins/eagle/footer.txt +++ skins/eagle/footer.txt @@ -0,0 +1,24 @@ +<div class="footer"> + <th1> + proc getTclVersion {} { + if {[catch {tclEval info patchlevel} tclVersion] == 0} { + return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" + } + return "" + } + proc getVersion { version } { + set length [string length $version] + return [string range $version 1 [expr {$length - 2}]] + } + set version [getVersion $manifest_version] + set tclVersion [getTclVersion] + set fossilUrl https://www.fossil-scm.org + </th1> + This page was generated in about + <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by + <a href="$fossilUrl/">Fossil</a> + version $release_version $tclVersion + <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> + <a href="$fossilUrl/index.html/timeline?c=$manifest_date&y=ci">$manifest_date</a> +</div> +</body></html> ADDED skins/eagle/header.txt Index: skins/eagle/header.txt ================================================================== --- skins/eagle/header.txt +++ skins/eagle/header.txt @@ -0,0 +1,127 @@ +<html> +<head> +<base href="$baseurl/$current_page" /> +<title>$<project_name>: $<title> + + + + +
+ +
$</div> + <div class="status"><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> +<script> +function updateClock(){ + var e = document.getElementById("clock"); + if(e){ + var d = new Date(); + e.innerHTML=d.toISOString().replace("T"," ").replace(/:\d\d\.\d+Z/,""); + setTimeout("updateClock();",(60-d.getSeconds())*1000); + } +} +updateClock(); +</script> +<div class="mainmenu"> +<th1> +html "<a href='$home$index_page'>Home</a>\n" +html "<a href='$home/help'>Help</a>\n" +if {[anycap jor]} { + html "<a href='$home/timeline'>Timeline</a>\n" +} +if {[hascap oh]} { + html "<a href='$home/tree?ci=tip'>Files</a>\n" +} +if {[hascap o]} { + html "<a href='$home/brlist'>Branches</a>\n" + html "<a href='$home/taglist'>Tags</a>\n" +} +if {[hascap r]} { + html "<a href='$home/reportlist'>Tickets</a>\n" +} +if {[hascap j]} { + html "<a href='$home/wiki'>Wiki</a>\n" +} +if {[hascap s]} { + html "<a href='$home/setup'>Admin</a>\n" +} elseif {[hascap a]} { + html "<a href='$home/setup_ulist'>Users</a>\n" +} +if {[info exists login]} { + html "<a href='$home/login'>Logout</a>\n" +} else { + html "<a href='$home/login'>Login</a>\n" +} +</th1></div> ADDED skins/enhanced1/css.txt Index: skins/enhanced1/css.txt ================================================================== --- skins/enhanced1/css.txt +++ skins/enhanced1/css.txt @@ -0,0 +1,145 @@ +/* General settings for the entire page */ +body { + margin: 0ex 1ex; + padding: 0px; + background-color: white; + font-family: sans-serif; +} + +/* The project logo in the upper left-hand corner of each page */ +div.logo { + display: table-cell; + text-align: center; + vertical-align: bottom; + font-weight: bold; + color: #558195; + min-width: 200px; + white-space: nowrap; +} + +/* The page title centered at the top of each page */ +div.title { + display: table-cell; + font-size: 2em; + font-weight: bold; + text-align: center; + padding: 0 0 0 1em; + color: #558195; + vertical-align: bottom; + width: 100%; +} + +/* The login status message in the top right-hand corner */ +div.status { + display: table-cell; + text-align: right; + vertical-align: bottom; + color: #558195; + font-size: 0.8em; + font-weight: bold; + min-width: 200px; + white-space: nowrap; +} + +/* The header across the top of the page */ +div.header { + display: table; + width: 100%; +} + +/* The main menu bar that appears at the top of the page beneath +** the header */ +div.mainmenu { + padding: 5px 10px 5px 10px; + font-size: 0.9em; + font-weight: bold; + text-align: center; + letter-spacing: 1px; + background-color: #558195; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + color: white; +} + +/* The submenu bar that *sometimes* appears below the main menu */ +div.submenu, div.sectionmenu { + padding: 3px 10px 3px 0px; + font-size: 0.9em; + text-align: center; + background-color: #456878; + color: white; +} +div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, +div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { + padding: 3px 10px 3px 10px; + color: white; + text-decoration: none; +} +div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { + color: #558195; + background-color: white; +} + +/* All page content from the bottom of the menu or submenu down to +** the footer */ +div.content { + padding: 0ex 1ex 1ex 1ex; + border: solid #aaa; + border-width: 1px; +} + +/* Some pages have section dividers */ +div.section { + margin-bottom: 0px; + margin-top: 1em; + padding: 1px 1px 1px 1px; + font-size: 1.2em; + font-weight: bold; + background-color: #558195; + color: white; + white-space: nowrap; +} + +/* The "Date" that occurs on the left hand side of timelines */ +div.divider { + background: #a1c4d4; + border: 2px #558195 solid; + font-size: 1em; font-weight: normal; + padding: .25em; + margin: .2em 0 .2em 0; + float: left; + clear: left; + white-space: nowrap; +} + +/* The footer at the very bottom of the page */ +div.footer { + clear: both; + font-size: 0.8em; + padding: 5px 10px 5px 10px; + text-align: right; + background-color: #558195; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + color: white; +} + +/* Hyperlink colors in the footer */ +div.footer a { color: white; } +div.footer a:link { color: white; } +div.footer a:visited { color: white; } +div.footer a:hover { background-color: white; color: #558195; } + +/* verbatim blocks */ +pre.verbatim { + background-color: #f5f5f5; + padding: 0.5em; + white-space: pre-wrap; +} + +/* The label/value pairs on (for example) the ci page */ +table.label-value th { + vertical-align: top; + text-align: right; + padding: 0.2ex 2ex; +} ADDED skins/enhanced1/footer.txt Index: skins/enhanced1/footer.txt ================================================================== --- skins/enhanced1/footer.txt +++ skins/enhanced1/footer.txt @@ -0,0 +1,24 @@ +<div class="footer"> + <th1> + proc getTclVersion {} { + if {[catch {tclEval info patchlevel} tclVersion] == 0} { + return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" + } + return "" + } + proc getVersion { version } { + set length [string length $version] + return [string range $version 1 [expr {$length - 2}]] + } + set version [getVersion $manifest_version] + set tclVersion [getTclVersion] + set fossilUrl https://www.fossil-scm.org + </th1> + This page was generated in about + <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by + <a href="$fossilUrl/">Fossil</a> + version $release_version $tclVersion + <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> + <a href="$fossilUrl/index.html/timeline?c=$manifest_date&y=ci">$manifest_date</a> +</div> +</body></html> ADDED skins/enhanced1/header.txt Index: skins/enhanced1/header.txt ================================================================== --- skins/enhanced1/header.txt +++ skins/enhanced1/header.txt @@ -0,0 +1,127 @@ +<html> +<head> +<base href="$baseurl/$current_page" /> +<title>$<project_name>: $<title> + + + + +
+ +
$</div> + <div class="status"><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> +<script> +function updateClock(){ + var e = document.getElementById("clock"); + if(e){ + var d = new Date(); + e.innerHTML=d.toISOString().replace("T"," ").replace(/:\d\d\.\d+Z/,""); + setTimeout("updateClock();",(60-d.getSeconds())*1000); + } +} +updateClock(); +</script> +<div class="mainmenu"> +<th1> +html "<a href='$home$index_page'>Home</a>\n" +html "<a href='$home/help'>Help</a>\n" +if {[anycap jor]} { + html "<a href='$home/timeline'>Timeline</a>\n" +} +if {[hascap oh]} { + html "<a href='$home/tree?ci=tip'>Files</a>\n" +} +if {[hascap o]} { + html "<a href='$home/brlist'>Branches</a>\n" + html "<a href='$home/taglist'>Tags</a>\n" +} +if {[hascap r]} { + html "<a href='$home/reportlist'>Tickets</a>\n" +} +if {[hascap j]} { + html "<a href='$home/wiki'>Wiki</a>\n" +} +if {[hascap s]} { + html "<a href='$home/setup'>Admin</a>\n" +} elseif {[hascap a]} { + html "<a href='$home/setup_ulist'>Users</a>\n" +} +if {[info exists login]} { + html "<a href='$home/login'>Logout</a>\n" +} else { + html "<a href='$home/login'>Login</a>\n" +} +</th1></div> ADDED skins/etienne1/README.md Index: skins/etienne1/README.md ================================================================== --- skins/etienne1/README.md +++ skins/etienne1/README.md @@ -0,0 +1,1 @@ +This skin was contributed by Étienne Deparis. ADDED skins/etienne1/css.txt Index: skins/etienne1/css.txt ================================================================== --- skins/etienne1/css.txt +++ skins/etienne1/css.txt @@ -0,0 +1,154 @@ +body { + margin: 0 auto; + width: 960px; + font-family: sans-serif; + font-size:14pt; +} + +a { + color: #4183C4; + text-decoration: none; +} +a:hover { + color: #4183C4; + text-decoration: underline; +} + +hr { + border: 0px; +} + +.title { + color: #4183C4; + float:left; + padding-top: 30px; + padding-bottom: 10px; +} +.title h1 { + display:inline; +} +.title h1:after { + content: " / "; + color: #777; + font-weight: normal; +} + +.status { + float:right; + font-size:.7em; + padding-top:50px; +} + +.mainmenu { + font-size:.8em; + clear:both; + padding:10px; + background:#eaeaea linear-gradient(#fafafa, #eaeaea) repeat-x; + border:1px solid #eaeaea; + border-radius:5px; +} + +.mainmenu a { + padding: 10px 20px; + text-decoration:none; + color: #777; + border-right:1px solid #eaeaea; +} +.mainmenu a.active, +.mainmenu a:hover { + color: #000; + border-bottom:2px solid #D26911; +} + +.submenu { + font-size: .7em; + margin-top: 10px; + padding: 10px; + border-bottom: 1px solid #ccc; +} + +.submenu a { + padding: 10px; + text-decoration:none; + color: #777; +} + +.submenu a:hover { + border: 1px solid #ccc; + border-bottom: 1px solid #fff; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +.content { + padding-top: 10px; + font-size:.9em; + color: #444; +} + +.udiff, .sbsdiff, +.content blockquote { + font-size: .85em !important; + overflow: auto; + border: 1px solid #ccc; + border-radius: 5px; +} +.content blockquote { + padding: 0 15px; +} + +table.report { + cursor: auto; + border-radius: 5px; + border: 1px solid #ccc; + margin: 1em 0; +} +.report td, .report th { + border: 0; + font-size: .8em; + padding: 10px; +} +.report td:first-child { + border-top-left-radius: 5px; +} +.report tbody tr:last-child td:first-child { + border-bottom-left-radius: 5px; +} +.report td:last-child { + border-top-right-radius: 5px; +} +.report tbody tr:last-child { + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +} +.report tbody tr:last-child td:last-child { + border-bottom-right-radius: 5px; +} +.report th { + cursor: pointer; +} +.report thead+tbody tr:hover { + background-color: #f5f9fc !important; +} + +td.tktDspLabel { + max-width: 70px; + text-align: right; +} +td.tktDspValue { + max-width: 800px; + text-align: left; + vertical-align: top; + background-color: #f5f9fc; +} +td.tktDspValue pre { + white-space: pre-wrap; +} + +.footer { + border-top: 1px solid #ccc; + padding: 10px; + font-size:.7em; + margin-top: 10px; + color: #ccc; +} ADDED skins/etienne1/footer.txt Index: skins/etienne1/footer.txt ================================================================== --- skins/etienne1/footer.txt +++ skins/etienne1/footer.txt @@ -0,0 +1,6 @@ +<div class="footer"> +This page was generated in about +<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by +Fossil version $manifest_version $manifest_date +</div> +</body></html> ADDED skins/etienne1/header.txt Index: skins/etienne1/header.txt ================================================================== --- skins/etienne1/header.txt +++ skins/etienne1/header.txt @@ -0,0 +1,88 @@ +<html> + <head> + <base href="$baseurl/$current_page" /> + <title>$<project_name>: $<title> + + + + + +
+

$

$</div> + <div class="status"><th1> + if {[info exists login]} { + html "$login — <a href='$home/login'>Logout</a>\n" + } else { + html "<a href='$home/login'>Login</a>\n" + } + </th1></div> + </div> + + <div class="mainmenu"> + <th1> +proc isin {val lst} { + set tot [llength $lst] + for {set i 0} {$i < $tot} {set i [expr {$i + 1}]} { + set cur [lindex $lst $i] + if {$val eq $cur} { + return 0 + } + } + return 1 +} + +proc menulink {pagename url name} { + upvar current_page current + upvar home home + + set compsetup [string compare [string range $current 0 4] setup] + set comphome [string compare [string range $current 0 3] home] + set comptag [string compare $current tagtimeline] + set compbr [string compare $current brtimeline] + set compdir [isin $current "artifact ci finfo hexdump"] + set comptl [string compare $current info] + set comptkt [isin $current "modreq rptedit tktnew rptsql rptview"] + + html "<a href='$home$url'" + + if {$pagename eq $current + || ($pagename eq "home" && $comphome == 0) + || ($pagename eq "setup" && $compsetup == 0) + || ($pagename eq "taglist" && $comptag == 0) + || ($pagename eq "dir" && $compdir == 0) + || ($pagename eq "timeline" && $comptl == 0) + || ($pagename eq "reportlist" && $comptkt == 0) + || ($pagename eq "brlist" && $compbr == 0) + } { + html " class='active' " + } + + html ">$name</a>" +} + +menulink "home" $index_page Home + +if {[anycap jor]} { + menulink "timeline" "/timeline" Timeline +} +if {[hascap oh]} { + menulink "dir" "/dir?ci=tip" Files +} +if {[hascap o]} { + menulink "brlist" "/brlist" Branches + menulink "taglist" "/taglist" Tags +} +if {[hascap r]} { + menulink "reportlist" "/reportlist" Tickets +} +if {[hascap j]} { + menulink "wiki" "/wiki" Wiki +} +if {[hascap s]} { + menulink "setup" "/setup" Admin +} elseif {[hascap a]} { + menulink "setup_ulist" "/setup_ulist" Users +} +</th1></div> ADDED skins/khaki/css.txt Index: skins/khaki/css.txt ================================================================== --- skins/khaki/css.txt +++ skins/khaki/css.txt @@ -0,0 +1,143 @@ +/* General settings for the entire page */ +body { + margin: 0ex 0ex; + padding: 0px; + background-color: #fef3bc; + font-family: sans-serif; +} + +/* The project logo in the upper left-hand corner of each page */ +div.logo { + display: inline; + text-align: center; + vertical-align: bottom; + font-weight: bold; + font-size: 2.5em; + color: #a09048; + white-space: nowrap; +} + +/* The page title centered at the top of each page */ +div.title { + display: table-cell; + font-size: 2em; + font-weight: bold; + text-align: left; + padding: 0 0 0 5px; + color: #a09048; + vertical-align: bottom; + width: 100%; +} + +/* The login status message in the top right-hand corner */ +div.status { + display: table-cell; + text-align: right; + vertical-align: bottom; + color: #a09048; + padding: 5px 5px 0 0; + font-size: 0.8em; + font-weight: bold; + white-space: nowrap; +} + +/* The header across the top of the page */ +div.header { + display: table; + width: 100%; +} + +/* The main menu bar that appears at the top of the page beneath +** the header */ +div.mainmenu { + padding: 5px 10px 5px 10px; + font-size: 0.9em; + font-weight: bold; + text-align: center; + letter-spacing: 1px; + background-color: #a09048; + color: black; +} + +/* The submenu bar that *sometimes* appears below the main menu */ +div.submenu, div.sectionmenu { + padding: 3px 10px 3px 0px; + font-size: 0.9em; + text-align: center; + background-color: #c0af58; + color: white; +} +div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, +div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { + padding: 3px 10px 3px 10px; + color: white; + text-decoration: none; +} +div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { + color: #a09048; + background-color: white; +} + +/* All page content from the bottom of the menu or submenu down to +** the footer */ +div.content { + padding: 1ex 5px; +} +div.content a { color: #706532; } +div.content a:link { color: #706532; } +div.content a:visited { color: #704032; } +div.content a:hover { background-color: white; color: #706532; } + +/* Some pages have section dividers */ +div.section { + margin-bottom: 0px; + margin-top: 1em; + padding: 3px 3px 0 3px; + font-size: 1.2em; + font-weight: bold; + background-color: #a09048; + color: white; + white-space: nowrap; +} + +/* The "Date" that occurs on the left hand side of timelines */ +div.divider { + background: #e1d498; + border: 2px #a09048 solid; + font-size: 1em; font-weight: normal; + padding: .25em; + margin: .2em 0 .2em 0; + float: left; + clear: left; + white-space: nowrap; +} + +/* The footer at the very bottom of the page */ +div.footer { + font-size: 0.8em; + margin-top: 12px; + padding: 5px 10px 5px 10px; + text-align: right; + background-color: #a09048; + color: white; +} + +/* Hyperlink colors */ +div.footer a { color: white; } +div.footer a:link { color: white; } +div.footer a:visited { color: white; } +div.footer a:hover { background-color: white; color: #558195; } + +/* <verbatim> blocks */ +pre.verbatim { + background-color: #f5f5f5; + padding: 0.5em; + white-space: pre-wrap; +} + +/* The label/value pairs on (for example) the ci page */ +table.label-value th { + vertical-align: top; + text-align: right; + padding: 0.2ex 2ex; +} ADDED skins/khaki/footer.txt Index: skins/khaki/footer.txt ================================================================== --- skins/khaki/footer.txt +++ skins/khaki/footer.txt @@ -0,0 +1,4 @@ +<div class="footer"> +Fossil version $manifest_version $manifest_date +</div> +</body></html> ADDED skins/khaki/header.txt Index: skins/khaki/header.txt ================================================================== --- skins/khaki/header.txt +++ skins/khaki/header.txt @@ -0,0 +1,52 @@ +<html> +<head> +<base href="$baseurl/$current_page" /> +<title>$<project_name>: $<title> + + + + +
+
$</div> + <div class="status"> + <div class="logo">$<project_name></div><br/> + <th1> + if {[info exists login]} { + puts "Logged in as $login" + } else { + puts "Not logged in" + } + </th1></div> +</div> +<div class="mainmenu"> +<th1> +html "<a href='$home$index_page'>Home</a>\n" +if {[anycap jor]} { + html "<a href='$home/timeline'>Timeline</a>\n" +} +if {[hascap oh]} { + html "<a href='$home/tree?ci=tip'>Files</a>\n" +} +if {[hascap o]} { + html "<a href='$home/brlist'>Branches</a>\n" + html "<a href='$home/taglist'>Tags</a>\n" +} +if {[hascap r]} { + html "<a href='$home/reportlist'>Tickets</a>\n" +} +if {[hascap j]} { + html "<a href='$home/wiki'>Wiki</a>\n" +} +if {[hascap s]} { + html "<a href='$home/setup'>Admin</a>\n" +} elseif {[hascap a]} { + html "<a href='$home/setup_ulist'>Users</a>\n" +} +if {[info exists login]} { + html "<a href='$home/login'>Logout</a>\n" +} else { + html "<a href='$home/login'>Login</a>\n" +} +</th1></div> ADDED skins/plain_gray/css.txt Index: skins/plain_gray/css.txt ================================================================== --- skins/plain_gray/css.txt +++ skins/plain_gray/css.txt @@ -0,0 +1,139 @@ +/* General settings for the entire page */ +body { + margin: 0ex 1ex; + padding: 0px; + background-color: white; + font-family: sans-serif; +} + +/* The project logo in the upper left-hand corner of each page */ +div.logo { + display: table-row; + text-align: center; + /* vertical-align: bottom;*/ + font-size: 2em; + font-weight: bold; + background-color: #707070; + color: #ffffff; + min-width: 200px; + white-space: nowrap; +} + +/* The page title centered at the top of each page */ +div.title { + display: table-cell; + font-size: 1.5em; + font-weight: bold; + text-align: center; + padding: 0 0 0 10px; + color: #404040; + vertical-align: bottom; + width: 100%; +} + +/* The login status message in the top right-hand corner */ +div.status { + display: table-cell; + text-align: right; + vertical-align: bottom; + color: #404040; + font-size: 0.8em; + font-weight: bold; + min-width: 200px; + white-space: nowrap; +} + +/* The header across the top of the page */ +div.header { + display: table; + width: 100%; +} + +/* The main menu bar that appears at the top of the page beneath +** the header */ +div.mainmenu { + padding: 5px 10px 5px 10px; + font-size: 0.9em; + font-weight: bold; + text-align: center; + letter-spacing: 1px; + background-color: #404040; + color: white; +} + +/* The submenu bar that *sometimes* appears below the main menu */ +div.submenu, div.sectionmenu { + padding: 3px 10px 3px 0px; + font-size: 0.9em; + text-align: center; + background-color: #606060; + color: white; +} +div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, +div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { + padding: 3px 10px 3px 10px; + color: white; + text-decoration: none; +} +div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { + color: #404040; + background-color: white; +} + +/* All page content from the bottom of the menu or submenu down to +** the footer */ +div.content { + padding: 0ex 0ex 0ex 0ex; +} +/* Hyperlink colors */ +div.content a { color: #604000; } +div.content a:link { color: #604000;} +div.content a:visited { color: #600000; } + +/* <verbatim> blocks */ +pre.verbatim { + background-color: #ffffff; + padding: 0.5em; + white-space: pre-wrap; +} + +/* Some pages have section dividers */ +div.section { + margin-bottom: 0px; + margin-top: 1em; + padding: 1px 1px 1px 1px; + font-size: 1.2em; + font-weight: bold; + background-color: #404040; + color: white; + white-space: nowrap; +} + +/* The "Date" that occurs on the left hand side of timelines */ +div.divider { + background: #a0a0a0; + border: 2px #505050 solid; + font-size: 1em; font-weight: normal; + padding: .25em; + margin: .2em 0 .2em 0; + float: left; + clear: left; + white-space: nowrap; +} + +/* The footer at the very bottom of the page */ +div.footer { + font-size: 0.8em; + margin-top: 12px; + padding: 5px 10px 5px 10px; + text-align: right; + background-color: #404040; + color: white; +} + +/* The label/value pairs on (for example) the vinfo page */ +table.label-value th { + vertical-align: top; + text-align: right; + padding: 0.2ex 2ex; +} ADDED skins/plain_gray/footer.txt Index: skins/plain_gray/footer.txt ================================================================== --- skins/plain_gray/footer.txt +++ skins/plain_gray/footer.txt @@ -0,0 +1,4 @@ +<div class="footer"> +Fossil version $manifest_version $manifest_date +</div> +</body></html> ADDED skins/plain_gray/header.txt Index: skins/plain_gray/header.txt ================================================================== --- skins/plain_gray/header.txt +++ skins/plain_gray/header.txt @@ -0,0 +1,50 @@ +<html> +<head> +<base href="$baseurl/$current_page" /> +<title>$<project_name>: $<title> + + + + +
+
$
$</div> + <div class="status"><th1> + if {[info exists login]} { + puts "Logged in as $login" + } else { + puts "Not logged in" + } + </th1></div> +</div> +<div class="mainmenu"> +<th1> +html "<a href='$home$index_page'>Home</a>\n" +if {[anycap jor]} { + html "<a href='$home/timeline'>Timeline</a>\n" +} +if {[hascap oh]} { + html "<a href='$home/tree?ci=tip'>Files</a>\n" +} +if {[hascap o]} { + html "<a href='$home/brlist'>Branches</a>\n" + html "<a href='$home/taglist'>Tags</a>\n" +} +if {[hascap r]} { + html "<a href='$home/reportlist'>Tickets</a>\n" +} +if {[hascap j]} { + html "<a href='$home/wiki'>Wiki</a>\n" +} +if {[hascap s]} { + html "<a href='$home/setup'>Admin</a>\n" +} elseif {[hascap a]} { + html "<a href='$home/setup_ulist'>Users</a>\n" +} +if {[info exists login]} { + html "<a href='$home/login'>Logout</a>\n" +} else { + html "<a href='$home/login'>Login</a>\n" +} +</th1></div> ADDED skins/rounded1/css.txt Index: skins/rounded1/css.txt ================================================================== --- skins/rounded1/css.txt +++ skins/rounded1/css.txt @@ -0,0 +1,195 @@ +/* General settings for the entire page */ +html { + min-height: 100%; +} +body { + margin: 0ex 1ex; + padding: 0px; + background-color: white; + color: #333; + font-family: Verdana, sans-serif; + font-size: 0.8em; +} + +/* The project logo in the upper left-hand corner of each page */ +div.logo { + display: table-cell; + text-align: right; + vertical-align: bottom; + font-weight: normal; + white-space: nowrap; +} + +/* Widths */ +div.header, div.mainmenu, div.submenu, div.content, div.footer { + max-width: 900px; + margin: auto; + padding: 3px 20px 3px 20px; + clear: both; +} + +/* The page title at the top of each page */ +div.title { + display: table-cell; + padding-left: 10px; + font-size: 2em; + margin: 10px 0 10px -20px; + vertical-align: bottom; + text-align: left; + width: 80%; + font-family: Verdana, sans-serif; + font-weight: bold; + color: #558195; + text-shadow: 0px 2px 2px #999999; +} + +/* The login status message in the top right-hand corner */ +div.status { + display: table-cell; + text-align: right; + vertical-align: bottom; + color: #333; + margin-right: -20px; + white-space: nowrap; +} + +/* The main menu bar that appears at the top of the page beneath + ** the header */ +div.mainmenu { + text-align: center; + color: white; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + vertical-align: middle; + padding-top: 8px; + padding-bottom: 8px; + background-color: #446979; + box-shadow: 0px 3px 4px #333333; +} + +/* The submenu bar that *sometimes* appears below the main menu */ +div.submenu { + padding-top:10px; + padding-bottom:0; + text-align: right; + color: #000; + background-color: #fff; + height: 1.5em; + vertical-align:middle; + box-shadow: 0px 3px 4px #999; +} +div.mainmenu a, div.mainmenu a:visited { + padding: 3px 10px 3px 10px; + color: white; + text-decoration: none; +} +div.submenu a, div.submenu a:visited, a.button, +div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { + padding: 2px 8px; + color: #000; + font-family: Arial; + text-decoration: none; + margin:auto; + border-radius: 5px; + background-color: #e0e0e0; + text-shadow: 0px -1px 0px #eee; + border: 1px solid #000; +} + +div.mainmenu a:hover { + color: #000; + background-color: white; +} + +div.submenu a:hover, div.sectionmenu>a.button:hover { + background-color: #c0c0c0; +} + +/* All page content from the bottom of the menu or submenu down to + ** the footer */ +div.content { + background-color: #fff; + box-shadow: 0px 3px 4px #999; + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; + padding-bottom: 1em; + min-height:40%; +} + + +/* Some pages have section dividers */ +div.section { + margin-bottom: 0.5em; + margin-top: 1em; + margin-right: auto; + padding: 1px 1px 1px 1px; + font-size: 1.2em; + font-weight: bold; + text-align: center; + color: white; + border-radius: 5px; + background-color: #446979; + box-shadow: 0px 3px 4px #333333; + white-space: nowrap; +} + +/* The "Date" that occurs on the left hand side of timelines */ +div.divider { + font-size: 1.2em; + font-family: Georgia, serif; + font-weight: bold; + margin-top: 1em; + white-space: nowrap; +} + +/* The footer at the very bottom of the page */ +div.footer { + font-size: 0.9em; + text-align: right; + margin-bottom: 1em; + color: #666; +} + +/* Hyperlink colors in the footer */ +div.footer a { color: white; } +div.footer a:link { color: white; } +div.footer a:visited { color: white; } +div.footer a:hover { background-color: white; color: #558195; } + +/* <verbatim> blocks */ +pre.verbatim, blockquote pre { + font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace; + background-color: #f3f3f3; + padding: 0.5em; + white-space: pre-wrap; +} + +blockquote pre { + border: 1px #000 dashed; +} + +/* The label/value pairs on (for example) the ci page */ +table.label-value th { + vertical-align: top; + text-align: right; + padding: 0.2ex 2ex; +} + +table.report tr th { + padding: 3px 5px; + text-transform: capitalize; + cursor: pointer; +} + +table.report tr td { + padding: 3px 5px; + cursor: pointer; +} + +textarea { + font-size: 1em; +} + +.fullsize-text { + font-size: 1.25em; +} ADDED skins/rounded1/footer.txt Index: skins/rounded1/footer.txt ================================================================== --- skins/rounded1/footer.txt +++ skins/rounded1/footer.txt @@ -0,0 +1,4 @@ +<div class="footer"> +Fossil version $manifest_version $manifest_date +</div> +</body></html> ADDED skins/rounded1/header.txt Index: skins/rounded1/header.txt ================================================================== --- skins/rounded1/header.txt +++ skins/rounded1/header.txt @@ -0,0 +1,54 @@ +<html> +<head> +<base href="$baseurl/$current_page" /> +<title>$<project_name>: $<title> + + + + +
+ +
$</div> + <div class="status"><th1> + if {[info exists login]} { + puts "Logged in as $login" + } else { + puts "Not logged in" + } + </th1></div> +</div> +<div class="mainmenu"> +<th1> +html "<a href='$home$index_page'>Home</a>\n" +if {[anycap jor]} { + html "<a href='$home/timeline'>Timeline</a>\n" +} +if {[hascap oh]} { + html "<a href='$home/tree?ci=tip'>Files</a>\n" +} +if {[hascap o]} { + html "<a href='$home/brlist'>Branches</a>\n" + html "<a href='$home/taglist'>Tags</a>\n" +} +if {[hascap r]} { + html "<a href='$home/reportlist'>Tickets</a>\n" +} +if {[hascap j]} { + html "<a href='$home/wiki'>Wiki</a>\n" +} +if {[hascap s]} { + html "<a href='$home/setup'>Admin</a>\n" +} elseif {[hascap a]} { + html "<a href='$home/setup_ulist'>Users</a>\n" +} +if {[info exists login]} { + html "<a href='$home/login'>Logout</a>\n" +} else { + html "<a href='$home/login'>Login</a>\n" +} +</th1></div> Index: src/attach.c ================================================================== --- src/attach.c +++ src/attach.c @@ -90,12 +90,12 @@ @ <li><p> @ Attachment %z(href("%R/ainfo/%s",zUuid))%S(zUuid)</a> if( moderation_pending(attachid) ){ @ <span class="modpending">*** Awaiting Moderator Approval ***</span> } - @ <br><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a> - @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> + @ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> + @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; if( zComment && zComment[0] ){ @ %!w(zComment)<br /> } if( zPage==0 && zTkt==0 ){ Index: src/branch.c ================================================================== --- src/branch.c +++ src/branch.c @@ -341,10 +341,11 @@ Stmt q; double rNow; login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } style_header("Branches"); + style_adunit_config(ADUNIT_RIGHT_OK); login_anonymous_available(); db_prepare(&q, brlistQuery/*works-like:""*/); rNow = db_double(0.0, "SELECT julianday('now')"); @ <div class="brlist"><table id="branchlisttable"> @@ -366,11 +367,11 @@ sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0; @ <tr> @ <td>%z(href("%R/timeline?n=100&r=%T",zBranch))%h(zBranch)</a></td> @ <td data-sortkey="%016llx(-iMtime)">%s(zAge)</td> - @ <td data-sortkey="%08x(-nCkin)">%d(nCkin)</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> @@ -379,11 +380,11 @@ } @ </tr> } @ </tbody></table></div> db_finalize(&q); - output_table_sorting_javascript("branchlisttable","tkktt",2); + output_table_sorting_javascript("branchlisttable","tkNtt",2); style_footer(); } /* ** WEBPAGE: brlist Index: src/browse.c ================================================================== --- src/browse.c +++ src/browse.c @@ -133,10 +133,11 @@ if( strcmp(PD("type",""),"tree")==0 ){ page_tree(); return; } login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } style_header("File List"); + style_adunit_config(ADUNIT_RIGHT_OK); sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, pathelementFunc, 0, 0); url_initialize(&sURI, "dir"); /* If the name= parameter is an empty string, make it a NULL pointer */ @@ -555,10 +556,11 @@ style_header("Folder Hierarchy"); }else{ showDirOnly = 0; style_header("File Tree"); } + style_adunit_config(ADUNIT_RIGHT_OK); if( P("expand")!=0 ){ startExpanded = 1; url_add_parameter(&sURI, "expand", "1"); }else{ startExpanded = 0; @@ -635,10 +637,12 @@ } if( linkTip ){ style_submenu_element("Tip", "Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); } + style_submenu_element("Flat-View", "Flat-View", "%s", + url_render(&sURI, "type", "flat", 0, 0)); /* Compute the file hierarchy. */ if( zCI ){ Stmt q; @@ -652,10 +656,13 @@ ); while( db_step(&q)==SQLITE_ROW ){ const char *zFile = db_column_text(&q,0); const char *zUuid = db_column_text(&q,1); double mtime = db_column_double(&q,2); + if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){ + continue; + } if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; tree_add_node(&sTree, zFile, zUuid, mtime); nFile++; } db_finalize(&q); Index: src/builtin.c ================================================================== --- src/builtin.c +++ src/builtin.c @@ -31,22 +31,30 @@ /* ** Return a pointer to built-in content */ const unsigned char *builtin_file(const char *zFilename, int *piSize){ - int lwr, upr, i; + int lwr, upr, i, c; lwr = 0; upr = sizeof(aBuiltinFiles)/sizeof(aBuiltinFiles[0]) - 1; while( upr>=lwr ){ i = (upr+lwr)/2; - if( strcmp(aBuiltinFiles[i].zName,zFilename)==0 ){ + c = strcmp(aBuiltinFiles[i].zName,zFilename); + if( c<0 ){ + lwr = i+1; + }else if( c>0 ){ + upr = i-1; + }else{ if( piSize ) *piSize = aBuiltinFiles[i].nByte; return aBuiltinFiles[i].pData; } } if( piSize ) *piSize = 0; return 0; +} +const char *builtin_text(const char *zFilename){ + return (char*)builtin_file(zFilename, 0); } /* ** COMMAND: test-builtin-list ** Index: src/cgi.c ================================================================== --- src/cgi.c +++ src/cgi.c @@ -108,10 +108,13 @@ ** if it does and false if it does not. */ int cgi_header_contains(const char *zNeedle){ return strstr(blob_str(&cgiContent[0]), zNeedle)!=0; } +int cgi_body_contains(const char *zNeedle){ + return strstr(blob_str(&cgiContent[1]), zNeedle)!=0; +} /* ** Append reply content to what already exists. */ void cgi_append_content(const char *zData, int nAmt){ Index: src/codecheck1.c ================================================================== --- src/codecheck1.c +++ src/codecheck1.c @@ -276,11 +276,11 @@ /* Certain functions are guaranteed to return a string that is safe ** for use with %s */ z = next_non_whitespace(z, &len, &eType); for(i=0; i<sizeof(azSafeFunc)/sizeof(azSafeFunc[0]); i++){ - if( eType==TK_ID + if( eType==TK_ID && strncmp(z, azSafeFunc[i], len)==0 && strlen(azSafeFunc[i])==len ){ return 1; } @@ -291,11 +291,11 @@ if( is_string_expr(z) ) return 1; /* If the "safe-for-%s" comment appears in the argument, then ** let it through */ if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1; - + return 0; } /* ** Processing flags @@ -460,11 +460,11 @@ for(i=len-1; i>0 && isspace(z[i]); i--){ z[i] = 0; } z += len + 1; } acType = (char*)&azArg[nArg]; if( fmtArg>nArg ){ - printf("%s:%d: too few arguments to %.*s()\n", + printf("%s:%d: too few arguments to %.*s()\n", zFilename, lnFCall, szFName, zFCall); nErr++; }else{ const char *zFmt = azArg[fmtArg-1]; const char *zOverride = strstr(zFmt, "/*works-like:"); @@ -537,11 +537,11 @@ nCurly--; }else if( nCurly>0 && z[0]=='(' && ePrev==TK_ID && (x = isFormatFunc(zPrev,szPrev,&fmtFlags))>0 ){ nErr += checkFormatFunc(zName, zPrev, lnPrev, x, fmtFlags); } - } + } zPrev = z; ePrev = eToken; szPrev = szToken; lnPrev = ln; } Index: src/configure.c ================================================================== --- src/configure.c +++ src/configure.c @@ -97,10 +97,11 @@ { "timeline-max-comment", CONFIGSET_SKIN }, { "timeline-plaintext", CONFIGSET_SKIN }, { "adunit", CONFIGSET_SKIN }, { "adunit-omit-if-admin", CONFIGSET_SKIN }, { "adunit-omit-if-user", CONFIGSET_SKIN }, + { "white-foreground", CONFIGSET_SKIN }, #ifdef FOSSIL_ENABLE_TH1_DOCS { "th1-docs", CONFIGSET_TH1 }, #endif #ifdef FOSSIL_ENABLE_TH1_HOOKS { "th1-hooks", CONFIGSET_TH1 }, @@ -989,5 +990,196 @@ fossil_fatal("METHOD should be one of:" " export import merge pull push reset"); } configure_rebuild(); } + + +/* +** COMMAND: test-var-list +** +** Usage: %fossil test-var-list ?PATTERN? ?--unset? ?--mtime? +** +** Show the content of the CONFIG table in a repository. If PATTERN is +** specified, then only show the entries that match that glob pattern. +** Last modification time is shown if the --mtime option is present. +** +** If the --unset option is included, then entries are deleted rather than +** being displayed. WARNING! This cannot be undone. Be sure you know what +** you are doing! The --unset option only works if there is a PATTERN. +** Probably you should run the command once without --unset to make sure +** you know exactly what is being deleted. +** +** If not in an open check-out, use the -R REPO option to specify a +** a repository. +*/ +void test_var_list_cmd(void){ + Stmt q; + int i, j; + const char *zPattern = 0; + int doUnset; + int showMtime; + Blob sql; + Blob ans; + unsigned char zTrans[1000]; + + doUnset = find_option("unset",0,0)!=0; + showMtime = find_option("mtime",0,0)!=0; + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); + verify_all_options(); + if( g.argc>=3 ){ + zPattern = g.argv[2]; + } + blob_init(&sql,0,0); + blob_appendf(&sql, "SELECT name, value, datetime(mtime,'unixepoch')" + " FROM config"); + if( zPattern ){ + blob_appendf(&sql, " WHERE name GLOB %Q", zPattern); + } + if( showMtime ){ + blob_appendf(&sql, " ORDER BY mtime, name"); + }else{ + blob_appendf(&sql, " ORDER BY name"); + } + db_prepare(&q, "%s", blob_str(&sql)/*safe-for-%s*/); + blob_reset(&sql); +#define MX_VAL 40 +#define MX_NM 28 +#define MX_LONGNM 60 + while( db_step(&q)==SQLITE_ROW ){ + const char *zName = db_column_text(&q,0); + int nName = db_column_bytes(&q,0); + const char *zValue = db_column_text(&q,1); + int szValue = db_column_bytes(&q,1); + const char *zMTime = db_column_text(&q,2); + for(i=j=0; j<MX_VAL && zValue[i]; i++){ + unsigned char c = (unsigned char)zValue[i]; + if( c>=' ' && c<='~' ){ + zTrans[j++] = c; + }else{ + zTrans[j++] = '\\'; + if( c=='\n' ){ + zTrans[j++] = 'n'; + }else if( c=='\r' ){ + zTrans[j++] = 'r'; + }else if( c=='\t' ){ + zTrans[j++] = 't'; + }else{ + zTrans[j++] = '0' + ((c>>6)&7); + zTrans[j++] = '0' + ((c>>3)&7); + zTrans[j++] = '0' + (c&7); + } + } + } + zTrans[j] = 0; + if( i<szValue ){ + sqlite3_snprintf(sizeof(zTrans)-j, (char*)zTrans+j, "...+%d", szValue-i); + j += (int)strlen((char*)zTrans+j); + } + if( showMtime ){ + fossil_print("%s:%*s%s\n", zName, 58-nName, "", zMTime); + }else if( nName<MX_NM-2 ){ + fossil_print("%s:%*s%s\n", zName, MX_NM-1-nName, "", zTrans); + }else if( nName<MX_LONGNM-2 && j<10 ){ + fossil_print("%s:%*s%s\n", zName, MX_LONGNM-1-nName, "", zTrans); + }else{ + fossil_print("%s:\n%*s%s\n", zName, MX_NM, "", zTrans); + } + } + db_finalize(&q); + if( zPattern && doUnset ){ + prompt_user("Delete all of the above? (y/N)? ", &ans); + if( blob_str(&ans)[0]=='y' || blob_str(&ans)[0]=='Y' ){ + db_multi_exec("DELETE FROM config WHERE name GLOB %Q", zPattern); + } + blob_reset(&ans); + } +} + +/* +** COMMAND: test-var-get +** +** Usage: %fossil test-var-get VAR ?FILE? +** +** Write the text of the VAR variable into FILE. If FILE is "-" +** or is omitted then output goes to standard output. VAR can be a +** GLOB pattern. +** +** If not in an open check-out, use the -R REPO option to specify a +** a repository. +*/ +void test_var_get_cmd(void){ + const char *zVar; + const char *zFile; + int n; + Blob x; + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); + verify_all_options(); + if( g.argc<3 ){ + usage("VAR ?FILE?"); + } + zVar = g.argv[2]; + zFile = g.argc>=4 ? g.argv[3] : "-"; + n = db_int(0, "SELECT count(*) FROM config WHERE name GLOB %Q", zVar); + if( n==0 ){ + fossil_fatal("no match for %Q", zVar); + } + if( n>1 ){ + fossil_fatal("multiple matches: %s", + db_text(0, "SELECT group_concat(quote(name),', ') FROM (" + " SELECT name FROM config WHERE name GLOB %Q ORDER BY 1)", + zVar)); + } + blob_init(&x,0,0); + db_blob(&x, "SELECT value FROM config WHERE name GLOB %Q", zVar); + blob_write_to_file(&x, zFile); +} + +/* +** COMMAND: test-var-set +** +** Usage: %fossil test-var-set VAR ?VALUE? ?--file FILE? +** +** Store VALUE or the content of FILE (exactly one of which must be +** supplied) into variable VAR. Use a FILE of "-" to read from +** standard input. +** +** WARNING: changing the value of a variable can interfere with the +** operation of Fossil. Be sure you know what you are doing. +** +** Use "--blob FILE" instead of "--file FILE" to load a binary blob +** such as a GIF. +*/ +void test_var_set_cmd(void){ + const char *zVar; + const char *zFile; + const char *zBlob; + Blob x; + Stmt ins; + zFile = find_option("file",0,1); + zBlob = find_option("blob",0,1); + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); + verify_all_options(); + if( g.argc<3 || (zFile==0 && zBlob==0 && g.argc<4) ){ + usage("VAR ?VALUE? ?--file FILE?"); + } + zVar = g.argv[2]; + if( zFile ){ + if( zBlob ) fossil_fatal("cannot do both --file or --blob"); + blob_read_from_file(&x, zFile); + }else if( zBlob ){ + blob_read_from_file(&x, zBlob); + }else{ + blob_init(&x,g.argv[3],-1); + } + db_prepare(&ins, + "REPLACE INTO config(name,value,mtime)" + "VALUES(%Q,:val,now())", zVar); + if( zBlob ){ + db_bind_blob(&ins, ":val", &x); + }else{ + db_bind_text(&ins, ":val", blob_str(&x)); + } + db_step(&ins); + db_finalize(&ins); + blob_reset(&x); +} Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -805,10 +805,12 @@ db_checkin_mtime_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); + sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, + db_now_function, 0, 0); } /* ** Open a database file. Return a pointer to the new database @@ -827,12 +829,10 @@ if( rc!=SQLITE_OK ){ db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); } sqlite3_busy_timeout(db, 5000); sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ - sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, - db_now_function, 0, 0); sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); sqlite3_create_function( @@ -927,30 +927,35 @@ char *zHome; if( g.zConfigDbName ){ if( useAttach==g.useAttach ) return; db_close_config(); } + zHome = fossil_getenv("FOSSIL_HOME"); #if defined(_WIN32) || defined(__CYGWIN__) - zHome = fossil_getenv("LOCALAPPDATA"); if( zHome==0 ){ - zHome = fossil_getenv("APPDATA"); + zHome = fossil_getenv("LOCALAPPDATA"); if( zHome==0 ){ - char *zDrive = fossil_getenv("HOMEDRIVE"); - zHome = fossil_getenv("HOMEPATH"); - if( zDrive && zHome ) zHome = mprintf("%s%s", zDrive, zHome); + zHome = fossil_getenv("APPDATA"); + if( zHome==0 ){ + char *zDrive = fossil_getenv("HOMEDRIVE"); + char *zPath = fossil_getenv("HOMEPATH"); + if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); + } } } if( zHome==0 ){ - fossil_fatal("cannot locate home directory - " - "please set the LOCALAPPDATA or APPDATA or HOMEPATH " - "environment variables"); + fossil_fatal("cannot locate home directory - please set the " + "FOSSIL_HOME, LOCALAPPDATA, APPDATA, or HOMEPATH " + "environment variables"); } #else - zHome = fossil_getenv("HOME"); + if( zHome==0 ){ + zHome = fossil_getenv("HOME"); + } if( zHome==0 ){ - fossil_fatal("cannot locate home directory - " - "please set the HOME environment variable"); + fossil_fatal("cannot locate home directory - please set the " + "FOSSIL_HOME or HOME environment variables"); } #endif if( file_isdir(zHome)!=1 ){ fossil_fatal("invalid home directory: %s", zHome); } @@ -1355,10 +1360,22 @@ while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){ fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt)); } } db_close_config(); + + /* If the localdb (the check-out database) is open and if it has + ** a lot of unused free space, then VACUUM it as we shut down. + */ + if( g.localOpen && strcmp(db_name("localdb"),"main")==0 ){ + int nFree = db_int(0, "PRAGMA main.freelist_count"); + int nTotal = db_int(0, "PRAGMA main.page_count"); + if( nFree>nTotal/4 ){ + db_multi_exec("VACUUM;"); + } + } + if( g.db ){ sqlite3_wal_checkpoint(g.db, 0); sqlite3_close(g.db); g.db = 0; g.zMainDbType = 0; @@ -1422,13 +1439,13 @@ " WHERE login=%Q", zUser ); if( !setupUserOnly ){ db_multi_exec( "INSERT OR IGNORE INTO user(login,pw,cap,info)" - " VALUES('anonymous',hex(randomblob(8)),'hmncz','Anon');" + " VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" - " VALUES('nobody','','gjor','Nobody');" + " VALUES('nobody','','gjorz','Nobody');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('developer','','dei','Dev');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('reader','','kptw','Reader');" ); Index: src/diff.tcl ================================================================== --- src/diff.tcl +++ src/diff.tcl @@ -1,5 +1,14 @@ +# The "diff --tk" command outputs prepends a "set fossilcmd {...}" line +# to this file, then runs this file using "tclsh" in order to display the +# graphical diff in a separate window. A typical "set fossilcmd" line +# looks like this: +# +# set fossilcmd {| "./fossil" diff --html -y -i -v} +# +# This header comment is stripped off by the "mkbuiltin.c" program. +# set prog { package require Tk array set CFG { TITLE {Fossil Diff} Index: src/doc.c ================================================================== --- src/doc.c +++ src/doc.c @@ -353,147 +353,125 @@ } } /* ** WEBPAGE: doc -** URL: /doc?name=BASELINE/PATH -** URL: /doc/BASELINE/PATH +** URL: /doc?name=CHECKIN/FILE +** URL: /doc/CHECKIN/FILE +** +** CHECKIN can be either tag or SHA1 hash or timestamp identifying a +** particular check, or the name of a branch (meaning the most recent +** check-in on that branch) or one of various magic words: +** +** "tip" means the most recent check-in +** +** "ckout" means the current check-out, if the server is run from +** within a check-out, otherwise it is the same as "tip" +** +** FILE is the name of a file to delivered up as a webpage. FILE is relative +** to the root of the source tree of the repository. The FILE must +** be a part of CHECKIN, except when CHECKIN=="ckout" when FILE is read +** directly from disk and need not be a managed file. ** -** BASELINE can be either a baseline uuid prefix or magic words "tip" -** to mean the most recently checked in baseline or "ckout" to mean the -** content of the local checkout, if any. PATH is the relative pathname -** of some file. This method returns the file content. +** The "ckout" CHECKIN is intended for development - to provide a mechanism +** for looking at what a file will look like using the /doc webpage after +** it gets checked in. ** -** If PATH matches the patterns *.wiki or *.txt then formatting content -** is added before returning the file. For all other names, the content -** is returned straight without any interpretation or processing. +** The file extension is used to decide how to render the file. */ void doc_page(void){ const char *zName; /* Argument to the /doc page */ + const char *zOrigName; /* Original document name */ const char *zMime; /* Document MIME type */ - int vid = 0; /* Artifact of baseline */ + char *zCheckin; /* The checkin holding the document */ + int vid = 0; /* Artifact of checkin */ int rid = 0; /* Artifact of file */ int i; /* Loop counter */ Blob filebody; /* Content of the documentation file */ - char zBaseline[UUID_SIZE+1]; /* Baseline UUID */ + int nMiss = 0; /* Failed attempts to find the document */ login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } zName = PD("name", "tip/index.wiki"); for(i=0; zName[i] && zName[i]!='/'; i++){} - if( zName[i]==0 || i>UUID_SIZE ){ - zName = "index.html"; - goto doc_not_found; + zCheckin = mprintf("%.*s", i, zName); + if( zName[i]==0 ){ + zName = "index.wiki"; + }else{ + zName += i; } - g.zPath = mprintf("%s/%s", g.zPath, zName); - memcpy(zBaseline, zName, i); - zBaseline[i] = 0; - zName += i; while( zName[0]=='/' ){ zName++; } + g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName); + zOrigName = zName; if( !file_is_simple_pathname(zName, 1) ){ - int n = strlen(zName); - if( n>0 && zName[n-1]=='/' ){ - zName = mprintf("%sindex.html", zName); + if( sqlite3_strglob("*/", zName)==0 ){ + zOrigName = zName = mprintf("%sindex.wiki", zName); if( !file_is_simple_pathname(zName, 1) ){ goto doc_not_found; } }else{ goto doc_not_found; } } - if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local(0)==0 ){ - sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); + if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ + sqlite3_snprintf(sizeof(zCheckin), zCheckin, "tip"); } - if( fossil_strcmp(zBaseline,"ckout")==0 ){ + if( fossil_strcmp(zCheckin,"ckout")==0 ){ /* Read from the local checkout */ char *zFullpath; db_must_be_within_tree(); - zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); - if( !file_isfile(zFullpath) ){ - goto doc_not_found; - } - if( blob_read_from_file(&filebody, zFullpath)<0 ){ - goto doc_not_found; + while( rid==0 && nMiss<2 ){ + zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); + if( file_isfile(zFullpath) + && blob_read_from_file(&filebody, zFullpath)<0 ){ + rid = 1; /* Fake RID just to get the loop to end */ + } + fossil_free(zFullpath); + if( rid ) break; + nMiss++; + zName = "404.md"; } }else{ db_begin_transaction(); - if( fossil_strcmp(zBaseline,"tip")==0 ){ - vid = db_int(0, "SELECT objid FROM event WHERE type='ci'" - " ORDER BY mtime DESC LIMIT 1"); - }else{ - vid = name_to_typed_rid(zBaseline, "ci"); - } - - /* Create the baseline cache if it does not already exist */ + vid = name_to_typed_rid(zCheckin, "ci"); db_multi_exec( "CREATE TABLE IF NOT EXISTS vcache(\n" - " vid INTEGER, -- baseline ID\n" + " vid INTEGER, -- checkin ID\n" " fname TEXT, -- filename\n" " rid INTEGER, -- artifact ID\n" - " UNIQUE(vid,fname,rid)\n" - ")" + " PRIMARY KEY(vid,fname)\n" + ") WITHOUT ROWID" ); - - - - /* Check to see if the documentation file artifact ID is contained - ** in the baseline cache */ - rid = db_int(0, "SELECT rid FROM vcache" - " WHERE vid=%d AND fname=%Q", vid, zName); - if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ - goto doc_not_found; - } - - if( rid==0 ){ - Stmt s; - Manifest *pM; - ManifestFile *pFile; - - /* Add the vid baseline to the cache */ - if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){ - db_multi_exec("DELETE FROM vcache"); - } - pM = manifest_get(vid, CFTYPE_MANIFEST, 0); - if( pM==0 ){ - goto doc_not_found; - } - db_prepare(&s, + if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ + db_multi_exec( + "DELETE FROM vcache;\n" + "CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;\n" "INSERT INTO vcache(vid,fname,rid)" - " SELECT %d, :fname, rid FROM blob" - " WHERE uuid=:uuid", + " SELECT checkinID, filename, blob.rid FROM foci, blob" + " WHERE blob.uuid=foci.uuid" + " AND foci.checkinID=%d;", vid ); - manifest_file_rewind(pM); - while( (pFile = manifest_file_next(pM,0))!=0 ){ - db_bind_text(&s, ":fname", pFile->zName); - db_bind_text(&s, ":uuid", pFile->zUuid); - db_step(&s); - db_reset(&s); - } - db_finalize(&s); - manifest_destroy(pM); - - /* Try again to find the file */ + } + while( rid==0 && nMiss<2 ){ rid = db_int(0, "SELECT rid FROM vcache" " WHERE vid=%d AND fname=%Q", vid, zName); - } - if( rid==0 ){ - goto doc_not_found; + if( rid ) break; + nMiss++; + zName = "404.md"; } - - /* Get the file content */ - if( content_get(rid, &filebody)==0 ){ + if( rid==0 || content_get(rid, &filebody)==0 ){ goto doc_not_found; } db_end_transaction(0); } blob_to_utf8_no_bom(&filebody, 0); /* The file is now contained in the filebody blob. Deliver the ** file to the user */ - zMime = P("mimetype"); + zMime = nMiss==0 ? P("mimetype") : 0; if( zMime==0 ){ zMime = mimetype_from_name(zName); } Th_Store("doc_name", zName); Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" @@ -500,10 +478,11 @@ " FROM blob WHERE rid=%d", vid)); Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" " WHERE objid=%d AND type='ci'", vid)); if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ Blob title, tail; + style_adunit_config(ADUNIT_RIGHT_OK); if( wiki_find_title(&filebody, &title, &tail) ){ style_header("%s", blob_str(&title)); wiki_convert(&tail, 0, WIKI_BUTTONS); }else{ style_header("Documentation"); @@ -515,11 +494,11 @@ Blob tail = BLOB_INITIALIZER; markdown_to_html(&filebody, &title, &tail); if( blob_size(&title)>0 ){ style_header("%s", blob_str(&title)); }else{ - style_header("Documentation"); + style_header("%s", nMiss?"Not Found":"Documentation"); } blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail)); style_footer(); }else if( fossil_strcmp(zMime, "text/plain")==0 ){ style_header("Documentation"); @@ -536,17 +515,22 @@ #endif }else{ cgi_set_content_type(zMime); cgi_set_content(&filebody); } + if( nMiss ) cgi_set_status(404, "Not Found"); return; -doc_not_found: /* Jump here when unable to locate the document */ +doc_not_found: db_end_transaction(0); - style_header("Document Not Found"); - @ <p>No such document: %h(zName)</p> + cgi_set_status(404, "Not Found"); + style_header("Not Found"); + @ <p>Document %h(zOrigName) not found + if( fossil_strcmp(zCheckin,"ckout")!=0 ){ + @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> + } style_footer(); return; } /* Index: src/file.c ================================================================== --- src/file.c +++ src/file.c @@ -1262,10 +1262,29 @@ char *zValue = getenv(zName); #endif if( zValue ) zValue = fossil_filename_to_utf8(zValue); return zValue; } + +/* +** Sets the value of an environment variable as UTF8. +*/ +int fossil_setenv(const char *zName, const char *zValue){ + int rc; + char *zString = mprintf("%s=%s", zName, zValue); +#ifdef _WIN32 + wchar_t *uString = fossil_utf8_to_unicode(zString); + rc = _wputenv(uString); + fossil_unicode_free(uString); + fossil_free(zString); +#else + rc = putenv(zString); + /* NOTE: Cannot free the string on POSIX. */ + /* fossil_free(zString); */ +#endif + return rc; +} /* ** Like fopen() but always takes a UTF8 argument. */ FILE *fossil_fopen(const char *zName, const char *zMode){ Index: src/http_socket.c ================================================================== --- src/http_socket.c +++ src/http_socket.c @@ -27,10 +27,13 @@ */ #include "config.h" #include "http_socket.h" #if defined(_WIN32) +# if !defined(_WIN32_WINNT) +# define _WIN32_WINNT 0x0501 +# endif # include <winsock2.h> # include <ws2tcpip.h> #else # include <netinet/in.h> # include <arpa/inet.h> @@ -45,11 +48,10 @@ ** There can only be a single socket connection open at a time. ** State information about that socket is stored in the following ** local variables: */ static int socketIsInit = 0; /* True after global initialization */ -static int addrIsInit = 0; /* True once addr is initialized */ #if defined(_WIN32) static WSADATA socketInfo; /* Windows socket initialize data */ #endif static int iSocket = -1; /* The socket on which we talk to the server */ static char *socketErrMsg = 0; /* Text of most recent socket error */ @@ -106,11 +108,10 @@ WSACleanup(); #endif socket_clear_errmsg(); socketIsInit = 0; } - addrIsInit = 0; } /* ** Close the currently open socket. If no socket is open, this routine ** is a no-op. @@ -134,54 +135,56 @@ ** pUrlDAta->port TCP/IP port to use. Ex: 80 ** ** Return the number of errors. */ int socket_open(UrlData *pUrlData){ - static struct sockaddr_in addr; /* The server address */ + int rc = 0; + struct addrinfo *ai = 0; + struct addrinfo *p; + struct addrinfo hints; + char zPort[30]; + char zRemote[NI_MAXHOST]; socket_global_init(); - if( !addrIsInit ){ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(pUrlData->port); - *(int*)&addr.sin_addr = inet_addr(pUrlData->name); - if( -1 == *(int*)&addr.sin_addr ){ -#ifndef FOSSIL_STATIC_LINK - struct hostent *pHost; - pHost = gethostbyname(pUrlData->name); - if( pHost!=0 ){ - memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); - }else -#endif - { - socket_set_errmsg("can't resolve host name: %s", pUrlData->name); - return 1; - } + memset(&hints, 0, sizeof(struct addrinfo)); + assert( iSocket<0 ); + hints.ai_family = g.fIPv4 ? AF_INET : AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + sqlite3_snprintf(sizeof(zPort),zPort,"%d", pUrlData->port); + rc = getaddrinfo(pUrlData->name, zPort, &hints, &ai); + if( rc ){ + socket_set_errmsg("getaddrinfo() fails: %s", gai_strerror(rc)); + goto end_socket_open; + } + for(p=ai; p; p=p->ai_next){ + iSocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if( iSocket<0 ) continue; + if( connect(iSocket,p->ai_addr,p->ai_addrlen)<0 ){ + socket_close(); + continue; + } + rc = getnameinfo(p->ai_addr, p->ai_addrlen, zRemote, sizeof(zRemote), + 0, 0, NI_NUMERICHOST); + if( rc ){ + socket_set_errmsg("getnameinfo() failed: %s", gai_strerror(rc)); + goto end_socket_open; } - addrIsInit = 1; - - /* 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. - */ - g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); + g.zIpAddr = mprintf("%s", zRemote); + break; } - iSocket = socket(AF_INET,SOCK_STREAM,0); - if( iSocket<0 ){ - socket_set_errmsg("cannot create a socket"); - return 1; - } - if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ + if( p==0 ){ socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, pUrlData->port); - socket_close(); - return 1; } #if !defined(_WIN32) signal(SIGPIPE, SIG_IGN); #endif - return 0; +end_socket_open: + if( rc && iSocket>=0 ) socket_close(); + if( ai ) freeaddrinfo(ai); + return rc; } /* ** Send content out over the open socket connection. */ Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -198,13 +198,10 @@ int verboseFlag = find_option("verbose","v",0)!=0; if( !verboseFlag ){ verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ } - /* We should be done with options.. */ - verify_all_options(); - if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){ db_open_config(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>")); @@ -211,10 +208,11 @@ fossil_print("project-code: %s\n", db_get("project-code", "<none>")); extraRepoInfo(); return; } db_find_and_open_repository(0,0); + verify_all_options(); if( g.argc==2 ){ int vid; /* 012345678901234 */ db_record_repository_filename(0); fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); Index: src/login.c ================================================================== --- src/login.c +++ src/login.c @@ -575,10 +575,11 @@ login_set_user_cookie(zUsername, uid, NULL); redirect_to_g(); } } style_header("Login/Logout"); + style_adunit_config(ADUNIT_OFF); @ %s(zErrMsg) if( zGoto && P("anon")==0 ){ @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p> } form_begin(0, "%R/login"); Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -149,10 +149,11 @@ int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ int fSshTrace; /* Trace the SSH setup traffic */ int fSshClient; /* HTTP client flags for SSH client */ char *zSshCmd; /* SSH command string */ int fNoSync; /* Do not do an autosync ever. --nosync */ + int fIPv4; /* Use only IPv4, not IPv6. --ipv4 */ char *zPath; /* Name of webpage being served */ char *zExtra; /* Extra path information past the webpage name */ char *zBaseURL; /* Full text of the URL being served */ char *zHttpsURL; /* zBaseURL translated to https: */ char *zTop; /* Parent directory of zPath */ @@ -1787,57 +1788,119 @@ g.cgiOutput = 1; blob_read_from_file(&config, zFile); while( blob_line(&config, &line) ){ if( !blob_token(&line, &key) ) continue; if( blob_buffer(&key)[0]=='#' ) continue; - if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){ - g.fDebug = fossil_fopen(blob_str(&value), "ab"); - blob_reset(&value); - continue; - } - if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ - g.zErrlog = mprintf("%s", blob_str(&value)); - continue; - } - if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){ - cgi_setenv("HOME", blob_str(&value)); - blob_reset(&value); - continue; - } if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){ + /* repository: FILENAME + ** + ** The name of the Fossil repository to be served via CGI. Most + ** fossil CGI scripts have a single non-comment line that contains + ** this one entry. + */ blob_trim(&value); db_open_repository(blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){ + /* directory: DIRECTORY + ** + ** If repository: is omitted, then terms of the PATH_INFO cgi parameter + ** are appended to DIRECTORY looking for a repository (whose name ends + ** in ".fossil") or a file in "files:". + */ db_close(1); g.zRepositoryName = mprintf("%s", blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){ + /* notfound: URL + ** + ** If using directory: and no suitable repository or file is found, + ** then redirect to URL. + */ zNotFound = mprintf("%s", blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "localauth") ){ + /* localauth + ** + ** Grant "administrator" privileges to users connecting with HTTP + ** from IP address 127.0.0.1. Do not bother checking credentials. + */ g.useLocalauth = 1; continue; } if( blob_eq(&key, "redirect:") && blob_token(&line, &value) && blob_token(&line, &value2) ){ + /* See the header comment on the redirect_web_page() function + ** above for details. */ nRedirect++; azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*)); azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value)); azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2)); blob_reset(&value); blob_reset(&value2); continue; } if( blob_eq(&key, "files:") && blob_token(&line, &value) ){ + /* files: GLOBLIST + ** + ** GLOBLIST is a comma-separated list of filename globs. For + ** example: *.html,*.css,*.js + ** + ** If the repository: line is omitted and then PATH_INFO is searched + ** for files that match any of these GLOBs and if any such file is + ** found it is returned verbatim. This feature allows "fossil server" + ** to function as a primitive web-server delivering arbitrary content. + */ pFileGlob = glob_create(blob_str(&value)); + blob_reset(&value); + continue; + } + if( blob_eq(&key, "setenv:") && blob_token(&line, &value) + && blob_token(&line, &value2) ){ + /* setenv: NAME VALUE + ** + ** Sets environment variable NAME to VALUE + */ + fossil_setenv(blob_str(&value), blob_str(&value2)); + blob_reset(&value); + blob_reset(&value2); + continue; + } + if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){ + /* debug: FILENAME + ** + ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go + ** into FILENAME. + */ + g.fDebug = fossil_fopen(blob_str(&value), "ab"); + blob_reset(&value); + continue; + } + if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ + /* errorlog: FILENAME + ** + ** Causes messages from warnings, errors, and panics to be appended + ** to FILENAME. + */ + g.zErrlog = mprintf("%s", blob_str(&value)); + blob_reset(&value); + continue; + } + if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){ + /* HOME: VALUE + ** + ** Set CGI parameter "HOME" to VALUE. This is legacy. Use + ** setenv: instead. + */ + cgi_setenv("HOME", blob_str(&value)); + blob_reset(&value); continue; } } blob_reset(&config); if( g.db==0 && g.zRepositoryName==0 && nRedirect==0 ){ Index: src/main.mk ================================================================== --- src/main.mk +++ src/main.mk @@ -131,10 +131,34 @@ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ + $(SRCDIR)/../skins/black_and_white/css.txt \ + $(SRCDIR)/../skins/black_and_white/footer.txt \ + $(SRCDIR)/../skins/black_and_white/header.txt \ + $(SRCDIR)/../skins/default/css.txt \ + $(SRCDIR)/../skins/default/footer.txt \ + $(SRCDIR)/../skins/default/header.txt \ + $(SRCDIR)/../skins/eagle/css.txt \ + $(SRCDIR)/../skins/eagle/footer.txt \ + $(SRCDIR)/../skins/eagle/header.txt \ + $(SRCDIR)/../skins/enhanced1/css.txt \ + $(SRCDIR)/../skins/enhanced1/footer.txt \ + $(SRCDIR)/../skins/enhanced1/header.txt \ + $(SRCDIR)/../skins/etienne1/css.txt \ + $(SRCDIR)/../skins/etienne1/footer.txt \ + $(SRCDIR)/../skins/etienne1/header.txt \ + $(SRCDIR)/../skins/khaki/css.txt \ + $(SRCDIR)/../skins/khaki/footer.txt \ + $(SRCDIR)/../skins/khaki/header.txt \ + $(SRCDIR)/../skins/plain_gray/css.txt \ + $(SRCDIR)/../skins/plain_gray/footer.txt \ + $(SRCDIR)/../skins/plain_gray/header.txt \ + $(SRCDIR)/../skins/rounded1/css.txt \ + $(SRCDIR)/../skins/rounded1/footer.txt \ + $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/diff.tcl TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ Index: src/makeheaders.c ================================================================== --- src/makeheaders.c +++ src/makeheaders.c @@ -112,19 +112,19 @@ ** ** struct Xyzzy; ** ** Not every object has a forward declaration. If it does, thought, the ** forward declaration will be contained in the zFwd field for C and -** the zFwdCpp for C++. The zDecl field contains the complete -** declaration text. +** the zFwdCpp for C++. The zDecl field contains the complete +** declaration text. */ typedef struct Decl Decl; struct Decl { char *zName; /* Name of the object being declared. The appearance ** of this name is a source file triggers the declaration ** to be added to the header for that file. */ - char *zFile; /* File from which extracted. */ + const char *zFile; /* File from which extracted. */ char *zIf; /* Surround the declaration with this #if */ char *zFwd; /* A forward declaration. NULL if there is none. */ char *zFwdCpp; /* Use this forward declaration for C++. */ char *zDecl; /* A full declaration of this object */ char *zExtra; /* Extra declaration text inserted into class objects */ @@ -163,11 +163,11 @@ ** in the output when using the -H option.) ** ** EXPORT scope The object is visible and usable everywhere. ** ** The DP_Flag is a temporary use flag that is used during processing to -** prevent an infinite loop. It's use is localized. +** prevent an infinite loop. It's use is localized. ** ** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent ** and are used to specify what type of declaration the object requires. */ #define DP_Forward 0x001 /* Has a forward declaration in this file */ @@ -201,11 +201,11 @@ ** Be careful not to confuse PS_Export with DP_Export or ** PS_Local with DP_Local. Their names are similar, but the meanings ** of these flags are very different. */ #define PS_Extern 0x000800 /* "extern" has been seen */ -#define PS_Export 0x001000 /* If between "#if EXPORT_INTERFACE" +#define PS_Export 0x001000 /* If between "#if EXPORT_INTERFACE" ** and "#endif" */ #define PS_Export2 0x002000 /* If "EXPORT" seen */ #define PS_Typedef 0x004000 /* If "typedef" has been seen */ #define PS_Static 0x008000 /* If "static" has been seen */ #define PS_Interface 0x010000 /* If within #if INTERFACE..#endif */ @@ -231,11 +231,11 @@ #define TY_Union 0x04000000 #define TY_Enumeration 0x08000000 #define TY_Defunct 0x10000000 /* Used to erase a declaration */ /* -** Each nested #if (or #ifdef or #ifndef) is stored in a stack of +** Each nested #if (or #ifdef or #ifndef) is stored in a stack of ** instances of the following structure. */ typedef struct Ifmacro Ifmacro; struct Ifmacro { int nLine; /* Line number where this macro occurs */ @@ -293,11 +293,11 @@ int flags; /* One or more DP_, PS_ and/or TY_ flags */ InFile *pNext; /* Next input file in the list of them all */ IdentTable idTable; /* All identifiers in this input file */ }; -/* +/* ** An unbounded string is able to grow without limit. We use these ** to construct large in-memory strings from lots of smaller components. */ typedef struct String String; struct String { @@ -332,19 +332,19 @@ ** never to read a file that it generated itself. ** ** The "#undef INTERFACE" part is a hack to work around a name collision ** in MSVC 2008. */ -const char zTopLine[] = +const char zTopLine[] = "/* \aThis file was automatically generated. Do not edit! */\n" "#undef INTERFACE\n"; #define nTopLine (sizeof(zTopLine)-1) /* ** The name of the file currently being parsed. */ -static char *zFilename; +static const char *zFilename; /* ** The stack of #if macros for the file currently being parsed. */ static Ifmacro *ifStack = 0; @@ -702,11 +702,11 @@ struct stat sStat; FILE *pIn; char *zBuf; int n; - if( stat(zFilename,&sStat)!=0 + if( stat(zFilename,&sStat)!=0 #ifndef WIN32 || !S_ISREG(sStat.st_mode) #endif ){ return 0; @@ -889,12 +889,12 @@ } } } i++; } - if( z[i] ){ - i += 2; + if( z[i] ){ + i += 2; }else{ isBlockComment = 0; fprintf(stderr,"%s:%d: Unterminated comment\n", zFilename, startLine); nErr++; @@ -906,11 +906,11 @@ pToken->eType = TT_Other; pToken->nText = 1 + (z[i+1]=='+'); } break; - case '0': + case '0': if( z[i+1]=='x' || z[i+1]=='X' ){ /* A hex constant */ i += 2; while( isxdigit(z[i]) ){ i++; } }else{ @@ -963,11 +963,11 @@ while( isalnum(z[i]) || z[i]=='_' ){ i++; }; pToken->eType = TT_Id; pToken->nText = i - pIn->i; break; - case ':': + case ':': pToken->eType = TT_Other; pToken->nText = 1 + (z[i+1]==':'); break; case '=': @@ -977,11 +977,11 @@ case '-': case '*': case '%': case '^': case '&': - case '|': + case '|': pToken->eType = TT_Other; pToken->nText = 1 + (z[i+1]=='='); break; default: @@ -1064,11 +1064,11 @@ } } /* NOT REACHED */ } -/* +/* ** This routine looks for identifiers (strings of contiguous alphanumeric ** characters) within a preprocessor directive and adds every such string ** found to the given identifier table */ static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){ @@ -1157,11 +1157,11 @@ case TT_Id: if( pTable ){ IdentTableInsert(pTable,pToken->zText,pToken->nText); } break; - + case TT_Preprocessor: if( pTable!=0 ){ FindIdentifiersInMacro(pToken,pTable); } break; @@ -1263,11 +1263,11 @@ exit(1); } pList = TokenizeFile(zFile,&sTable); for(p=pList; p; p=p->pNext){ int j; - switch( p->eType ){ + switch( p->eType ){ case TT_Space: printf("%4d: Space\n",p->nLine); break; case TT_Id: printf("%4d: Id %.*s\n",p->nLine,p->nText,p->zText); @@ -1330,11 +1330,11 @@ needSpace = 1; break; default: c = pFirst->zText[0]; - printf("%s%.*s", + printf("%s%.*s", (needSpace && (c=='*' || c=='{')) ? " " : "", pFirst->nText, pFirst->zText); needSpace = pFirst->zText[0]==','; break; } @@ -1371,13 +1371,13 @@ StringInit(&str); pLast = pLast->pNext; while( pFirst!=pLast ){ if( pFirst==pSkip ){ iSkip = nSkip; } - if( iSkip>0 ){ + if( iSkip>0 ){ iSkip--; - pFirst=pFirst->pNext; + pFirst=pFirst->pNext; continue; } switch( pFirst->eType ){ case TT_Preprocessor: StringAppend(&str,"\n",1); @@ -1384,13 +1384,13 @@ StringAppend(&str,pFirst->zText,pFirst->nText); StringAppend(&str,"\n",1); needSpace = 0; break; - case TT_Id: + case TT_Id: switch( pFirst->zText[0] ){ - case 'E': + case 'E': if( pFirst->nText==6 && strncmp(pFirst->zText,"EXPORT",6)==0 ){ skipOne = 1; } break; case 'P': @@ -1645,17 +1645,17 @@ pLast = pLast->pNext; for(p=pFirst; p && p!=pLast; p=p->pNext){ if( p->eType==TT_Id ){ static IdentTable sReserved; static int isInit = 0; - static char *aWords[] = { "char", "class", - "const", "double", "enum", "extern", "EXPORT", "ET_PROC", + static const char *aWords[] = { "char", "class", + "const", "double", "enum", "extern", "EXPORT", "ET_PROC", "float", "int", "long", "PRIVATE", "PROTECTED", "PUBLIC", - "register", "static", "struct", "sizeof", "signed", "typedef", + "register", "static", "struct", "sizeof", "signed", "typedef", "union", "volatile", "virtual", "void", }; - + if( !isInit ){ int i; for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){ IdentTableInsert(&sReserved,aWords[i],0); } @@ -1768,11 +1768,11 @@ pCode = pLast; while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){ pLast = pLast->pPrev; } if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){ - fprintf(stderr,"%s:%d: Unrecognized syntax.\n", + fprintf(stderr,"%s:%d: Unrecognized syntax.\n", zFilename, pFirst->nLine); return 1; } if( flags & (PS_Interface|PS_Export|PS_Local) ){ fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n", @@ -1849,11 +1849,11 @@ return 1; } #ifdef DEBUG if( debugMask & PARSER ){ - printf("**** Found inline routine: %.*s on line %d...\n", + printf("**** Found inline routine: %.*s on line %d...\n", pName->nText, pName->zText, pFirst->nLine); PrintTokens(pFirst,pEnd); printf("\n"); } #endif @@ -1888,11 +1888,11 @@ ** to search for an occurrence of an ID followed immediately by '('. ** If found, we have a prototype. Otherwise we are dealing with a ** variable definition. */ static int isVariableDef(Token *pFirst, Token *pEnd){ - if( pEnd && pEnd->zText[0]=='=' && + if( pEnd && pEnd->zText[0]=='=' && (pEnd->pPrev->nText!=8 || strncmp(pEnd->pPrev->zText,"operator",8)!=0) ){ return 1; } while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){ @@ -1949,11 +1949,11 @@ } while( pFirst!=0 && pFirst->pNext!=pEnd && ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0) || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0)) ){ - /* Lose the initial "static" or local from local variables. + /* Lose the initial "static" or local from local variables. ** We'll prepend "extern" later. */ pFirst = pFirst->pNext; isLocal = 1; } if( pFirst==0 || !isLocal ){ @@ -1962,11 +1962,11 @@ }else if( flags & PS_Method ){ /* Methods are declared by their class. Don't declare separately. */ return nErr; } isVar = (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd); - if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0 + if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0 && (flags & PS_Extern)==0 ){ fprintf(stderr,"%s:%d: Can't define a variable in this context\n", zFilename, pFirst->nLine); nErr++; } @@ -2095,11 +2095,11 @@ nCmd++; } if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){ /* - ** Pop the if stack + ** Pop the if stack */ pIf = ifStack; if( pIf==0 ){ fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine); return 1; @@ -2106,11 +2106,11 @@ } ifStack = pIf->pNext; SafeFree(pIf); }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){ /* - ** Record a #define if we are in PS_Interface or PS_Export + ** Record a #define if we are in PS_Interface or PS_Export */ Decl *pDecl; if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; } zArg = &zCmd[6]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ @@ -2129,11 +2129,11 @@ }else if( flags & PS_Local ){ DeclSetProperty(pDecl,DP_Local); } }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){ /* - ** Record an #include if we are in PS_Interface or PS_Export + ** Record an #include if we are in PS_Interface or PS_Export */ Include *pInclude; char *zIf; if( !(flags & (PS_Interface|PS_Export)) ){ return 0; } @@ -2184,11 +2184,11 @@ PushIfMacro(0,0,0,pToken->nLine,PS_Local); }else{ PushIfMacro(0,zArg,nArg,pToken->nLine,0); } }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){ - /* + /* ** Push an #ifdef. */ zArg = &zCmd[5]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ zArg++; @@ -2207,11 +2207,11 @@ if( *zArg==0 || *zArg=='\n' ){ return 0; } nArg = pToken->nText + (int)(pToken->zText - zArg); PushIfMacro("!defined",zArg,nArg,pToken->nLine,0); }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){ /* - ** Invert the #if on the top of the stack + ** Invert the #if on the top of the stack */ if( ifStack==0 ){ fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename, pToken->nLine); return 1; @@ -2224,33 +2224,33 @@ }else{ pIf->flags = 0; } }else{ /* - ** This directive can be safely ignored + ** This directive can be safely ignored */ return 0; } - /* - ** Recompute the preset flags + /* + ** Recompute the preset flags */ *pPresetFlags = 0; for(pIf = ifStack; pIf; pIf=pIf->pNext){ *pPresetFlags |= pIf->flags; } - + return nErr; } /* ** Parse an entire file. Return the number of errors. ** ** pList is a list of tokens in the file. Whitespace tokens have been ** eliminated, and text with {...} has been collapsed into a ** single TT_Brace token. -** +** ** initFlags are a set of parse flags that should always be set for this ** file. For .c files this is normally 0. For .h files it is PS_Interface. */ static int ParseFile(Token *pList, int initFlags){ int nErr = 0; @@ -2279,11 +2279,11 @@ pStart = 0; flags = presetFlags; break; case '=': - if( pList->pPrev->nText==8 + if( pList->pPrev->nText==8 && strncmp(pList->pPrev->zText,"operator",8)==0 ){ break; } nErr += ProcessDecl(pStart,pList,flags); pStart = 0; @@ -2471,11 +2471,11 @@ pDecl->zExtra = 0; } /* ** Reset the DP_Forward and DP_Declared flags on all Decl structures. -** Set both flags for anything that is tagged as local and isn't +** Set both flags for anything that is tagged as local and isn't ** in the file zFilename so that it won't be printing in other files. */ static void ResetDeclFlags(char *zFilename){ Decl *pDecl; @@ -2574,11 +2574,11 @@ int flag; int isCpp; /* True if generating C++ */ int doneTypedef = 0; /* True if a typedef has been done for this object */ /* printf("BEGIN %s of %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/ - /* + /* ** For any object that has a forward declaration, go ahead and do the ** forward declaration first. */ isCpp = (pState->flags & DP_Cplusplus) != 0; for(p=pDecl; p; p=p->pSameName){ @@ -2626,12 +2626,12 @@ ** function on a recursive call with the same pDecl. Hence, recursive ** calls to this function (through ScanText()) can never change the ** value of DP_Flag out from under us. */ for(p=pDecl; p; p=p->pSameName){ - if( !DeclHasProperty(p,DP_Declared) - && (p->zFwd==0 || needFullDecl) + if( !DeclHasProperty(p,DP_Declared) + && (p->zFwd==0 || needFullDecl) && p->zDecl!=0 ){ DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag); }else{ DeclClearProperty(p,DP_Flag); @@ -2735,12 +2735,12 @@ ** by sToken. */ pDecl = FindDecl(sToken.zText,sToken.nText); if( pDecl==0 ) continue; - /* - ** If we get this far, we've found an identifier that has a + /* + ** If we get this far, we've found an identifier that has a ** declaration in the database. Now see if we the full declaration ** or just a forward declaration. */ GetNonspaceToken(&sIn,&sNext); if( sNext.zText[0]=='*' ){ @@ -2770,12 +2770,12 @@ int progress; do{ progress = 0; for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){ - if( DeclHasProperty(pDecl,DP_Forward) - && !DeclHasProperty(pDecl,DP_Declared) + if( DeclHasProperty(pDecl,DP_Forward) + && !DeclHasProperty(pDecl,DP_Declared) ){ DeclareObject(pDecl,pState,1); progress = 1; assert( DeclHasProperty(pDecl,DP_Declared) ); } @@ -2842,11 +2842,11 @@ nErr++; } }else if( report ){ fprintf(report,"unchanged\n"); } - SafeFree(zOldVersion); + SafeFree(zOldVersion); IdentTableReset(&includeTable); StringReset(&outStr); return nErr; } @@ -2878,11 +2878,11 @@ } ChangeIfContext(0,&sState); printf("%s",StringGet(&outStr)); IdentTableReset(&includeTable); StringReset(&outStr); - return 0; + return 0; } #ifdef DEBUG /* ** Return the number of characters in the given string prior to the @@ -3040,11 +3040,11 @@ int nSrc; char *zSrc; InFile *pFile; int i; - /* + /* ** Get the name of the input file to be scanned. The input file is ** everything before the first ':' or the whole file if no ':' is seen. ** ** Except, on windows, ignore any ':' that occurs as the second character ** since it might be part of the drive specifier. So really, the ":' has @@ -3099,11 +3099,11 @@ } } /* ** If pFile->zSrc contains no 'c' or 'C' in its extension, it - ** must be a header file. In that case, we need to set the + ** must be a header file. In that case, we need to set the ** PS_Interface flag. */ pFile->flags |= PS_Interface; for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){ if( zSrc[i]=='c' || zSrc[i]=='C' ){ @@ -3110,11 +3110,11 @@ pFile->flags &= ~PS_Interface; break; } } - /* Done! + /* Done! */ return pFile; } /* MS-Windows and MS-DOS both have the following serious OS bug: the @@ -3162,11 +3162,11 @@ while( c!=EOF ){ while( c!=EOF && isspace(c) ){ if( c=='\n' ){ startOfLine = 1; } - c = getc(in); + c = getc(in); if( startOfLine && c=='#' ){ while( c!=EOF && c!='\n' ){ c = getc(in); } } @@ -3184,11 +3184,11 @@ if( nAlloc==0 ){ nAlloc = 100 + argc; zNew = malloc( sizeof(char*) * nAlloc ); }else{ nAlloc *= 2; - zNew = realloc( zNew, sizeof(char*) * nAlloc ); + zNew = realloc( zNew, sizeof(char*) * nAlloc ); } } if( zNew ){ int j = nNew + index; zNew[j] = malloc( n + 1 ); @@ -3254,11 +3254,11 @@ /* ** The following text contains a few simple #defines that we want ** to be available to every file. */ -static char zInit[] = +static const char zInit[] = "#define INTERFACE 0\n" "#define EXPORT_INTERFACE 0\n" "#define LOCAL_INTERFACE 0\n" "#define EXPORT\n" "#define LOCAL static\n" Index: src/makemake.tcl ================================================================== --- src/makemake.tcl +++ src/makemake.tcl @@ -1,8 +1,8 @@ #!/usr/bin/tclsh # -# Run this TCL script to generate the various makefiles for a variety +# Run this Tcl script to generate the various makefiles for a variety # of platforms. Files generated include: # # src/main.mk # makefile for all unix systems # win/Makefile.mingw # makefile for mingw on windows # win/Makefile.* # makefiles for other windows compilers @@ -142,10 +142,11 @@ # Additional resource files that get built into the executable. # set extra_files { diff.tcl + ../skins/*/*.txt } # Options used to compile the included SQLite library. # set SQLITE_OPTIONS { @@ -203,10 +204,19 @@ } # STOP HERE. # Unless the build procedures changes, you should not have to edit anything # below this line. + +# Expand any wildcards in "extra_files" +set new_extra_files {} +foreach file $extra_files { + foreach x [glob -nocomplain $file] { + lappend new_extra_files $x + } +} +set extra_files $new_extra_files ############################################################################## ############################################################################## ############################################################################## # Start by generating the "main.mk" makefile used for all unix systems. @@ -573,12 +583,13 @@ #### 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. # -OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1k/include -OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1k +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l +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 @@ -1305,11 +1316,11 @@ # Uncomment to enable Tcl support # FOSSIL_ENABLE_TCL = 1 !ifdef FOSSIL_ENABLE_SSL -SSLDIR = $(B)\compat\openssl-1.0.1k +SSLDIR = $(B)\compat\openssl-1.0.1l SSLINCDIR = $(SSLDIR)\inc32 SSLLIBDIR = $(SSLDIR)\out32 SSLLFLAGS = /nologo /opt:ref /debug SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" Index: src/mkbuiltin.c ================================================================== --- src/mkbuiltin.c +++ src/mkbuiltin.c @@ -29,11 +29,11 @@ #include <string.h> /* ** Read the entire content of the file named zFilename into memory obtained -** from malloc() and retur a pointer to that memory. Write the size of the +** 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){ FILE *in; unsigned char *z; @@ -62,10 +62,11 @@ */ typedef struct Resource Resource; struct Resource { const char *zName; int nByte; + int idx; }; /* ** Compare two Resource objects for sorting purposes. They sort ** in zName order so that Fossil can search for resources using @@ -82,10 +83,11 @@ int j, n; Resource *aRes; int nRes = argc-1; unsigned char *pData; int nErr = 0; + int nSkip; aRes = malloc( nRes*sizeof(aRes[0]) ); if( aRes==0 ){ fprintf(stderr, "malloc failed\n"); return 1; @@ -103,15 +105,24 @@ if( pData==0 ){ fprintf(stderr, "Cannot open file [%s]\n", aRes[i].zName); nErr++; continue; } - aRes[i].nByte = sz; + + /* 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); - for(j=n=0; j<=sz; j++){ + i, sz+1-nSkip); + for(j=nSkip, n=0; j<=sz; j++){ printf("%3d", pData[j]); if( j==sz ){ printf(" };\n"); }else if( n==14 ){ printf(",\n "); @@ -129,17 +140,26 @@ printf(" const unsigned char *pData;\n"); printf(" int nByte;\n"); printf("};\n"); printf("static const BuiltinFileTable aBuiltinFiles[] = {\n"); for(i=0; i<nRes; i++){ - const char *zTail; const char *z = aRes[i].zName; + const char *zTail; + int nSlash = 0; zTail = z; while( z && z[0] ){ - if( z[0]=='/' || z[0]=='\\' ) zTail = &z[1]; + if( z[0]=='/' || z[0]=='\\' ){ + nSlash++; + if( nSlash<=2 || z[-1]=='.' ) zTail = &z[1]; + } z++; } - printf(" { \"%s\", bidata%d, %d },\n", zTail, i, aRes[i].nByte); + aRes[i].zName = zTail; + } + qsort(aRes, nRes, sizeof(aRes[0]), compareResource); + for(i=0; i<nRes; i++){ + printf(" { \"%s\", bidata%d, %d },\n", + aRes[i].zName, aRes[i].idx, aRes[i].nByte); } printf("};\n"); return nErr; } Index: src/mkindex.c ================================================================== --- src/mkindex.c +++ src/mkindex.c @@ -217,11 +217,11 @@ if( zLine[i]!='(' ) goto page_skip; nFixed = nUsed; nHelp = 0; return; -page_skip: +page_skip: for(i=nFixed; i<nUsed; i++){ fprintf(stderr,"%s:%d: skipping page \"%s\"\n", zFile, nLine, aEntry[i].zPath); } nUsed = nFixed; @@ -327,11 +327,11 @@ aEntry[i].zHelp[0] = 0; } } puts("struct CmdHelp {" "int eType; " - "char const * zText;" + "const char *zText;" "};"); puts("static struct CmdHelp aCmdHelp[] = {"); for(i=0; i<nFixed; i++){ if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); if( aEntry[i].zHelp==0 ){ @@ -361,11 +361,11 @@ scan_for_label("WEBPAGE:",zLine,0); scan_for_label("COMMAND:",zLine,1); scan_for_func(zLine); } fclose(in); - nUsed = nFixed; + nUsed = nFixed; } int main(int argc, char **argv){ int i; for(i=1; i<argc; i++){ Index: src/mkversion.c ================================================================== --- src/mkversion.c +++ src/mkversion.c @@ -43,11 +43,11 @@ exit(1); } fclose(v); for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} *z = 0; - printf("#define RELEASE_VERSION \"%s\"\n", b); + printf("#define RELEASE_VERSION \"%s\"\n", b); x=0; i=0; z=b; while(1){ if( z[0]>='0' && z[0]<='9' ){ Index: src/report.c ================================================================== --- src/report.c +++ src/report.c @@ -171,10 +171,11 @@ /* We've already seen an error. No need to continue. */ return SQLITE_OK; } switch( code ){ case SQLITE_SELECT: + case SQLITE_RECURSIVE: case SQLITE_FUNCTION: { break; } case SQLITE_READ: { static const char *const azAllowed[] = { @@ -201,15 +202,10 @@ }else if( !g.perm.RdAddr && strncmp(zArg2, "private_", 8)==0 ){ rc = SQLITE_IGNORE; } break; } - case SQLITE_RECURSIVE: { - *(char**)pError = mprintf("recursive queries are not allowed"); - rc = SQLITE_DENY; - break; - } default: { *(char**)pError = mprintf("only SELECT statements are allowed"); rc = SQLITE_DENY; break; } @@ -240,15 +236,17 @@ const char *zTail; sqlite3_stmt *pStmt; int rc; /* First make sure the SQL is a single query command by verifying that - ** the first token is "SELECT" and that there are no unquoted semicolons. + ** the first token is "SELECT" or "WITH" and that there are no unquoted + ** semicolons. */ for(i=0; fossil_isspace(zSql[i]); i++){} - if( fossil_strnicmp(&zSql[i],"select",6)!=0 ){ - return mprintf("The SQL must be a SELECT statement"); + if( fossil_strnicmp(&zSql[i], "select", 6)!=0 + && fossil_strnicmp(&zSql[i], "with", 4)!=0 ){ + return mprintf("The SQL must be a SELECT or WITH statement"); } for(i=0; zSql[i]; i++){ if( zSql[i]==';' ){ int bad; int c = zSql[i+1]; @@ -936,10 +934,11 @@ ** t Sort by text ** n Sort numerically ** k Sort by the data-sortkey property ** x This column is not sortable ** +** Capital letters mean sort in reverse order. ** If there are fewer characters in zColumnTypes[] than their are columns, ** the all extra columns assume type "t" (text). ** ** The third parameter is the column that was initially sorted (using 1-based ** column numbers, like SQL). Make this value 0 if none of the columns are @@ -959,14 +958,14 @@ @ this.columnTypes = columnTypes; @ this.sort = function (cell) { @ var column = cell.cellIndex; @ var sortFn; @ switch( cell.sortType ){ - @ case "n": sortFn = this.sortNumeric; break; - @ case "t": sortFn = this.sortText; break; - @ case "k": sortFn = this.sortKey; break; - @ case "x": return; + @ case "N": case "n": sortFn = this.sortNumeric; break; + @ case "T": case "t": sortFn = this.sortText; break; + @ case "K": case "k": sortFn = this.sortKey; break; + @ default: return; @ } @ this.sortIndex = column; @ var newRows = new Array(); @ for (j = 0; j < this.tbody[0].rows.length; j++) { @ newRows[j] = this.tbody[0].rows[j]; @@ -975,10 +974,13 @@ @ newRows.reverse(); @ this.prevColumn = -this.prevColumn; @ }else{ @ newRows.sort(sortFn); @ this.prevColumn = this.sortIndex+1; + @ if( cell.sortType>="A" && cell.sortType<="Z" ){ + @ newRows.reverse(); + @ } @ } @ for (i=0;i<newRows.length;i++) { @ this.tbody[0].appendChild(newRows[i]); @ } @ this.setHdrIcons(); @@ -1002,27 +1004,28 @@ @ } @ this.sortText = function(a,b) { @ var i = thisObject.sortIndex; @ aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); @ bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); - @ if(aa==bb) return 0; + @ if(aa==bb) return a.rowIndex-b.rowIndex; @ if(aa<bb) return -1; @ return 1; @ } @ this.sortNumeric = function(a,b) { @ var i = thisObject.sortIndex; @ aa = parseFloat(a.cells[i].textContent); @ if (isNaN(aa)) aa = 0; @ bb = parseFloat(b.cells[i].textContent); @ if (isNaN(bb)) bb = 0; + @ if(aa==bb) return a.rowIndex-b.rowIndex; @ return aa-bb; @ } @ this.sortKey = function(a,b) { @ var i = thisObject.sortIndex; @ aa = a.cells[i].getAttribute("data-sortkey"); @ bb = b.cells[i].getAttribute("data-sortkey"); - @ if(aa==bb) return 0; + @ if(aa==bb) return a.rowIndex-b.rowIndex; @ if(aa<bb) return -1; @ return 1; @ } @ var x = tableEl.getElementsByTagName('thead'); @ if(!(this.tbody && this.tbody[0].rows && this.tbody[0].rows.length>0)){ Index: src/search.c ================================================================== --- src/search.c +++ src/search.c @@ -165,11 +165,11 @@ int i; azDoc = fossil_malloc( sizeof(const char*)*(argc+1) ); for(i=0; i<argc; i++) azDoc[i] = (const char*)sqlite3_value_text(argv[i]); score = search_score(p, argc, azDoc); - fossil_free(azDoc); + fossil_free((void *)azDoc); sqlite3_result_int(context, score); } /* ** Register the "score()" SQL function to score its input text Index: src/setup.c ================================================================== --- src/setup.c +++ src/setup.c @@ -1525,24 +1525,26 @@ login_needed(); } db_begin_transaction(); if( P("clear")!=0 ){ db_multi_exec("DELETE FROM config WHERE name='css'"); - cgi_replace_parameter("css", zDefaultCSS); + cgi_replace_parameter("css", builtin_text("skins/default.css")); db_end_transaction(0); cgi_redirect("setup_editcss"); } if( P("submit")!=0 ){ - textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS, 0); + textarea_attribute(0, 0, 0, "css", "css", + builtin_text("skins/default.css"), 0); db_end_transaction(0); cgi_redirect("setup_editcss"); } style_header("Edit CSS"); @ <form action="%s(g.zTop)/setup_editcss" method="post"><div> login_insert_csrf_secret(); @ Edit the CSS below:<br /> - textarea_attribute("", 35, 80, "css", "css", zDefaultCSS, 0); + textarea_attribute("", 35, 80, "css", "css", + builtin_text("skins/default.css"), 0); @ <br /> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Revert To Default" /> @ </div></form> @ <p><span class="note">Note:</span> Press your browser Reload button after @@ -1568,15 +1570,17 @@ login_needed(); } db_begin_transaction(); if( P("clear")!=0 ){ db_multi_exec("DELETE FROM config WHERE name='header'"); - cgi_replace_parameter("header", zDefaultHeader); + cgi_replace_parameter("header", builtin_text("skins/default.header")); }else if( P("submit")!=0 ){ - textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader, 0); + textarea_attribute(0, 0, 0, "header", "header", + builtin_text("skins/default.header"), 0); }else if( P("fixbase")!=0 ){ - const char *z = db_get("header", (char*)zDefaultHeader); + const char *z = db_get("header", + (char*)builtin_text("skins/default.header")); char *zHead = strstr(z, "<head>"); if( strstr(z, "<base href=")==0 && zHead!=0 ){ char *zNew; char *zTail = &zHead[6]; while( fossil_isspace(zTail[0]) ) zTail++; @@ -1598,14 +1602,15 @@ @ <tt><head></tt> in the header! @ <input type="submit" name="fixbase" value="Add <base> Now"></p> } login_insert_csrf_secret(); - @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to + @ <p>Edit HTML text with embedded TH1 (a Tcl dialect) that will be used to @ generate the beginning of every page through start of the main @ menu.</p> - textarea_attribute("", 35, 80, "header", "header", zDefaultHeader, 0); + textarea_attribute("", 35, 80, "header", "header", + builtin_text("skins/default.header"), 0); @ <br /> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Revert To Default" /> @ </div></form> @ <hr /> @@ -1612,11 +1617,11 @@ @ The default header is shown below for reference. Other examples @ of headers can be seen on the <a href="setup_skin">skins page</a>. @ See also the <a href="setup_editcss">CSS</a> and @ <a href="setup_footer">footer</a> editing screens. @ <blockquote><pre> - @ %h(zDefaultHeader) + @ %h(builtin_text("skins/default.header")) @ </pre></blockquote> style_footer(); db_end_transaction(0); } @@ -1629,19 +1634,20 @@ login_needed(); } db_begin_transaction(); if( P("clear")!=0 ){ db_multi_exec("DELETE FROM config WHERE name='footer'"); - cgi_replace_parameter("footer", zDefaultFooter); + cgi_replace_parameter("footer", builtin_text("skins/default.footer")); } style_header("Edit Page Footer"); @ <form action="%s(g.zTop)/setup_footer" method="post"><div> login_insert_csrf_secret(); - @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to + @ <p>Edit HTML text with embedded TH1 (a Tcl dialect) that will be used to @ generate the end of every page.</p> - textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter, 0); + textarea_attribute("", 20, 80, "footer", "footer", + builtin_text("skins/default.footer"), 0); @ <br /> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Revert To Default" /> @ </div></form> @ <hr /> @@ -1648,11 +1654,11 @@ @ The default footer is shown below for reference. Other examples @ of footers can be seen on the <a href="setup_skin">skins page</a>. @ See also the <a href="setup_editcss">CSS</a> and @ <a href="setup_header">header</a> editing screens. @ <blockquote><pre> - @ %h(zDefaultFooter) + @ %h(builtin_text("skins/default.footer")) @ </pre></blockquote> style_footer(); db_end_transaction(0); } @@ -1715,13 +1721,15 @@ } style_header("Edit Ad Unit"); @ <form action="%s(g.zTop)/setup_adunit" method="post"><div> login_insert_csrf_secret(); - @ <p>Edit HTML text for an ad unit that will be inserted after the - @ menu bar and above the content of every page.</p> - textarea_attribute("", 20, 80, "adunit", "adunit", "", 0); + @ <b>Banner Ad-Unit:</b><br /> + textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); + @ <br /> + @ <b>Right-Column Ad-Unit:</b><br /> + textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); @ <br /> onoff_attribute("Omit ads to administrator", "adunit-omit-if-admin", "oia", 0, 0); @ <br /> onoff_attribute("Omit ads to logged-in users", @@ -1728,10 +1736,41 @@ "adunit-omit-if-user", "oiu", 0, 0); @ <br /> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Delete Ad-Unit" /> @ </div></form> + @ <hr /> + @ <b>Ad-Unit Notes:</b><ul> + @ <li>Leave both Ad-Units blank to disable all advertising. + @ <li>The "Banner Ad-Unit" is used for wide pages. + @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. + @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is used on all pages. + @ <li>Suggested <a href="setup_editcss">CSS</a> changes: + @ <blockquote><pre> + @ div.adunit_banner { + @ margin: auto; + @ width: 100%; + @ } + @ div.adunit_right { + @ float: right; + @ } + @ div.adunit_right_container { + @ min-height: <i>height-of-right-column-ad-unit</i>; + @ } + @ </pre></blockquote> + @ <li>For a place-holder Ad-Unit for testing, Copy/Paste the following + @ with appropriate adjustments to "width:" and "height:". + @ <blockquote><pre> + @ <div style=' + @ margin: 0 auto; + @ width: 600px; + @ height: 90px; + @ border: 1px solid #f11; + @ background-color: #fcc; + @ '>Demo Ad</div> + @ </pre></blockquote> + @ </li> style_footer(); db_end_transaction(0); } /* Index: src/sitemap.c ================================================================== --- src/sitemap.c +++ src/sitemap.c @@ -27,10 +27,11 @@ ** Show an incomplete list of web pages offered by the Fossil web engine. */ void sitemap_page(void){ login_check_credentials(); style_header("Site Map"); + style_adunit_config(ADUNIT_RIGHT_OK); @ <p> @ The following links are just a few of the many web-pages available for @ this Fossil repository: @ </p> @ Index: src/skins.c ================================================================== --- src/skins.c +++ src/skins.c @@ -19,1255 +19,38 @@ */ #include "config.h" #include <assert.h> #include "skins.h" -/* @-comment: ## */ -/* -** A black-and-white theme with the project title in a bar across the top -** and no logo image. -*/ -static const char zBuiltinSkin1[] = -@ REPLACE INTO config(name,mtime,value) -@ VALUES('css',now(),'/* General settings for the entire page */ -@ body { -@ margin: 0ex 1ex; -@ padding: 0px; -@ background-color: white; -@ font-family: sans-serif; -@ } -@ -@ /* The project logo in the upper left-hand corner of each page */ -@ div.logo { -@ display: table-row; -@ text-align: center; -@ /* vertical-align: bottom;*/ -@ font-size: 2em; -@ font-weight: bold; -@ background-color: #707070; -@ color: #ffffff; -@ min-width: 200px; -@ white-space: nowrap; -@ } -@ -@ /* The page title centered at the top of each page */ -@ div.title { -@ display: table-cell; -@ font-size: 1.5em; -@ font-weight: bold; -@ text-align: center; -@ padding: 0 0 0 10px; -@ color: #404040; -@ vertical-align: bottom; -@ width: 100%; -@ } -@ -@ /* The login status message in the top right-hand corner */ -@ div.status { -@ display: table-cell; -@ text-align: right; -@ vertical-align: bottom; -@ color: #404040; -@ font-size: 0.8em; -@ font-weight: bold; -@ min-width: 200px; -@ white-space: nowrap; -@ } -@ -@ /* The header across the top of the page */ -@ div.header { -@ display: table; -@ width: 100%; -@ } -@ -@ /* The main menu bar that appears at the top of the page beneath -@ ** the header */ -@ div.mainmenu { -@ padding: 5px 10px 5px 10px; -@ font-size: 0.9em; -@ font-weight: bold; -@ text-align: center; -@ letter-spacing: 1px; -@ background-color: #404040; -@ color: white; -@ } -@ -@ /* The submenu bar that *sometimes* appears below the main menu */ -@ div.submenu, div.sectionmenu { -@ padding: 3px 10px 3px 0px; -@ font-size: 0.9em; -@ text-align: center; -@ background-color: #606060; -@ color: white; -@ } -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { -@ padding: 3px 10px 3px 10px; -@ color: white; -@ text-decoration: none; -@ } -@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { -@ color: #404040; -@ background-color: white; -@ } -@ -@ /* All page content from the bottom of the menu or submenu down to -@ ** the footer */ -@ div.content { -@ padding: 0ex 0ex 0ex 0ex; -@ } -@ /* Hyperlink colors */ -@ div.content a { color: #604000; } -@ div.content a:link { color: #604000;} -@ div.content a:visited { color: #600000; } -@ -@ /* <verbatim> blocks */ -@ pre.verbatim { -@ background-color: #ffffff; -@ padding: 0.5em; -@ white-space: pre-wrap; -@ } -@ -@ /* Some pages have section dividers */ -@ div.section { -@ margin-bottom: 0px; -@ margin-top: 1em; -@ padding: 1px 1px 1px 1px; -@ font-size: 1.2em; -@ font-weight: bold; -@ background-color: #404040; -@ color: white; -@ white-space: nowrap; -@ } -@ -@ /* The "Date" that occurs on the left hand side of timelines */ -@ div.divider { -@ background: #a0a0a0; -@ border: 2px #505050 solid; -@ font-size: 1em; font-weight: normal; -@ padding: .25em; -@ margin: .2em 0 .2em 0; -@ float: left; -@ clear: left; -@ white-space: nowrap; -@ } -@ -@ /* The footer at the very bottom of the page */ -@ div.footer { -@ font-size: 0.8em; -@ margin-top: 12px; -@ padding: 5px 10px 5px 10px; -@ text-align: right; -@ background-color: #404040; -@ color: white; -@ } -@ -@ /* The label/value pairs on (for example) the vinfo page */ -@ table.label-value th { -@ vertical-align: top; -@ text-align: right; -@ padding: 0.2ex 2ex; -@ }'); -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> -@ <head> -@ <base href="$baseurl/$current_page" /> -@ <title>$<project_name>: $<title> -@ -@ -@ -@ -@
-@
$
$</div> -@ <div class="status"><th1> -@ if {[info exists login]} { -@ puts "Logged in as $login" -@ } else { -@ puts "Not logged in" -@ } -@ </th1></div> -@ </div> -@ <div class="mainmenu"> -@ <th1> -@ html "<a href=''$home$index_page''>Home</a>\n" -@ if {[anycap jor]} { -@ html "<a href=''$home/timeline''>Timeline</a>\n" -@ } -@ if {[hascap oh]} { -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" -@ } -@ if {[hascap o]} { -@ html "<a href=''$home/brlist''>Branches</a>\n" -@ html "<a href=''$home/taglist''>Tags</a>\n" -@ } -@ if {[hascap r]} { -@ html "<a href=''$home/reportlist''>Tickets</a>\n" -@ } -@ if {[hascap j]} { -@ html "<a href=''$home/wiki''>Wiki</a>\n" -@ } -@ if {[hascap s]} { -@ html "<a href=''$home/setup''>Admin</a>\n" -@ } elseif {[hascap a]} { -@ html "<a href=''$home/setup_ulist''>Users</a>\n" -@ } -@ if {[info exists login]} { -@ html "<a href=''$home/login''>Logout</a>\n" -@ } else { -@ html "<a href=''$home/login''>Login</a>\n" -@ } -@ </th1></div> -@ '); -@ REPLACE INTO config(name,mtime,value) -@ VALUES('footer',now(),'<div class="footer"> -@ Fossil version $manifest_version $manifest_date -@ </div> -@ </body></html> -@ '); -; - -/* -** A tan theme with the project title above the user identification -** and no logo image. -*/ -static const char zBuiltinSkin2[] = -@ REPLACE INTO config(name,mtime,value) -@ VALUES('css',now(),'/* General settings for the entire page */ -@ body { -@ margin: 0ex 0ex; -@ padding: 0px; -@ background-color: #fef3bc; -@ font-family: sans-serif; -@ } -@ -@ /* The project logo in the upper left-hand corner of each page */ -@ div.logo { -@ display: inline; -@ text-align: center; -@ vertical-align: bottom; -@ font-weight: bold; -@ font-size: 2.5em; -@ color: #a09048; -@ white-space: nowrap; -@ } -@ -@ /* The page title centered at the top of each page */ -@ div.title { -@ display: table-cell; -@ font-size: 2em; -@ font-weight: bold; -@ text-align: left; -@ padding: 0 0 0 5px; -@ color: #a09048; -@ vertical-align: bottom; -@ width: 100%; -@ } -@ -@ /* The login status message in the top right-hand corner */ -@ div.status { -@ display: table-cell; -@ text-align: right; -@ vertical-align: bottom; -@ color: #a09048; -@ padding: 5px 5px 0 0; -@ font-size: 0.8em; -@ font-weight: bold; -@ white-space: nowrap; -@ } -@ -@ /* The header across the top of the page */ -@ div.header { -@ display: table; -@ width: 100%; -@ } -@ -@ /* The main menu bar that appears at the top of the page beneath -@ ** the header */ -@ div.mainmenu { -@ padding: 5px 10px 5px 10px; -@ font-size: 0.9em; -@ font-weight: bold; -@ text-align: center; -@ letter-spacing: 1px; -@ background-color: #a09048; -@ color: black; -@ } -@ -@ /* The submenu bar that *sometimes* appears below the main menu */ -@ div.submenu, div.sectionmenu { -@ padding: 3px 10px 3px 0px; -@ font-size: 0.9em; -@ text-align: center; -@ background-color: #c0af58; -@ color: white; -@ } -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { -@ padding: 3px 10px 3px 10px; -@ color: white; -@ text-decoration: none; -@ } -@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { -@ color: #a09048; -@ background-color: white; -@ } -@ -@ /* All page content from the bottom of the menu or submenu down to -@ ** the footer */ -@ div.content { -@ padding: 1ex 5px; -@ } -@ div.content a { color: #706532; } -@ div.content a:link { color: #706532; } -@ div.content a:visited { color: #704032; } -@ div.content a:hover { background-color: white; color: #706532; } -@ -@ /* Some pages have section dividers */ -@ div.section { -@ margin-bottom: 0px; -@ margin-top: 1em; -@ padding: 3px 3px 0 3px; -@ font-size: 1.2em; -@ font-weight: bold; -@ background-color: #a09048; -@ color: white; -@ white-space: nowrap; -@ } -@ -@ /* The "Date" that occurs on the left hand side of timelines */ -@ div.divider { -@ background: #e1d498; -@ border: 2px #a09048 solid; -@ font-size: 1em; font-weight: normal; -@ padding: .25em; -@ margin: .2em 0 .2em 0; -@ float: left; -@ clear: left; -@ white-space: nowrap; -@ } -@ -@ /* The footer at the very bottom of the page */ -@ div.footer { -@ font-size: 0.8em; -@ margin-top: 12px; -@ padding: 5px 10px 5px 10px; -@ text-align: right; -@ background-color: #a09048; -@ color: white; -@ } -@ -@ /* Hyperlink colors */ -@ div.footer a { color: white; } -@ div.footer a:link { color: white; } -@ div.footer a:visited { color: white; } -@ div.footer a:hover { background-color: white; color: #558195; } -@ -@ /* <verbatim> blocks */ -@ pre.verbatim { -@ background-color: #f5f5f5; -@ padding: 0.5em; -@ white-space: pre-wrap; -@ } -@ -@ /* The label/value pairs on (for example) the ci page */ -@ table.label-value th { -@ vertical-align: top; -@ text-align: right; -@ padding: 0.2ex 2ex; -@ }'); -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> -@ <head> -@ <base href="$baseurl/$current_page" /> -@ <title>$<project_name>: $<title> -@ -@ -@ -@ -@
-@
$</div> -@ <div class="status"> -@ <div class="logo">$<project_name></div><br/> -@ <th1> -@ if {[info exists login]} { -@ puts "Logged in as $login" -@ } else { -@ puts "Not logged in" -@ } -@ </th1></div> -@ </div> -@ <div class="mainmenu"> -@ <th1> -@ html "<a href=''$home$index_page''>Home</a>\n" -@ if {[anycap jor]} { -@ html "<a href=''$home/timeline''>Timeline</a>\n" -@ } -@ if {[hascap oh]} { -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" -@ } -@ if {[hascap o]} { -@ html "<a href=''$home/brlist''>Branches</a>\n" -@ html "<a href=''$home/taglist''>Tags</a>\n" -@ } -@ if {[hascap r]} { -@ html "<a href=''$home/reportlist''>Tickets</a>\n" -@ } -@ if {[hascap j]} { -@ html "<a href=''$home/wiki''>Wiki</a>\n" -@ } -@ if {[hascap s]} { -@ html "<a href=''$home/setup''>Admin</a>\n" -@ } elseif {[hascap a]} { -@ html "<a href=''$home/setup_ulist''>Users</a>\n" -@ } -@ if {[info exists login]} { -@ html "<a href=''$home/login''>Logout</a>\n" -@ } else { -@ html "<a href=''$home/login''>Login</a>\n" -@ } -@ </th1></div> -@ '); -@ REPLACE INTO config(name,mtime,value) -@ VALUES('footer',now(),'<div class="footer"> -@ Fossil version $manifest_version $manifest_date -@ </div> -@ </body></html> -@ '); -; - -/* -** Black letters on a white or cream background with the main menu -** stuck on the left-hand side. -*/ -static const char zBuiltinSkin3[] = -@ REPLACE INTO config(name,mtime,value) -@ VALUES('css',now(),'/* General settings for the entire page */ -@ body { -@ margin:0px 0px 0px 0px; -@ padding:0px; -@ font-family:verdana, arial, helvetica, "sans serif"; -@ color:#333; -@ background-color:white; -@ } -@ -@ /* consistent colours */ -@ h2 { -@ color: #333; -@ } -@ h3 { -@ color: #333; -@ } -@ -@ /* The project logo in the upper left-hand corner of each page */ -@ div.logo { -@ display: table-cell; -@ text-align: left; -@ vertical-align: bottom; -@ font-weight: bold; -@ color: #333; -@ white-space: nowrap; -@ } -@ -@ /* The page title centered at the top of each page */ -@ div.title { -@ display: table-cell; -@ font-size: 2em; -@ font-weight: bold; -@ text-align: center; -@ color: #333; -@ vertical-align: bottom; -@ width: 100%; -@ } -@ -@ /* The login status message in the top right-hand corner */ -@ div.status { -@ display: table-cell; -@ padding-right: 10px; -@ text-align: right; -@ vertical-align: bottom; -@ padding-bottom: 5px; -@ color: #333; -@ font-size: 0.8em; -@ font-weight: bold; -@ white-space: nowrap; -@ } -@ -@ /* The header across the top of the page */ -@ div.header { -@ margin:10px 0px 10px 0px; -@ padding:1px 0px 0px 20px; -@ border-style:solid; -@ border-color:black; -@ border-width:1px 0px; -@ background-color:#eee; -@ } -@ -@ /* The main menu bar that appears at the top left of the page beneath -@ ** the header. Width must be co-ordinated with the container below */ -@ div.mainmenu { -@ float: left; -@ margin-left: 10px; -@ margin-right: 10px; -@ font-size: 0.9em; -@ font-weight: bold; -@ padding:5px; -@ background-color:#eee; -@ border:1px solid #999; -@ width:8em; -@ } -@ -@ /* Main menu is now a list */ -@ div.mainmenu ul { -@ padding: 0; -@ list-style:none; -@ } -@ div.mainmenu a, div.mainmenu a:visited{ -@ padding: 1px 10px 1px 10px; -@ color: #333; -@ text-decoration: none; -@ } -@ div.mainmenu a:hover { -@ color: #eee; -@ background-color: #333; -@ } -@ -@ /* Container for the sub-menu and content so they don''t spread -@ ** out underneath the main menu */ -@ #container { -@ padding-left: 9em; -@ } -@ -@ /* The submenu bar that *sometimes* appears below the main menu */ -@ div.submenu, div.sectionmenu { -@ padding: 3px 10px 3px 10px; -@ font-size: 0.9em; -@ text-align: center; -@ border:1px solid #999; -@ border-width:1px 0px; -@ background-color: #eee; -@ color: #333; -@ } -@ div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, -@ div.sectionmenu>a.button:visited { -@ padding: 3px 10px 3px 10px; -@ color: #333; -@ text-decoration: none; -@ } -@ div.submenu a:hover, div.sectionmenu>a.button:hover { -@ color: #eee; -@ background-color: #333; -@ } -@ -@ /* All page content from the bottom of the menu or submenu down to -@ ** the footer */ -@ div.content { -@ padding: 2ex 1ex 0ex 2ex; -@ } -@ -@ /* Some pages have section dividers */ -@ div.section { -@ margin-bottom: 0px; -@ margin-top: 1em; -@ padding: 1px 1px 1px 1px; -@ font-size: 1.2em; -@ font-weight: bold; -@ border-style:solid; -@ border-color:#999; -@ border-width:1px 0px; -@ background-color: #eee; -@ color: #333; -@ white-space: nowrap; -@ } -@ -@ /* The "Date" that occurs on the left hand side of timelines */ -@ div.divider { -@ background: #eee; -@ border: 2px #999 solid; -@ font-size: 1em; font-weight: normal; -@ padding: .25em; -@ margin: .2em 0 .2em 0; -@ float: left; -@ clear: left; -@ color: #333; -@ white-space: nowrap; -@ } -@ -@ /* The footer at the very bottom of the page */ -@ div.footer { -@ font-size: 0.8em; -@ margin-top: 12px; -@ padding: 5px 10px 5px 10px; -@ text-align: right; -@ background-color: #eee; -@ color: #555; -@ } -@ -@ /* <verbatim> blocks */ -@ pre.verbatim { -@ background-color: #f5f5f5; -@ padding: 0.5em; -@ white-space: pre-wrap; -@ } -@ -@ /* The label/value pairs on (for example) the ci page */ -@ table.label-value th { -@ vertical-align: top; -@ text-align: right; -@ padding: 0.2ex 2ex; -@ }'); -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> -@ <head> -@ <base href="$baseurl/$current_page" /> -@ <title>$<project_name>: $<title> -@ -@ -@ -@ -@
-@ -@
$</div> -@ <div class="status"><th1> -@ if {[info exists login]} { -@ puts "Logged in as $login" -@ } else { -@ puts "Not logged in" -@ } -@ </th1></div> -@ </div> -@ <div class="mainmenu"> -@ <th1> -@ html "<a href=''$home$index_page''>Home</a>\n" -@ if {[anycap jor]} { -@ html "<a href=''$home/timeline''>Timeline</a>\n" -@ } -@ if {[hascap oh]} { -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" -@ } -@ if {[hascap o]} { -@ html "<a href=''$home/brlist''>Branches</a>\n" -@ html "<a href=''$home/taglist''>Tags</a>\n" -@ } -@ if {[hascap r]} { -@ html "<a href=''$home/reportlist''>Tickets</a>\n" -@ } -@ if {[hascap j]} { -@ html "<a href=''$home/wiki''>Wiki</a>\n" -@ } -@ if {[hascap s]} { -@ html "<a href=''$home/setup''>Admin</a>\n" -@ } elseif {[hascap a]} { -@ html "<a href=''$home/setup_ulist''>Users</a>\n" -@ } -@ if {[info exists login]} { -@ html "<a href=''$home/login''>Logout</a>\n" -@ } else { -@ html "<a href=''$home/login''>Login</a>\n" -@ } -@ </th1></ul></div> -@ <div id="container"> -@ '); -@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div> -@ <div class="footer"> -@ Fossil version $manifest_version $manifest_date -@ </div> -@ </body></html> -@ '); -; - - -/* -** Shadow boxes and rounded corners. -*/ -static const char zBuiltinSkin4[] = -@ REPLACE INTO config(name,mtime,value) -@ VALUES('css',now(),'/* General settings for the entire page */ -@ html { -@ min-height: 100%; -@ } -@ body { -@ margin: 0ex 1ex; -@ padding: 0px; -@ background-color: white; -@ color: #333; -@ font-family: Verdana, sans-serif; -@ font-size: 0.8em; -@ } -@ -@ /* The project logo in the upper left-hand corner of each page */ -@ div.logo { -@ display: table-cell; -@ text-align: right; -@ vertical-align: bottom; -@ font-weight: normal; -@ white-space: nowrap; -@ } -@ -@ /* Widths */ -@ div.header, div.mainmenu, div.submenu, div.content, div.footer { -@ max-width: 900px; -@ margin: auto; -@ padding: 3px 20px 3px 20px; -@ clear: both; -@ } -@ -@ /* The page title at the top of each page */ -@ div.title { -@ display: table-cell; -@ padding-left: 10px; -@ font-size: 2em; -@ margin: 10px 0 10px -20px; -@ vertical-align: bottom; -@ text-align: left; -@ width: 80%; -@ font-family: Verdana, sans-serif; -@ font-weight: bold; -@ color: #558195; -@ text-shadow: 0px 2px 2px #999999; -@ } -@ -@ /* The login status message in the top right-hand corner */ -@ div.status { -@ display: table-cell; -@ text-align: right; -@ vertical-align: bottom; -@ color: #333; -@ margin-right: -20px; -@ white-space: nowrap; -@ } -@ -@ /* The main menu bar that appears at the top of the page beneath -@ ** the header */ -@ div.mainmenu { -@ text-align: center; -@ color: white; -@ border-top-left-radius: 5px; -@ border-top-right-radius: 5px; -@ vertical-align: middle; -@ padding-top: 8px; -@ padding-bottom: 8px; -@ background-color: #446979; -@ box-shadow: 0px 3px 4px #333333; -@ } -@ -@ /* The submenu bar that *sometimes* appears below the main menu */ -@ div.submenu { -@ padding-top:10px; -@ padding-bottom:0; -@ text-align: right; -@ color: #000; -@ background-color: #fff; -@ height: 1.5em; -@ vertical-align:middle; -@ box-shadow: 0px 3px 4px #999; -@ } -@ div.mainmenu a, div.mainmenu a:visited { -@ padding: 3px 10px 3px 10px; -@ color: white; -@ text-decoration: none; -@ } -@ div.submenu a, div.submenu a:visited, a.button, -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { -@ padding: 2px 8px; -@ color: #000; -@ font-family: Arial; -@ text-decoration: none; -@ margin:auto; -@ border-radius: 5px; -@ background-color: #e0e0e0; -@ text-shadow: 0px -1px 0px #eee; -@ border: 1px solid #000; -@ } -@ -@ div.mainmenu a:hover { -@ color: #000; -@ background-color: white; -@ } -@ -@ div.submenu a:hover, div.sectionmenu>a.button:hover { -@ background-color: #c0c0c0; -@ } -@ -@ /* All page content from the bottom of the menu or submenu down to -@ ** the footer */ -@ div.content { -@ background-color: #fff; -@ box-shadow: 0px 3px 4px #999; -@ border-bottom-right-radius: 5px; -@ border-bottom-left-radius: 5px; -@ padding-bottom: 1em; -@ min-height:40%; -@ } -@ -@ -@ /* Some pages have section dividers */ -@ div.section { -@ margin-bottom: 0.5em; -@ margin-top: 1em; -@ margin-right: auto; -@ padding: 1px 1px 1px 1px; -@ font-size: 1.2em; -@ font-weight: bold; -@ text-align: center; -@ color: white; -@ border-radius: 5px; -@ background-color: #446979; -@ box-shadow: 0px 3px 4px #333333; -@ white-space: nowrap; -@ } -@ -@ /* The "Date" that occurs on the left hand side of timelines */ -@ div.divider { -@ font-size: 1.2em; -@ font-family: Georgia, serif; -@ font-weight: bold; -@ margin-top: 1em; -@ white-space: nowrap; -@ } -@ -@ /* The footer at the very bottom of the page */ -@ div.footer { -@ font-size: 0.9em; -@ text-align: right; -@ margin-bottom: 1em; -@ color: #666; -@ } -@ -@ /* Hyperlink colors in the footer */ -@ div.footer a { color: white; } -@ div.footer a:link { color: white; } -@ div.footer a:visited { color: white; } -@ div.footer a:hover { background-color: white; color: #558195; } -@ -@ /* <verbatim> blocks */ -@ pre.verbatim, blockquote pre { -@ font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace; -@ background-color: #f3f3f3; -@ padding: 0.5em; -@ white-space: pre-wrap; -@ } -@ -@ blockquote pre { -@ border: 1px #000 dashed; -@ } -@ -@ /* The label/value pairs on (for example) the ci page */ -@ table.label-value th { -@ vertical-align: top; -@ text-align: right; -@ padding: 0.2ex 2ex; -@ } -@ -@ table.report tr th { -@ padding: 3px 5px; -@ text-transform: capitalize; -@ cursor: pointer; -@ } -@ -@ table.report tr td { -@ padding: 3px 5px; -@ cursor: pointer; -@ } -@ -@ textarea { -@ font-size: 1em; -@ } -@ -@ .fullsize-text { -@ font-size: 1.25em; -@ }'); -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> -@ <head> -@ <base href="$baseurl/$current_page" /> -@ <title>$<project_name>: $<title> -@ -@ -@ -@ -@
-@ -@
$</div> -@ <div class="status"><th1> -@ if {[info exists login]} { -@ puts "Logged in as $login" -@ } else { -@ puts "Not logged in" -@ } -@ </th1></div> -@ </div> -@ <div class="mainmenu"> -@ <th1> -@ html "<a href=''$home$index_page''>Home</a>\n" -@ if {[anycap jor]} { -@ html "<a href=''$home/timeline''>Timeline</a>\n" -@ } -@ if {[hascap oh]} { -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" -@ } -@ if {[hascap o]} { -@ html "<a href=''$home/brlist''>Branches</a>\n" -@ html "<a href=''$home/taglist''>Tags</a>\n" -@ } -@ if {[hascap r]} { -@ html "<a href=''$home/reportlist''>Tickets</a>\n" -@ } -@ if {[hascap j]} { -@ html "<a href=''$home/wiki''>Wiki</a>\n" -@ } -@ if {[hascap s]} { -@ html "<a href=''$home/setup''>Admin</a>\n" -@ } elseif {[hascap a]} { -@ html "<a href=''$home/setup_ulist''>Users</a>\n" -@ } -@ if {[info exists login]} { -@ html "<a href=''$home/login''>Logout</a>\n" -@ } else { -@ html "<a href=''$home/login''>Login</a>\n" -@ } -@ </th1></div> -@ <div id="container"> -@ '); -@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div> -@ <div class="footer"> -@ Fossil version $manifest_version $manifest_date -@ </div> -@ </body></html> -@ '); -; - - -/* -** This skin is intended to be almost identical to the default one, with the -** following changes to the header and footer: -** -** 1. The logo image in the header has been modified to be a hyperlink to the -** root of the web site containing the repository using the same scheme -** (i.e. HTTP or HTTPS) as the base URL for the repository. The header -** contains a TH1 script block to help accomplish these tasks. -** -** 2. The Fossil version information in the footer has been augmented with -** hyperlinks to the corresponding points on the timeline in the official -** Fossil repository. Additionally, if the Tcl integration feature is -** enabled, the loaded version of Tcl is included, with a hyperlink to the -** official Tcl/Tk web site. The footer also contains a TH1 script block -** to help accomplish these tasks. -*/ -static const char zBuiltinSkin5[] = -@ REPLACE INTO config(name,mtime,value) -@ VALUES('css',now(),'/* General settings for the entire page */ -@ body { -@ margin: 0ex 1ex; -@ padding: 0px; -@ background-color: white; -@ font-family: sans-serif; -@ } -@ -@ /* The project logo in the upper left-hand corner of each page */ -@ div.logo { -@ display: table-cell; -@ text-align: center; -@ vertical-align: bottom; -@ font-weight: bold; -@ color: #558195; -@ min-width: 200px; -@ white-space: nowrap; -@ } -@ -@ /* The page title centered at the top of each page */ -@ div.title { -@ display: table-cell; -@ font-size: 2em; -@ font-weight: bold; -@ text-align: center; -@ padding: 0 0 0 1em; -@ color: #558195; -@ vertical-align: bottom; -@ width: 100%; -@ } -@ -@ /* The login status message in the top right-hand corner */ -@ div.status { -@ display: table-cell; -@ text-align: right; -@ vertical-align: bottom; -@ color: #558195; -@ font-size: 0.8em; -@ font-weight: bold; -@ min-width: 200px; -@ white-space: nowrap; -@ } -@ -@ /* The header across the top of the page */ -@ div.header { -@ display: table; -@ width: 100%; -@ } -@ -@ /* The main menu bar that appears at the top of the page beneath -@ ** the header */ -@ div.mainmenu { -@ padding: 5px 10px 5px 10px; -@ font-size: 0.9em; -@ font-weight: bold; -@ text-align: center; -@ letter-spacing: 1px; -@ background-color: #558195; -@ border-top-left-radius: 8px; -@ border-top-right-radius: 8px; -@ color: white; -@ } -@ -@ /* The submenu bar that *sometimes* appears below the main menu */ -@ div.submenu, div.sectionmenu { -@ padding: 3px 10px 3px 0px; -@ font-size: 0.9em; -@ text-align: center; -@ background-color: #456878; -@ color: white; -@ } -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { -@ padding: 3px 10px 3px 10px; -@ color: white; -@ text-decoration: none; -@ } -@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { -@ color: #558195; -@ background-color: white; -@ } -@ -@ /* All page content from the bottom of the menu or submenu down to -@ ** the footer */ -@ div.content { -@ padding: 0ex 1ex 1ex 1ex; -@ border: solid #aaa; -@ border-width: 1px; -@ } -@ -@ /* Some pages have section dividers */ -@ div.section { -@ margin-bottom: 0px; -@ margin-top: 1em; -@ padding: 1px 1px 1px 1px; -@ font-size: 1.2em; -@ font-weight: bold; -@ background-color: #558195; -@ color: white; -@ white-space: nowrap; -@ } -@ -@ /* The "Date" that occurs on the left hand side of timelines */ -@ div.divider { -@ background: #a1c4d4; -@ border: 2px #558195 solid; -@ font-size: 1em; font-weight: normal; -@ padding: .25em; -@ margin: .2em 0 .2em 0; -@ float: left; -@ clear: left; -@ white-space: nowrap; -@ } -@ -@ /* The footer at the very bottom of the page */ -@ div.footer { -@ clear: both; -@ font-size: 0.8em; -@ padding: 5px 10px 5px 10px; -@ text-align: right; -@ background-color: #558195; -@ border-bottom-left-radius: 8px; -@ border-bottom-right-radius: 8px; -@ color: white; -@ } -@ -@ /* Hyperlink colors in the footer */ -@ div.footer a { color: white; } -@ div.footer a:link { color: white; } -@ div.footer a:visited { color: white; } -@ div.footer a:hover { background-color: white; color: #558195; } -@ -@ /* verbatim blocks */ -@ pre.verbatim { -@ background-color: #f5f5f5; -@ padding: 0.5em; -@ white-space: pre-wrap; -@ } -@ -@ /* The label/value pairs on (for example) the ci page */ -@ table.label-value th { -@ vertical-align: top; -@ text-align: right; -@ padding: 0.2ex 2ex; -@ }'); -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> -@ <head> -@ <base href="$baseurl/$current_page" /> -@ <title>$<project_name>: $<title> -@ -@ -@ -@ -@
-@ -@
$
$</div> -@ <div class="status"><th1> -@ if {[info exists login]} { -@ puts "Logged in as $login" -@ } else { -@ puts "Not logged in" -@ } -@ </th1></div> -@ </div> -@ <div class="mainmenu"> -@ <th1> -@ html "<a href=''$home$index_page''>Home</a>\n" -@ if {[anycap jor]} { -@ html "<a href=''$home/timeline''>Timeline</a>\n" -@ } -@ if {[hascap oh]} { -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" -@ } -@ if {[hascap o]} { -@ html "<a href=''$home/brlist''>Branches</a>\n" -@ html "<a href=''$home/taglist''>Tags</a>\n" -@ } -@ if {[hascap r]} { -@ html "<a href=''$home/reportlist''>Tickets</a>\n" -@ } -@ if {[hascap j]} { -@ html "<a href=''$home/wiki''>Wiki</a>\n" -@ } -@ if {[hascap s]} { -@ html "<a href=''$home/setup''>Admin</a>\n" -@ } elseif {[hascap a]} { -@ html "<a href=''$home/setup_ulist''>Users</a>\n" -@ } -@ if {[info exists login]} { -@ html "<a href=''$home/login''>Logout</a>\n" -@ } else { -@ html "<a href=''$home/login''>Login</a>\n" -@ } -@ </th1></div> -@ '); -@ REPLACE INTO config(name,mtime,value) -@ VALUES('footer',now(),'<div class="footer"> -@ <th1> -@ proc getTclVersion {} { -@ if {[catch {tclEval info patchlevel} tclVersion] == 0} { -@ return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" -@ } -@ return "" -@ } -@ proc getVersion { version } { -@ set length [string length $version] -@ return [string range $version 1 [expr {$length - 2}]] -@ } -@ set version [getVersion $manifest_version] -@ set tclVersion [getTclVersion] -@ set fossilUrl http://www.fossil-scm.org -@ </th1> -@ This page was generated in about -@ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by -@ <a href="$fossilUrl/">Fossil</a> -@ version $release_version $tclVersion -@ <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> -@ <a href="$fossilUrl/index.html/timeline?c=$manifest_date&y=ci">$manifest_date</a> -@ </div> -@ </body></html> -@ '); -; - /* ** An array of available built-in skins. +** +** To add new built-in skins: +** +** 1. Pick a name for the new skin. (Here we use "xyzzy"). +** +** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt, +** and skins/xyzzy/footer.txt into the source tree. +** +** 3. Rerun "tclsh makemake.tcl" in the src/ folder in order to +** rebuild the makefiles to reference the new CSS, headers, and footers. +** +** 4. Make an entry in the following array for the new skin. */ static struct BuiltinSkin { - const char *zName; - const char *zValue; + const char *zDesc; /* Description of this skin */ + const char *zLabel; /* The directory under skins/ holding this skin */ + char *zSQL; /* Filled in at run-time with SQL to insert this skin */ } aBuiltinSkin[] = { - { "Default", 0 /* Filled in at runtime */ }, - { "Plain Gray, No Logo", zBuiltinSkin1 }, - { "Khaki, No Logo", zBuiltinSkin2 }, - { "Black & White, Menu on Left", zBuiltinSkin3 }, - { "Shadow boxes & Rounded Corners", zBuiltinSkin4 }, - { "Enhanced Default", zBuiltinSkin5 }, + { "Default", "default", 0 }, + { "Plain Gray, No Logo", "plain_gray", 0 }, + { "Khaki, No Logo", "khaki", 0 }, + { "Black & White, Menu on Left", "black_and_white", 0 }, + { "Shadow boxes & Rounded Corners", "rounded1", 0 }, + { "Enhanced Default", "enhanced1", 0 }, + { "San Francisco Modern", "etienne1", 0 }, + { "Eagle", "eagle", 0 }, }; /* ** For a skin named zSkinName, compute the name of the CONFIG table ** entry where that skin is stored and return it. @@ -1286,57 +69,158 @@ } return z; } /* -** Construct and return a string that represents the current skin if -** useDefault==0 or a string for the default skin if useDefault==1. +** Return true if there exists a skin name "zSkinName". +*/ +static int skinExists(const char *zSkinName){ + int i; + if( zSkinName==0 ) return 0; + for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ + if( fossil_strcmp(zSkinName, aBuiltinSkin[i].zDesc)==0 ) return 1; + } + return db_exists("SELECT 1 FROM config WHERE name='skin:%q'", zSkinName); +} + +/* +** Construct and return an string of SQL statements that represents +** a "skin" setting. If zName==0 then return the skin currently +** installed. Otherwise, return one of the built-in skins designated +** by zName. ** ** Memory to hold the returned string is obtained from malloc. */ -static char *getSkin(int useDefault){ +static char *getSkin(const char *zName){ + const char *z; + char *zLabel; + static const char *azType[] = { "css", "header", "footer" }; + int i; Blob val; blob_zero(&val); - blob_appendf(&val, - "REPLACE INTO config(name,value,mtime) VALUES('css',%Q,now());\n", - useDefault ? zDefaultCSS : db_get("css", (char*)zDefaultCSS) - ); - blob_appendf(&val, - "REPLACE INTO config(name,value,mtime) VALUES('header',%Q,now());\n", - useDefault ? zDefaultHeader : db_get("header", (char*)zDefaultHeader) - ); - blob_appendf(&val, - "REPLACE INTO config(name,value,mtime) VALUES('footer',%Q,now());\n", - useDefault ? zDefaultFooter : db_get("footer", (char*)zDefaultFooter) - ); + for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){ + if( zName ){ + zLabel = mprintf("skins/%s/%s.txt", zName, azType[i]); + z = builtin_text(zLabel); + fossil_free(zLabel); + }else{ + z = db_get(azType[i], 0); + if( z==0 ){ + zLabel = mprintf("skins/default/%s.txt", azType[i]); + z = builtin_text(zLabel); + fossil_free(zLabel); + } + } + blob_appendf(&val, + "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now());\n", + azType[i], z + ); + } return blob_str(&val); } /* -** Construct the default skin string and fill in the corresponding -** entry in aBuildinSkin[] +** Respond to a Rename button press. Return TRUE if a dialog was painted. +** Return FALSE to continue with the main Skins page. +*/ +static int skinRename(void){ + const char *zOldName; + const char *zNewName; + int ex = 0; + if( P("rename")==0 ) return 0; + zOldName = P("sn"); + zNewName = P("newname"); + if( zOldName==0 ) return 0; + if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){ + if( zNewName==0 ) zNewName = zOldName; + style_header("Rename A Skin"); + if( ex ){ + @ <p><span class="generalError">There is already another skin + @ named "%h(zNewName)". Choose a different name.</span></p> + } + @ <form action="%s(g.zTop)/setup_skin" method="post"><div> + @ <table border="0"><tr> + @ <tr><td align="right">Current name:<td align="left"><b>%h(zOldName)</b> + @ <tr><td align="right">New name:<td align="left"> + @ <input type="text" size="35" name="newname" value="%h(zNewName)"> + @ <tr><td><td> + @ <input type="hidden" name="sn" value="%h(zOldName)"> + @ <input type="submit" name="rename" value="Rename"> + @ <input type="submit" name="canren" value="Cancel"> + @ </table> + login_insert_csrf_secret(); + @ </div></form> + style_footer(); + return 1; + } + db_multi_exec( + "UPDATE config SET name='skin:%q' WHERE name='skin:%q';", + zNewName, zOldName + ); + return 0; +} + +/* +** Respond to a Save button press. Return TRUE if a dialog was painted. +** Return FALSE to continue with the main Skins page. */ -static void setDefaultSkin(void){ - aBuiltinSkin[0].zValue = getSkin(1); +static int skinSave(const char *zCurrent){ + const char *zNewName; + int ex = 0; + if( P("save")==0 ) return 0; + zNewName = P("svname"); + if( zNewName && zNewName[0]!=0 ){ + } + if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){ + if( zNewName==0 ) zNewName = ""; + style_header("Save Current Skin"); + if( ex ){ + @ <p><span class="generalError">There is already another skin + @ named "%h(zNewName)". Choose a different name.</span></p> + } + @ <form action="%s(g.zTop)/setup_skin" method="post"><div> + @ <table border="0"><tr> + @ <tr><td align="right">Name for this skin:<td align="left"> + @ <input type="text" size="35" name="svname" value="%h(zNewName)"> + @ <tr><td><td> + @ <input type="submit" name="save" value="Save"> + @ <input type="submit" name="cansave" value="Cancel"> + @ </table> + login_insert_csrf_secret(); + @ </div></form> + style_footer(); + return 1; + } + db_multi_exec( + "INSERT OR IGNORE INTO config(name, value, mtime)" + "VALUES('skin:%q',%Q,now())", + zNewName, zCurrent + ); + return 0; } /* ** WEBPAGE: setup_skin */ void setup_skin(void){ const char *z; char *zName; char *zErr = 0; - const char *zCurrent; /* Current skin */ - int i; /* Loop counter */ + const char *zCurrent = 0; /* Current skin */ + int i; /* Loop counter */ Stmt q; + int seenCurrent = 0; login_check_credentials(); if( !g.perm.Setup ){ login_needed(); } db_begin_transaction(); + zCurrent = getSkin(0); + for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ + aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel); + } /* Process requests to delete a user-defined skin */ if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ style_header("Confirm Custom Skin Delete"); @ <form action="%s(g.zTop)/setup_skin" method="post"><div> @@ -1351,51 +235,42 @@ return; } if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ db_multi_exec("DELETE FROM config WHERE name=%Q", zName); } - - setDefaultSkin(); - zCurrent = getSkin(0); + if( skinRename() ) return; + if( skinSave(zCurrent) ) return; - if( P("save")!=0 && (zName = skinVarName(P("save"),0))!=0 ){ - if( db_exists("SELECT 1 FROM config WHERE name=%Q", zName) - || fossil_strcmp(zName, "Default")==0 ){ - zErr = mprintf("Skin name \"%h\" already exists. " - "Choose a different name.", P("sn")); - }else{ - db_multi_exec("INSERT INTO config(name,value,mtime) VALUES(%Q,%Q,now())", - zName, zCurrent - ); - } - } - - /* The user pressed the "Use This Skin" button. */ + /* The user pressed one of the "Install" buttons. */ if( P("load") && (z = P("sn"))!=0 && z[0] ){ int seen = 0; + + /* Check to see if the current skin is already saved. If it is, there + ** is no need to create a backup */ + zCurrent = getSkin(0); for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ - if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ + if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ seen = 1; break; } } if( !seen ){ seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" " AND value=%Q", zCurrent); - } - if( !seen ){ - db_multi_exec( - "INSERT INTO config(name,value,mtime) VALUES(" - " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," - " %Q,now())", zCurrent - ); + if( !seen ){ + db_multi_exec( + "INSERT INTO config(name,value,mtime) VALUES(" + " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," + " %Q,now())", zCurrent + ); + } } seen = 0; for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ - if( fossil_strcmp(aBuiltinSkin[i].zName, z)==0 ){ + if( fossil_strcmp(aBuiltinSkin[i].zDesc, z)==0 ){ seen = 1; - zCurrent = aBuiltinSkin[i].zValue; + zCurrent = aBuiltinSkin[i].zSQL; db_multi_exec("%s", zCurrent/*safe-for-%s*/); break; } } if( !seen ){ @@ -1409,48 +284,59 @@ if( zErr ){ @ <p><font color="red">%h(zErr)</font></p> } @ <p>A "skin" is a combination of @ <a href="setup_editcss">CSS</a>, - @ <a href="setup_header">Header</a>, - @ <a href="setup_footer">Footer</a>, and - @ <a href="setup_logo">Logo</a> that determines the look and feel + @ <a href="setup_header">Header</a>, and + @ <a href="setup_footer">Footer</a> that determines the look and feel @ of the web interface.</p> @ @ <h2>Available Skins:</h2> - @ <ol> + @ <table border="0"> for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ - z = aBuiltinSkin[i].zName; - if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ - @ <li><p>%h(z).   <b>Currently In Use</b></p> + z = aBuiltinSkin[i].zDesc; + @ <tr><td>%d(i+1).<td>%h(z)<td>  <td> + if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ + @ (Currently In Use) + seenCurrent = 1; }else{ - @ <li><form action="%s(g.zTop)/setup_skin" method="post"><div> - @ %h(z).   + @ <form action="%s(g.zTop)/setup_skin" method="post"> @ <input type="hidden" name="sn" value="%h(z)" /> - @ <input type="submit" name="load" value="Use This Skin" /> - @ </div></form></li> + @ <input type="submit" name="load" value="Install" /> + @ </form> } + @ </tr> } db_prepare(&q, "SELECT substr(name, 6), value FROM config" " WHERE name GLOB 'skin:*'" " ORDER BY name" ); while( db_step(&q)==SQLITE_ROW ){ const char *zN = db_column_text(&q, 0); const char *zV = db_column_text(&q, 1); + i++; + @ <tr><td>%d(i).<td>%h(zN)<td>  <td> + @ <form action="%s(g.zTop)/setup_skin" method="post"> if( fossil_strcmp(zV, zCurrent)==0 ){ - @ <li><p>%h(zN).   <b>Currently In Use</b></p> + @ (Currently In Use) + seenCurrent = 1; }else{ - @ <li><form action="%s(g.zTop)/setup_skin" method="post"> - @ %h(zN).   - @ <input type="hidden" name="sn" value="%h(zN)"> - @ <input type="submit" name="load" value="Use This Skin"> - @ <input type="submit" name="del1" value="Delete This Skin"> - @ </form></li> + @ <input type="submit" name="load" value="Install"> + @ <input type="submit" name="del1" value="Delete"> } + @ <input type="submit" name="rename" value="Rename"> + @ <input type="hidden" name="sn" value="%h(zN)"> + @ </form></tr> } db_finalize(&q); - @ </ol> + if( !seenCurrent ){ + i++; + @ <tr><td>%d(i).<td><i>Current Configuration</i><td>  <td> + @ <form action="%s(g.zTop)/setup_skin" method="post"> + @ <input type="submit" name="save" value="Save"> + @ </form> + } + @ </table> style_footer(); db_end_transaction(0); } Index: src/sqlcmd.c ================================================================== --- src/sqlcmd.c +++ src/sqlcmd.c @@ -170,10 +170,12 @@ ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) ** found in check-in X (another BLOB.RID value). ** ** symbolic_name_to_rid(X) Return a the BLOB.RID corresponding to symbolic ** name X. +** +** now() Return the number of seconds since 1970. ** ** REGEXP The REGEXP operator works, unlike in ** standard SQLite. ** ** files_of_checkin The "files_of_check" virtual table is Index: src/sqlite3.c ================================================================== --- src/sqlite3.c +++ src/sqlite3.c @@ -1,8 +1,8 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.8.8. By combining all the individual C code files into this +** version 3.8.8.1. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% or more are commonly seen when SQLite is compiled as a single ** translation unit. @@ -276,13 +276,13 @@ ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.8" +#define SQLITE_VERSION "3.8.8.1" #define SQLITE_VERSION_NUMBER 3008008 -#define SQLITE_SOURCE_ID "2015-01-16 12:08:06 7d68a42face3ab14ed88407d4331872f5b243fdf" +#define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** @@ -19870,10 +19870,21 @@ # define SQLITE_WIN32_VOLATILE #else # define SQLITE_WIN32_VOLATILE volatile #endif +/* +** For some Windows sub-platforms, the _beginthreadex() / _endthreadex() +** functions are not available (e.g. those not using MSVC, Cygwin, etc). +*/ +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ + SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) +# define SQLITE_OS_WIN_THREADS 1 +#else +# define SQLITE_OS_WIN_THREADS 0 +#endif + #endif /* _OS_WIN_H_ */ /************** End of os_win.h **********************************************/ /************** Continuing where we left off in mutex_w32.c ******************/ #endif @@ -22433,11 +22444,11 @@ #endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */ /******************************** End Unix Pthreads *************************/ /********************************* Win32 Threads ****************************/ -#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0 +#if SQLITE_OS_WIN_THREADS #define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ #include <process.h> /* A running thread */ @@ -22526,11 +22537,11 @@ if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult; sqlite3_free(p); return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR; } -#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */ +#endif /* SQLITE_OS_WIN_THREADS */ /******************************** End Win32 Threads *************************/ /********************************* Single-Threaded **************************/ #ifndef SQLITE_THREADS_IMPLEMENTED @@ -67496,10 +67507,45 @@ if( pKeyInfo->db->mallocFailed ) return 1; return 0; } #endif +#if SQLITE_DEBUG +/* +** Count the number of fields (a.k.a. columns) in the record given by +** pKey,nKey. The verify that this count is less than or equal to the +** limit given by pKeyInfo->nField + pKeyInfo->nXField. +** +** If this constraint is not satisfied, it means that the high-speed +** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will +** not work correctly. If this assert() ever fires, it probably means +** that the KeyInfo.nField or KeyInfo.nXField values were computed +** incorrectly. +*/ +static void vdbeAssertFieldCountWithinLimits( + int nKey, const void *pKey, /* The record to verify */ + const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */ +){ + int nField = 0; + u32 szHdr; + u32 idx; + u32 notUsed; + const unsigned char *aKey = (const unsigned char*)pKey; + + if( CORRUPT_DB ) return; + idx = getVarint32(aKey, szHdr); + assert( szHdr<=nKey ); + while( idx<szHdr ){ + idx += getVarint32(aKey+idx, notUsed); + nField++; + } + assert( nField <= pKeyInfo->nField+pKeyInfo->nXField ); +} +#else +# define vdbeAssertFieldCountWithinLimits(A,B,C) +#endif + /* ** Both *pMem1 and *pMem2 contain string values. Compare the two values ** using the collation sequence pColl. As usual, return a negative , zero ** or positive value if *pMem1 is less than, equal to or greater than ** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);". @@ -67907,10 +67953,11 @@ u32 y; u64 x; i64 v = pPKey2->aMem[0].u.i; i64 lhs; + vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB ); switch( serial_type ){ case 1: { /* 1-byte signed integer */ lhs = ONE_BYTE_INT(aKey); testcase( lhs<0 ); @@ -67994,10 +68041,11 @@ ){ const u8 *aKey1 = (const u8*)pKey1; int serial_type; int res; + vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); getVarint32(&aKey1[1], serial_type); if( serial_type<12 ){ res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ }else if( !(serial_type & 0x01) ){ res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ @@ -105795,11 +105843,13 @@ if( pParse->db->mallocFailed ) return; pOp->p2 = nKey + nData; pKI = pOp->p4.pKeyInfo; memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */ sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO); - pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, 1); + testcase( pKI->nXField>2 ); + pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, + pKI->nXField-1); addrJmp = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); pSort->labelBkOut = sqlite3VdbeMakeLabel(v); pSort->regReturn = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); @@ -106306,11 +106356,11 @@ struct ExprList_item *pItem; sqlite3 *db = pParse->db; int i; nExpr = pList->nExpr; - pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra-iStart, 1); + pInfo = sqlite3KeyInfoAlloc(db, nExpr-iStart, nExtra+1); if( pInfo ){ assert( sqlite3KeyInfoIsWriteable(pInfo) ); for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){ CollSeq *pColl; pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr); @@ -110176,11 +110226,11 @@ ** we figure out that the sorting index is not needed. The addrSortIndex ** variable is used to facilitate that change. */ if( sSort.pOrderBy ){ KeyInfo *pKeyInfo; - pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, 0); + pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr); sSort.iECursor = pParse->nTab++; sSort.addrSortIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, sSort.iECursor, sSort.pOrderBy->nExpr+1+pEList->nExpr, 0, (char*)pKeyInfo, P4_KEYINFO @@ -110350,11 +110400,11 @@ ** implement it. Allocate that sorting index now. If it turns out ** that we do not need it after all, the OP_SorterOpen instruction ** will be converted into a Noop. */ sAggInfo.sortingIdx = pParse->nTab++; - pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, 0); + pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn); addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, sAggInfo.sortingIdx, sAggInfo.nSortingColumn, 0, (char*)pKeyInfo, P4_KEYINFO); /* Initialize memory locations used by GROUP BY aggregate processing Index: src/sqlite3.h ================================================================== --- src/sqlite3.h +++ src/sqlite3.h @@ -105,13 +105,13 @@ ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.8" +#define SQLITE_VERSION "3.8.8.1" #define SQLITE_VERSION_NUMBER 3008008 -#define SQLITE_SOURCE_ID "2015-01-16 12:08:06 7d68a42face3ab14ed88407d4331872f5b243fdf" +#define SQLITE_SOURCE_ID "2015-01-20 16:51:25 f73337e3e289915a76ca96e7a05a1a8d4e890d55" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** Index: src/stat.c ================================================================== --- src/stat.c +++ src/stat.c @@ -55,10 +55,11 @@ login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } brief = P("brief")!=0; style_header("Repository Statistics"); + style_adunit_config(ADUNIT_RIGHT_OK); if( g.perm.Admin ){ style_submenu_element("URLs", "URLs and Checkouts", "urllist"); style_submenu_element("Schema", "Repository Schema", "repo_schema"); style_submenu_element("Web-Cache", "Web-Cache Stats", "cachestat"); } @@ -290,10 +291,11 @@ int cnt; login_check_credentials(); if( !g.perm.Admin ){ login_needed(); return; } style_header("URLs and Checkouts"); + style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Stat", "Repository Stats", "stat"); style_submenu_element("Schema", "Repository Schema", "repo_schema"); @ <div class="section">URLs</div> @ <table border="0" width='100%%'> db_prepare(&q, "SELECT substr(name,9), datetime(mtime,'unixepoch')" @@ -336,10 +338,11 @@ Stmt q; login_check_credentials(); if( !g.perm.Admin ){ login_needed(); return; } style_header("Repository Schema"); + style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Stat", "Repository Stats", "stat"); style_submenu_element("URLs", "URLs and Checkouts", "urllist"); db_prepare(&q, "SELECT sql FROM %s.sqlite_master WHERE sql IS NOT NULL", db_name("repository")); @ <pre> Index: src/style.c ================================================================== --- src/style.c +++ src/style.c @@ -46,10 +46,15 @@ /* ** remember, if a sidebox was used */ static int sideboxUsed = 0; +/* +** Ad-unit styles. +*/ +static unsigned adUnitFlags = 0; + /* ** List of hyperlinks and forms that need to be resolved by javascript in ** the footer. */ @@ -284,11 +289,12 @@ ** Draw the header. */ void style_header(const char *zTitleFormat, ...){ va_list ap; char *zTitle; - const char *zHeader = db_get("header", (char*)zDefaultHeader); + const char *zHeader = db_get("header", 0); + if( zHeader==0 ) zHeader = builtin_text("skins/default/header.txt"); login_check_credentials(); va_start(ap, zTitleFormat); zTitle = vmprintf(zTitleFormat, ap); va_end(ap); @@ -344,33 +350,63 @@ @ if(!e) throw new Error("Expecting element with ID "+x); @ else return e;} @ </script> } +#if INTERFACE +/* Allowed parameters for style_adunit() */ +#define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ +#define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */ +#endif + /* -** Append ad unit text if appropriate. +** Various page implementations can invoke this interface to let the +** style manager know what kinds of ads are appropriate for this page. +*/ +void style_adunit_config(unsigned int mFlags){ + adUnitFlags = mFlags; +} + +/* +** Return the text of an ad-unit, if one should be rendered. Return +** NULL if no ad-unit is desired. +** +** The *pAdFlag value might be set to ADUNIT_RIGHT_OK if this is +** a right-hand vertical ad. */ -static void style_ad_unit(void){ - const char *zAd; +static const char *style_adunit_text(unsigned int *pAdFlag){ + const char *zAd = 0; + *pAdFlag = 0; + if( adUnitFlags & ADUNIT_OFF ) return 0; /* Disallow ads on this page */ if( g.perm.Admin && db_get_boolean("adunit-omit-if-admin",0) ){ - return; + return 0; } if( !login_is_nobody() && fossil_strcmp(g.zLogin,"anonymous")!=0 && db_get_boolean("adunit-omit-if-user",0) ){ - return; + return 0; } - zAd = db_get("adunit", 0); - if( zAd ) cgi_append_content(zAd, -1); + if( (adUnitFlags & ADUNIT_RIGHT_OK)!=0 + && !fossil_all_whitespace(zAd = db_get("adunit-right", 0)) + && !cgi_body_contains("<table") + ){ + *pAdFlag = ADUNIT_RIGHT_OK; + return zAd; + }else if( !fossil_all_whitespace(zAd = db_get("adunit",0)) ){ + return zAd; + } + return 0; } /* ** Draw the footer at the bottom of the page. */ void style_footer(void){ const char *zFooter; + const char *zAd = 0; + unsigned int mAdFlags = 0; if( !headerHasBeenGenerated ) return; /* Go back and put the submenu at the top of the page. We delay the ** creation of the submenu until the end so that we can add elements @@ -389,12 +425,25 @@ @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a> } } @ </div> } - style_ad_unit(); - @ <div class="content"> + + zAd = style_adunit_text(&mAdFlags); + if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){ + @ <div class="content adunit_right_container"> + @ <div class="adunit_right"> + cgi_append_content(zAd, -1); + @ </div> + }else{ + if( zAd ){ + @ <div class="adunit_banner"> + cgi_append_content(zAd, -1); + @ </div> + } + @ <div class="content"> + } cgi_destination(CGI_BODY); if( sideboxUsed ){ /* Put the footer at the bottom of the page. ** the additional clear/both is needed to extend the content @@ -406,11 +455,12 @@ /* Set the href= field on hyperlinks. Do this before the footer since ** the footer will be generating </html> */ style_resolve_href(); - zFooter = db_get("footer", (char*)zDefaultFooter); + zFooter = db_get("footer", 0); + if( zFooter==0 ) zFooter = builtin_text("skins/default/footer.txt"); if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1); Th_Render(zFooter); if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1); /* Render trace log if TH1 tracing is enabled. */ @@ -436,230 +486,10 @@ */ void style_sidebox_end(void){ @ </div> } -/* @-comment: // */ -/* -** The default page header. -*/ -const char zDefaultHeader[] = -@ <html> -@ <head> -@ <base href="$baseurl/$current_page" /> -@ <title>$<project_name>: $<title> -@ -@ -@ -@ -@
-@ -@
$
$</div> -@ <div class="status"><th1> -@ if {[info exists login]} { -@ puts "Logged in as $login" -@ } else { -@ puts "Not logged in" -@ } -@ </th1></div> -@ </div> -@ <div class="mainmenu"> -@ <th1> -@ html "<a href='$home$index_page'>Home</a>\n" -@ if {[anycap jor]} { -@ html "<a href='$home/timeline'>Timeline</a>\n" -@ } -@ if {[hascap oh]} { -@ html "<a href='$home/tree?ci=tip'>Files</a>\n" -@ } -@ if {[hascap o]} { -@ html "<a href='$home/brlist'>Branches</a>\n" -@ html "<a href='$home/taglist'>Tags</a>\n" -@ } -@ if {[hascap r]} { -@ html "<a href='$home/reportlist'>Tickets</a>\n" -@ } -@ if {[hascap j]} { -@ html "<a href='$home/wiki'>Wiki</a>\n" -@ } -@ if {[hascap s]} { -@ html "<a href='$home/setup'>Admin</a>\n" -@ } elseif {[hascap a]} { -@ html "<a href='$home/setup_ulist'>Users</a>\n" -@ } -@ if {[info exists login]} { -@ html "<a href='$home/login'>Logout</a>\n" -@ } else { -@ html "<a href='$home/login'>Login</a>\n" -@ } -@ </th1></div> -; - -/* -** The default page footer -*/ -const char zDefaultFooter[] = -@ <div class="footer"> -@ This page was generated in about -@ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by -@ Fossil version $manifest_version $manifest_date -@ </div> -@ </body></html> -; - -/* -** The default Cascading Style Sheet. -** It's assembled by different strings for each class. -** The default css contains all definitions. -** The style sheet, send to the client only contains the ones, -** not defined in the user defined css. -*/ -const char zDefaultCSS[] = -@ /* General settings for the entire page */ -@ body { -@ margin: 0ex 1ex; -@ padding: 0px; -@ background-color: white; -@ font-family: sans-serif; -@ } -@ -@ /* The project logo in the upper left-hand corner of each page */ -@ div.logo { -@ display: table-cell; -@ text-align: center; -@ vertical-align: bottom; -@ font-weight: bold; -@ color: #558195; -@ min-width: 200px; -@ white-space: nowrap; -@ } -@ -@ /* The page title centered at the top of each page */ -@ div.title { -@ display: table-cell; -@ font-size: 2em; -@ font-weight: bold; -@ text-align: center; -@ padding: 0 0 0 1em; -@ color: #558195; -@ vertical-align: bottom; -@ width: 100%; -@ } -@ -@ /* The login status message in the top right-hand corner */ -@ div.status { -@ display: table-cell; -@ text-align: right; -@ vertical-align: bottom; -@ color: #558195; -@ font-size: 0.8em; -@ font-weight: bold; -@ min-width: 200px; -@ white-space: nowrap; -@ } -@ -@ /* The header across the top of the page */ -@ div.header { -@ display: table; -@ width: 100%; -@ } -@ -@ /* The main menu bar that appears at the top of the page beneath -@ ** the header */ -@ div.mainmenu { -@ padding: 5px 10px 5px 10px; -@ font-size: 0.9em; -@ font-weight: bold; -@ text-align: center; -@ letter-spacing: 1px; -@ background-color: #558195; -@ border-top-left-radius: 8px; -@ border-top-right-radius: 8px; -@ color: white; -@ } -@ -@ /* The submenu bar that *sometimes* appears below the main menu */ -@ div.submenu, div.sectionmenu { -@ padding: 3px 10px 3px 0px; -@ font-size: 0.9em; -@ text-align: center; -@ background-color: #456878; -@ color: white; -@ } -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { -@ padding: 3px 10px 3px 10px; -@ color: white; -@ text-decoration: none; -@ } -@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { -@ color: #558195; -@ background-color: white; -@ } -@ -@ /* All page content from the bottom of the menu or submenu down to -@ ** the footer */ -@ div.content { -@ padding: 0ex 1ex 1ex 1ex; -@ border: solid #aaa; -@ border-width: 1px; -@ } -@ -@ /* Some pages have section dividers */ -@ div.section { -@ margin-bottom: 0px; -@ margin-top: 1em; -@ padding: 1px 1px 1px 1px; -@ font-size: 1.2em; -@ font-weight: bold; -@ background-color: #558195; -@ color: white; -@ white-space: nowrap; -@ } -@ -@ /* The "Date" that occurs on the left hand side of timelines */ -@ div.divider { -@ background: #a1c4d4; -@ border: 2px #558195 solid; -@ font-size: 1em; font-weight: normal; -@ padding: .25em; -@ margin: .2em 0 .2em 0; -@ float: left; -@ clear: left; -@ white-space: nowrap; -@ } -@ -@ /* The footer at the very bottom of the page */ -@ div.footer { -@ clear: both; -@ font-size: 0.8em; -@ padding: 5px 10px 5px 10px; -@ text-align: right; -@ background-color: #558195; -@ border-bottom-left-radius: 8px; -@ border-bottom-right-radius: 8px; -@ color: white; -@ } -@ -@ /* Hyperlink colors in the footer */ -@ div.footer a { color: white; } -@ div.footer a:link { color: white; } -@ div.footer a:visited { color: white; } -@ div.footer a:hover { background-color: white; color: #558195; } -@ -@ /* verbatim blocks */ -@ pre.verbatim { -@ background-color: #f5f5f5; -@ padding: 0.5em; -@ white-space: pre-wrap; -@} -; - /* The following table contains bits of default CSS that must ** be included if they are not found in the application-defined ** CSS. */ @@ -666,14 +496,10 @@ const struct strctCssDefaults { const char *elementClass; /* Name of element needed */ const char *comment; /* Comment text */ const char *value; /* CSS text */ } cssDefaultList[] = { - { "", - "", - zDefaultCSS - }, { "div.sidebox", "The nomenclature sidebox for branches,..", @ float: right; @ background-color: white; @ border-width: medium; @@ -1279,10 +1105,11 @@ @ text-align: left; @ padding: 0px 1em 0.5ex 0px; }, { ".brlist table td", "Branch list table headers", @ padding: 0px 2em 0px 0px; + @ white-space: nowrap; }, { "th.sort:after", "General styles for sortable column marker", @ margin-left: .4em; @ cursor: pointer; @@ -1310,19 +1137,16 @@ ** Append all of the default CSS to the CGI output. */ void cgi_append_default_css(void) { int i; + cgi_printf("%s", builtin_text("skins/default/css.txt")); for( i=0; cssDefaultList[i].elementClass; i++ ){ if( cssDefaultList[i].elementClass[0] ){ cgi_printf("/* %s */\n%s {\n%s\n}\n\n", cssDefaultList[i].comment, cssDefaultList[i].elementClass, - cssDefaultList[i].value - ); - }else{ - cgi_printf("%s", cssDefaultList[i].value ); } } } @@ -1333,11 +1157,11 @@ void page_style_css(void){ Blob css; int i; cgi_set_content_type("text/css"); - blob_init(&css, db_get("css",(char*)zDefaultCSS), -1); + blob_init(&css, db_get("css",(char*)builtin_text("skins/default/css.txt")), -1); /* add special missing definitions */ for(i=1; cssDefaultList[i].elementClass; i++){ if( strstr(blob_str(&css), cssDefaultList[i].elementClass)==0 ){ blob_appendf(&css, "/* %s */\n%s {\n%s}\n", Index: src/tag.c ================================================================== --- src/tag.c +++ src/tag.c @@ -535,11 +535,11 @@ tag_cmd_usage: usage("add|cancel|find|list ..."); } /* -** WEBPAGE: /taglist +** WEBPAGE: taglist */ void taglist_page(void){ Stmt q; login_check_credentials(); @@ -546,10 +546,11 @@ if( !g.perm.Read ){ login_needed(); } login_anonymous_available(); style_header("Tags"); + style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Timeline", "Timeline", "tagtimeline"); @ <h2>Non-propagating tags:</h2> db_prepare(&q, "SELECT substr(tagname,5)" " FROM tag" Index: src/th.h ================================================================== --- src/th.h +++ src/th.h @@ -1,8 +1,8 @@ /* This header file defines the external interface to the custom Scripting -** Language (TH) interpreter. TH is very similar to TCL but is not an +** Language (TH) interpreter. TH is very similar to Tcl but is not an ** exact clone. */ /* ** Before creating an interpreter, the application must allocate and Index: src/translate.c ================================================================== --- src/translate.c +++ src/translate.c @@ -42,11 +42,11 @@ ** ** Enhancement #2: ** ** Comments of the form: "/* @-comment: CC" cause CC to become a ** comment character for the @-substitution. Typical values for CC are -** "--" (for SQL text) or "#" (for TCL script) or "//" (for C++ code). +** "--" (for SQL text) or "#" (for Tcl script) or "//" (for C++ code). ** Lines of subsequent @-blocks that begin with CC are omitted from the ** output. ** */ #include <stdio.h> Index: src/url.c ================================================================== --- src/url.c +++ src/url.c @@ -362,10 +362,11 @@ ** feature. */ void url_proxy_options(void){ zProxyOpt = find_option("proxy", 0, 1); if( find_option("nosync",0,0) ) g.fNoSync = 1; + if( find_option("ipv4",0,0) ) g.fIPv4 = 1; } /* ** If the "proxy" setting is defined, then change the URL settings ** (initialized by a prior call to url_parse()) so that the HTTP Index: src/user.c ================================================================== --- src/user.c +++ src/user.c @@ -467,13 +467,13 @@ style_submenu_element("Newer", "Newer entries", "%s/access_log?o=%d&n=%d&y=%d", g.zTop, skip>=n ? skip-n : 0, n, y); } rc = db_prepare_ignore_error(&q, "%s", blob_sql_text(&sql)); - @ <center><table border="1" cellpadding="5"> - @ <tr><th width="33%%">Date</th><th width="34%%">User</th> - @ <th width="33%%">IP Address</th></tr> + @ <center><table border="1" cellpadding="5" id='logtable'> + @ <thead><tr><th width="33%%">Date</th><th width="34%%">User</th> + @ <th width="33%%">IP Address</th></tr></thead><tbody> while( rc==SQLITE_OK && db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zIP = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); int bSuccess = db_column_int(&q, 3); @@ -492,11 +492,11 @@ } if( skip>0 || cnt>n ){ style_submenu_element("All", "All entries", "%s/access_log?n=10000000", g.zTop); } - @ </table></center> + @ </tbody></table></center> db_finalize(&q); @ <hr> @ <form method="post" action="%s(g.zTop)/access_log"> @ <label><input type="checkbox" name="delold"> @ Delete all but the most recent 200 entries</input></label> @@ -515,7 +515,8 @@ @ <form method="post" action="%s(g.zTop)/access_log"> @ <label><input type="checkbox" name="delall"> @ Delete all entries</input></label> @ <input type="submit" name="delallbtn" value="Delete"></input> @ </form> + output_table_sorting_javascript("logtable", "Ttt", 1); style_footer(); } Index: src/util.c ================================================================== --- src/util.c +++ src/util.c @@ -331,5 +331,15 @@ int fossil_is_uuid(const char *zSym){ return zSym && (UUID_SIZE==strlen(zSym)) && validate16(zSym, UUID_SIZE); } + +/* +** Return true if the input string is NULL or all whitespace. +** Return false if the input string contains text. +*/ +int fossil_all_whitespace(const char *z){ + if( z==0 ) return 1; + while( fossil_isspace(z[0]) ){ z++; } + return z[0]==0; +} Index: src/xfer.c ================================================================== --- src/xfer.c +++ src/xfer.c @@ -1966,12 +1966,12 @@ g.clockSkewSeen = 1; } fossil_force_newline(); fossil_print( - "%s finished with %lld bytes sent, %lld bytes received\n", - zOpType, nSent, nRcvd); + "%s done, sent: %lld received: %lld ip: %s\n", + zOpType, nSent, nRcvd, g.zIpAddr); 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); Index: win/Makefile.mingw ================================================================== --- win/Makefile.mingw +++ win/Makefile.mingw @@ -146,12 +146,13 @@ #### 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. # -OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1k/include -OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1k +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l +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 @@ -483,10 +484,34 @@ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ + $(SRCDIR)/../skins/black_and_white/css.txt \ + $(SRCDIR)/../skins/black_and_white/footer.txt \ + $(SRCDIR)/../skins/black_and_white/header.txt \ + $(SRCDIR)/../skins/default/css.txt \ + $(SRCDIR)/../skins/default/footer.txt \ + $(SRCDIR)/../skins/default/header.txt \ + $(SRCDIR)/../skins/eagle/css.txt \ + $(SRCDIR)/../skins/eagle/footer.txt \ + $(SRCDIR)/../skins/eagle/header.txt \ + $(SRCDIR)/../skins/enhanced1/css.txt \ + $(SRCDIR)/../skins/enhanced1/footer.txt \ + $(SRCDIR)/../skins/enhanced1/header.txt \ + $(SRCDIR)/../skins/etienne1/css.txt \ + $(SRCDIR)/../skins/etienne1/footer.txt \ + $(SRCDIR)/../skins/etienne1/header.txt \ + $(SRCDIR)/../skins/khaki/css.txt \ + $(SRCDIR)/../skins/khaki/footer.txt \ + $(SRCDIR)/../skins/khaki/header.txt \ + $(SRCDIR)/../skins/plain_gray/css.txt \ + $(SRCDIR)/../skins/plain_gray/footer.txt \ + $(SRCDIR)/../skins/plain_gray/header.txt \ + $(SRCDIR)/../skins/rounded1/css.txt \ + $(SRCDIR)/../skins/rounded1/footer.txt \ + $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/diff.tcl TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ Index: win/Makefile.mingw.mistachkin ================================================================== --- win/Makefile.mingw.mistachkin +++ win/Makefile.mingw.mistachkin @@ -146,12 +146,13 @@ #### 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. # -OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1k/include -OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1k +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.1l +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 @@ -483,10 +484,31 @@ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ + $(SRCDIR)/../skins/black_and_white/css.txt \ + $(SRCDIR)/../skins/black_and_white/footer.txt \ + $(SRCDIR)/../skins/black_and_white/header.txt \ + $(SRCDIR)/../skins/default/css.txt \ + $(SRCDIR)/../skins/default/footer.txt \ + $(SRCDIR)/../skins/default/header.txt \ + $(SRCDIR)/../skins/eagle/css.txt \ + $(SRCDIR)/../skins/eagle/footer.txt \ + $(SRCDIR)/../skins/eagle/header.txt \ + $(SRCDIR)/../skins/enhanced1/css.txt \ + $(SRCDIR)/../skins/enhanced1/footer.txt \ + $(SRCDIR)/../skins/enhanced1/header.txt \ + $(SRCDIR)/../skins/khaki/css.txt \ + $(SRCDIR)/../skins/khaki/footer.txt \ + $(SRCDIR)/../skins/khaki/header.txt \ + $(SRCDIR)/../skins/plain_gray/css.txt \ + $(SRCDIR)/../skins/plain_gray/footer.txt \ + $(SRCDIR)/../skins/plain_gray/header.txt \ + $(SRCDIR)/../skins/rounded1/css.txt \ + $(SRCDIR)/../skins/rounded1/footer.txt \ + $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/diff.tcl TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ Index: win/Makefile.msc ================================================================== --- win/Makefile.msc +++ win/Makefile.msc @@ -55,11 +55,11 @@ # Uncomment to enable Tcl support # FOSSIL_ENABLE_TCL = 1 !ifdef FOSSIL_ENABLE_SSL -SSLDIR = $(B)\compat\openssl-1.0.1k +SSLDIR = $(B)\compat\openssl-1.0.1l SSLINCDIR = $(SSLDIR)\inc32 SSLLIBDIR = $(SSLDIR)\out32 SSLLFLAGS = /nologo /opt:ref /debug SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" @@ -322,11 +322,35 @@ wysiwyg_.c \ xfer_.c \ xfersetup_.c \ zip_.c -EXTRA_FILES = $(SRCDIR)\diff.tcl +EXTRA_FILES = $(SRCDIR)\../skins/black_and_white/css.txt \ + $(SRCDIR)\../skins/black_and_white/footer.txt \ + $(SRCDIR)\../skins/black_and_white/header.txt \ + $(SRCDIR)\../skins/default/css.txt \ + $(SRCDIR)\../skins/default/footer.txt \ + $(SRCDIR)\../skins/default/header.txt \ + $(SRCDIR)\../skins/eagle/css.txt \ + $(SRCDIR)\../skins/eagle/footer.txt \ + $(SRCDIR)\../skins/eagle/header.txt \ + $(SRCDIR)\../skins/enhanced1/css.txt \ + $(SRCDIR)\../skins/enhanced1/footer.txt \ + $(SRCDIR)\../skins/enhanced1/header.txt \ + $(SRCDIR)\../skins/etienne1/css.txt \ + $(SRCDIR)\../skins/etienne1/footer.txt \ + $(SRCDIR)\../skins/etienne1/header.txt \ + $(SRCDIR)\../skins/khaki/css.txt \ + $(SRCDIR)\../skins/khaki/footer.txt \ + $(SRCDIR)\../skins/khaki/header.txt \ + $(SRCDIR)\../skins/plain_gray/css.txt \ + $(SRCDIR)\../skins/plain_gray/footer.txt \ + $(SRCDIR)\../skins/plain_gray/header.txt \ + $(SRCDIR)\../skins/rounded1/css.txt \ + $(SRCDIR)\../skins/rounded1/footer.txt \ + $(SRCDIR)\../skins/rounded1/header.txt \ + $(SRCDIR)\diff.tcl OBJ = $(OX)\add$O \ $(OX)\allrepo$O \ $(OX)\attach$O \ $(OX)\bag$O \ Index: win/include/unistd.h ================================================================== --- win/include/unistd.h +++ win/include/unistd.h @@ -1,11 +1,11 @@ #ifndef _UNISTD_H #define _UNISTD_H 1 -/* This file intended to serve as a drop-in replacement for +/* This file intended to serve as a drop-in replacement for * unistd.h on Windows - * Please add functionality as neeeded + * Please add functionality as neeeded */ #include <stdlib.h> #include <io.h> #define srandom srand Index: www/build.wiki ================================================================== --- www/build.wiki +++ www/build.wiki @@ -122,11 +122,11 @@ 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.1k</b>"), then make sure that some recent +"<b>compat/openssl-1.0.1l</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>