shithub: werc

Download patch

ref: 9b78e9834bc0de583eb1256fe0da5be3944308c2
parent: b1cda506f6a4614abd995a304a92ad795b5183b9
parent: 0ddf86b7e75eca5659e9a62a8cdc10697ca29dcd
author: sl <[email protected]>
date: Thu Jul 9 22:53:52 EDT 2009

Merge.

--- a/.hgtags
+++ b/.hgtags
@@ -33,3 +33,9 @@
 0164a4b8c410abd369bd7e42a9a4f70c2e21b97b LATEST-RELEASE
 dd1b3d521ca78bb3a891c9161bb5f82289aaa98b 1.0.0-rc2
 b45ddd3941d923c32b4cbdb96c298323e19ff053 LATEST-RELEASE
+78e83db451a1f429b0834b3067a25a20a164f43d 1.0.0-rc3
+b45ddd3941d923c32b4cbdb96c298323e19ff053 LATEST-RELEASE
+f49efe26338443144579776394fb3bf2227123b0 LATEST-RELEASE
+f0c69c8496c4d07d42888035259ed2ed47343618 1.0.0-rc4
+f49efe26338443144579776394fb3bf2227123b0 LATEST-RELEASE
+ab607f1e4a35fb94f87fd70c84d7d656bc2c1cc8 LATEST-RELEASE
--- a/apps/blagh/app.rc
+++ b/apps/blagh/app.rc
@@ -4,8 +4,13 @@
     if(~ $#blagh_dirs 0)
         blagh_dirs=( . )
     conf_enable_app blagh
+
+    if(~ $"conf_blog_editors '')
+        conf_blog_editors=blog-editors
+
+    if(~ $"conf_max_posts_per_page '')
+        conf_max_posts_per_page=32
 }
-conf_blog_editors=blog-editors
 
 fn blagh_init {
     if(~ $#blagh_dirs 0 && ~ $req_path */[bB]log/*) {
@@ -67,9 +72,11 @@
 
     echo '<div style="text-align:right">(<a href="index.rss">RSS Feed</a>|<a href="index.atom">Atom Feed</a>)</div>'
 
+    { # XXX Not sure why this fixes issues with blog setup, probably bug in fltr_cache!
     for(p in `{get_post_list $blagh_root^$blagh_dirs}) {
         l=`{echo -n $p|sed 's!'$sitedir^'/?(.*)([0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9])(/[^/]+/)!\2 /\1\2\3!'}
         sed '1s!.*![&]('^$l(2)^') ('^$l(1)^')!' < $p/index.md 
+    }
     } | $formatter 
 }
 
@@ -76,7 +83,7 @@
 fn get_post_list {
     # /./->/|/ done to sort -t| and order by date
     # Note: $paths in blagh_dirs should not contain '/./' or '|'
-    ls -F $*^/./[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]/ >[2]/dev/null | sed -n '/'^$forbidden_uri_chars^'/d; s,/\./,/|/,; /\/$/p' | sort -r '-t|' +1 | sed 's,/+\|/+,/,'
+    ls -F $*^/./[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]/ >[2]/dev/null | sed -n '/'^$forbidden_uri_chars^'/d; s,/\./,/|/,; /\/$/p' | sort -r '-t|' +1 | sed -e 's,/+\|/+,/,' -e $conf_max_posts_per_page^'q'
 }
 
 fn mkbpost {
@@ -113,4 +120,8 @@
         }> $ddir/$"n^$"bpid/index.md 
     }
     status=$_status
+}
+
+fn strip_title_from_md_file {
+    sed '1N; /^.*\n===*$/N; /.*\n===*\n$/d'
 }
--- a/apps/blagh/atom.tpl
+++ b/apps/blagh/atom.tpl
@@ -14,7 +14,7 @@
     #mdate=`{/bin/date -Rd `{mtime $f|awk '{print $1}' }} # Not used because it is unreliable
     by=$stat(2)
     #ifs=() { summary=`{cat $f/index.md | crop_text 1024 ... | $formatter } }
-    ifs=() { summary=`{cat $f/index.md | ifs=$difs {$formatter} } }
+    ifs=() { summary=`{cat $f/index.md | strip_title_from_md_file | ifs=$difs {$formatter} } }
 }
 updated = `{/bin/date --rfc-3339'=seconds' |sed 's/ /T/'} 
 %}
--- a/apps/blagh/rss20.tpl
+++ b/apps/blagh/rss20.tpl
@@ -13,7 +13,7 @@
     #mdate=`{/bin/date -Rd $stat(1)} # Not used because it is unreliable
     post_uri=$base_url^`{cleanname `{echo $f | sed -e 's!^'$sitedir'!!'}}^'/'
     by=$stat(2)
-    ifs=() {summary=`{ cat $f/index.md | ifs=$difs {$formatter | escape_html} }}
+    ifs=() {summary=`{ cat $f/index.md |strip_title_from_md_file| ifs=$difs {$formatter | escape_html} }}
 }
 
 %}
--- a/apps/bridge/app.rc
+++ b/apps/bridge/app.rc
@@ -73,7 +73,7 @@
     if not if(check_user $groups_allowed_comments)
         u=$logged_user
     if not
-        _status='You are not a memeber of a group allowed to comment.'
+        _status='You are not a member of a group allowed to comment.'
 
     if(~ $#_status 0) {
         umask 002
--- a/apps/bridge/foot.tpl
+++ b/apps/bridge/foot.tpl
@@ -19,7 +19,7 @@
         <input type="password" name="comment_passwd2" value="" />
     </label>
     <div style="font-size: 70%">
-    Enter your desired user name/password and after your comment has been reviewed by an addmin it will be posted and your account will be enabled. If you are already registered please <a href="/_users/login">login</a> before posting.
+    Enter your desired user name/password and after your comment has been reviewed by an admin it will be posted and your account will be enabled. If you are already registered please <a href="/_users/login">login</a> before posting.
     </div>
 % }
 </form>
--- a/bin/corehandlers.rc
+++ b/bin/corehandlers.rc
@@ -47,7 +47,7 @@
     echo '</ul>'
 }
 
-fn md_handler { $formatter < $1 }
+fn md_handler { $formatter $1 }
 
 fn tpl_handler { template $* }
 
@@ -92,8 +92,8 @@
     if not if(test -f $local_path.html)
         handler_body_main=(html_handler $local_path.html)
     # Global tpl (eg sitemap.tpl), should take precedence over txt handler!
-    if not if(test -f lib^$req_path^.tpl)
-        handler_body_main=(tpl_handler lib^$req_path^.tpl)
+    if not if(test -f tpl^$req_path^.tpl)
+        handler_body_main=(tpl_handler tpl^$req_path^.tpl)
     if not if(test -f $local_path.txt)
         handler_body_main=(txt_handler $local_path.txt)
 
--- a/bin/fltr_cache.rc
+++ b/bin/fltr_cache.rc
@@ -19,8 +19,10 @@
             a=$f
             f=/dev/null
         }
-        if not
+        if not {
             score=`{sha1sum $f || exit 1}
+            score=$score(1)
+        }
     }
     cachedir=/tmp/fltr_cache/$score
     mkdir -p $cachedir >[2]/dev/null
--- a/bin/werc.rc
+++ b/bin/werc.rc
@@ -41,7 +41,9 @@
     current_date_time=`{date}
 
     # Note: $REQUEST_URI is not officially in CGI 1.1, but seems to be de-facto
-    req_path=`{echo -n $REQUEST_URI | sed 's/\?.*//; s!//+!/!g; s/'^$forbidden_uri_chars^'//g; s/\.\.*/./g; 1q'}
+    # Note: We only urldecode %5F->'_' because some sites (stackoverflow.com?) urlencode it in their links,
+    # perhaps we should completel urldecode the whole url.
+    req_path=`{echo -n $REQUEST_URI | sed 's/\?.*//; s!//+!/!g; s/%5[Ff]/_/g; s/'^$forbidden_uri_chars^'//g; s/\.\.*/./g; 1q'}
     req_url=$base_url^$req_path
     local_path=$sitedir$req_path
     ifs='/' { args=`{echo -n $req_path} }
--- /dev/null
+++ b/bin/werc_errlog_wrap.rc
@@ -1,0 +1,5 @@
+#!/usr/local/plan9/bin/rc
+
+# This is a wrapper script for broken http servers like recent lighttpd versions which throw away cgi's stderr.
+
+./werc.rc >>[2]/tmp/wlog.txt
--- a/lib/_debug.tpl
+++ /dev/null
@@ -1,29 +1,0 @@
-% if(! ~ $#debug_shell 0) {
-<form method="POST" name="prompt">
-<input size="80" type="text" name="command" value="%($"post_arg_command%)" />
-<input type="submit" Value="Run" />
-</form>
-<script language="javascript"><!--
-document.prompt.command.focus()
-//--></script>
-
-%{
-fn evl {
-    # Buffering is probably messing this up:
-    #rc -c 'flag x +;{'^$post_arg_command'} |[2] awk ''{print ">> "$0}'''
-    rc -c 'flag s +; flag x +;'^$post_arg_command
-}
-    if(! ~ $#post_arg_command 0 && ! ~ $#post_arg_command '') {
-        echo '<hr /><pre>'
-        evl | escape_html |[2] awk '{print "<b>"$0"</b>"}' 
-        echo '</pre>'
-    }
-%}
-% }
-
-<hr /><pre>
-% env | escape_html
-</pre><hr />
-
-% umask
-
--- a/lib/_users/login.tpl
+++ /dev/null
@@ -1,18 +1,0 @@
-<h1>User login</h1>
-<br />
-% if(check_user) {
-    You are logged in as: <b>%($logged_user%)</b>
-% }
-% if not {
-%    if (~ $REQUEST_METHOD POST)
-%        echo '<div class="notify_errors">Login failed!</div>'
-<form method="post" action="" style="text-align: right; float: left;">
-<fieldset>
-    <label>User name: <input type="text" name="user_name" value="%($"post_arg_user_name%)"/></label><br />
-    <label>User password: <input type="password" name="user_password" /></label><br />
-    <input name="s" type="submit" value="Login" />
-</fieldset>
-</form>
-% }
-
-<br style="clear:left" />
--- a/lib/feeds/atom.tpl
+++ /dev/null
@@ -1,141 +1,0 @@
-Content-Type: application/atom+xml
-
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- TODO: See for more info:http://www.tbray.org/ongoing/When/200x/2005/07/27/Atomic-RSS  -->
-%{
-fn statpost {
-    f = $1
-
-    updated = `{/bin/date --rfc-3339'=seconds' -r $f |sed 's/ /T/'} 
-    # XXX $post_uri is broken produces output that includes full file path (eg., /gsoc/www/...)
-    post_uri=$baseuri^`{cleanname `{echo $f | sed -e 's,^'$sitedir',,' -e 's/\.(md|tpl)$//g'}}
-    title=`{basename $f | sed 's/^[0-9\-]*_(.*)\.md$/\1/; s/_/ /g' }
-    # Not used: date=`{/bin/date -Rd `{basename $f |sed 's/(^[0-9\-]*).*/\1/; s/-[0-9]$//'}}
-    # TODO: use mtime(1) and ls(1) instead of lunix's stat(1)
-    stat=`{stat -c '%Y %U' $f}
-    #mdate=`{/bin/date -Rd `{mtime $f|awk '{print $1}' }} # Not used because it is unreliable
-    by=$stat(2)
-    ifs=() { summary=`{cat $f | crop_text 512 ... | $formatter } }
-}
-updated = `{/bin/date --rfc-3339'=seconds' |sed 's/ /T/'} 
-%}
-
-<feed xmlns="http://www.w3.org/2005/Atom"
-    xmlns:thr="http://purl.org/syndication/thread/1.0">
-
-    <link rel="self" href="%($uri%)"/>
-    <id>%($uri%)</id>
-    <icon>/favicon.ico</icon>
-
-    <title>%($siteTitle%)</title>
-    <subtitle>%($siteSubTitle%)</subtitle>
-
-    <!-- <updated>2008-09-24T12:47:00-04:00</updated> -->
-    <updated>%($updated%)</updated>
-    <link href="."/>
-
-%{
-        for(f in `{sortedBlogPostList $blogDirs}) {
-            statpost $f
-%}
-    <entry>
-        <!-- Maybe we should be smarter, see: http://diveintomark.org/archives/2004/05/28/howto-atom-id, example: <id>tag:intertwingly.net,2004:2899</id>  -->
-        <id>%($post_uri%)</id>
-        <link href="%($post_uri%)"/>
-        <title>%($title%)</title>
-        <!-- <link rel="replies" href="2899.atom" thr:count="0"/> -->
-        <author>
-            <name>%($by%)</name>
-        </author>
-
-
-        <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
-            %($summary%)
-        </div></content>
-
-        <updated>%($updated%)</updated>
-    </entry>
-
-
-%        }
-
-</feed>
-
-% exit 
-
-<feed xmlns="http://www.w3.org/2005/Atom"
-  xmlns:thr="http://purl.org/syndication/thread/1.0">
-  <link rel="self" href="http://intertwingly.net/blog/index.atom"/>
-  <id>http://intertwingly.net/blog/index.atom</id>
-  <icon>../favicon.ico</icon>
-
-  <title>Sam Ruby</title>
-  <subtitle>It’s just data</subtitle>
-  <author>
-    <name>Sam Ruby</name>
-    <email>[email protected]</email>
-    <uri>/blog/</uri>
-  </author>
-  <updated>2008-09-24T12:47:00-04:00</updated>
-  <link href="/blog/"/>
-  <link rel="license" href="http://creativecommons.org/licenses/BSD/"/>
-
-  <entry>
-    <id>tag:intertwingly.net,2004:2899</id>
-    <link href="/blog/2008/09/11/RubyConf-2008"/>
-    <link rel="replies" href="2899.atom" thr:count="0"/>
-    <title>RubyConf 2008</title>
-    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
-    
-<p>My <a href="http://www.rubyconf.org/talks/14">proposal</a> has been accepted for <a href="http://www.rubyconf.org/talks">RubyConf 2008</a>.  Because of the presence of Ruby implementers, this is going to be a bit challenging as it will likely turn into two talks at once.  One sharing experiences with fellow developers concerning things they may need to watch out for, and another with language designers about the impact of their changes.  It also is likely to be true, as it was at <a href="http://intertwingly.net/blog/2008/07/24/Ruby-1-9-What-to-Expect">OSCON</a>, that there will be members of the audience who know way more about this subject than I do.</p>
-<p>I had originally requested a slot on Saturday.  My current slot requires me to shave a day off of <a href="http://us.apachecon.com/c/acus2008/">ApacheCon</a>.  I’ve again asked that the slot be changed, but even if it doesn’t move, I can manage this.  At least we are only talking about a short hop from New Orleans to Orlando.</p>
-
-    </div></content>
-    <updated>2008-09-11T06:51:36-04:00</updated>
-  </entry>
-
-  <entry>
-    <id>tag:intertwingly.net,2004:2898</id>
-    <link href="/blog/2008/09/10/Small-Updates"/>
-    <link rel="replies" href="2898.atom" thr:count="8" thr:updated="2008-09-10T15:31:05-04:00"/>
-    <title>Small Updates</title>
-    <summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
-    
-    <p><a href="http://hublog.hubmed.org/archives/001744.html">Alf Eaton</a>: <em>Aside: if you’re reading a Planet that contains HubLog, those posts will all jump to the top - sorry! (I wish Planets dealt better with small updates so I didn’t have to worry about it).</em></p>
-<p>I don’t know what publishing software you use, but I see you provide an Atom feed, and Planet 2.0 and Venus both implement <a href="http://www.atomenabled.org/developers/syndication/atom-format-spec.php#element.updated">atom:updated</a> as specified in RFC 4287.</p>
-
-    </div></summary>
-    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
-
-<p><a href="http://hublog.hubmed.org/archives/001744.html"><cite>Alf Eaton</cite></a>: <em>Aside: if you’re reading a Planet that contains HubLog, those posts will all jump to the top - sorry! (I wish Planets dealt better with small updates so I didn’t have to worry about it).</em></p>
-<p>I don’t know what publishing software you use, but I see you provide an Atom feed, and Planet 2.0 and Venus both implement <a href="http://www.atomenabled.org/developers/syndication/atom-format-spec.php#element.updated">atom:updated</a> as specified in RFC 4287.</p>
-<p>More specifically, if you have a minor update and leave the updated date alone, the posts will not jump to the top.  The next release of WordPress, for example, will contain the necessary hooks for a <a href="http://blog.ciarang.com/posts/wp-minor-edit/">plugin</a> to provide a simple checkbox for indicating that the change constitutes a minor edit.</p>
-
-    </div></content>
-    <updated>2008-09-10T10:18:47-04:00</updated>
-  </entry>
-
-  <entry>
-    <id>tag:intertwingly.net,2004:2897</id>
-    <link href="/blog/2008/09/07/SVG-via-CSS"/>
-    <link rel="replies" href="2897.atom" thr:count="10" thr:updated="2008-09-12T02:21:21-04:00"/>
-    <title>SVG via CSS</title>
-    <summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
-    
-    <p>Now that I have my weblog looking reasonably consistent between Gecko and WebKit based browsers, I’ve taken another look at Opera.  Opera doesn’t have support for border-radius, but does have support for background images in SVG, which can be <a href="http://dev.opera.com/articles/view/new-development-techniques-using-opera-k/">used to provide the same effect</a>.  My Nav Bar on <a href="http://rails.intertwingly.net/blog/">my test site</a> now employs this technique, and it requires two separate images: <a href="http://rails.intertwingly.net/stylesheets/rc-039-CCD.svg">039 on CCD</a> and <a href="http://rails.intertwingly.net/stylesheets/rc-CCD-FFF.svg">CCD on FFF</a>.</p>
-<p>Frankly, my first reaction to this was mixed.  The pluses for SVG in CSS is that it doesn’t require either adjusting your markup or JavaScript to achieve these effects, a desirable characteristic that generally the <a href="http://www.cssjuice.com/25-rounded-corners-techniques-with-css/">other techniques</a> don’t share.</p>
-
-    </div></summary>
-
-    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
-    
-<p>Now that I have my weblog looking reasonably consistent between Gecko and WebKit based browsers, I’ve taken another look at Opera.  Opera doesn’t have support for border-radius, but does have support for background images in SVG, which can be <a href="http://dev.opera.com/articles/view/new-development-techniques-using-opera-k/">used to provide the same effect</a>.  My Nav Bar on <a href="http://rails.intertwingly.net/blog/">my test site</a> now employs this technique, and it requires two separate images: <a href="http://rails.intertwingly.net/stylesheets/rc-039-CCD.svg">039 on CCD</a> and <a href="http://rails.intertwingly.net/stylesheets/rc-CCD-FFF.svg">CCD on FFF</a>.</p>
-<p>Meanwhile, Robert O’Callahan has been exploring <a href="http://weblogs.mozillazine.org/roc/archives/2008/06/applying_svg_ef.html">other ways</a> to integrate these technologies.</p>
-
-    </div></content>
-    <updated>2008-09-07T11:12:29-04:00</updated>
-  </entry>
-
-</feed>
-
--- a/lib/feeds/html.tpl
+++ /dev/null
@@ -1,22 +1,0 @@
-% if (! ~ $blogTitle '')
-%    echo '<h1>'$"blogTitle'</h1>'
-
-<div style="text-align:right">(<a href="index.rss">RSS Feed</a>|<a href="index.atom">Atom Feed</a>)</div>
-
-%{
-for (f in `{ sortedBlogPostList $blogDirs }) {
-    gen_blog_post_title $f
-    cat $f 
-    echo ' ' # XXX I have no clue why the ' ' is needed, a echo without args breaks markdown.pl?!?
-} | $formatter
-
-# TODO Should check if user has perms and so on
-get_user
-if(~ $#logged_user 1) {
-%}
-<form method="POST" action="/_apps/brag/post_form">
-    <input type="hidden" name="target_blog_dir" value="%($blogDirs(1)%)" />
-    <input type="submit" name="Submit" value="New blog post" /> 
-</form>
-% }
-<hr />
--- a/lib/feeds/rss20.tpl
+++ /dev/null
@@ -1,61 +1,0 @@
-Content-Type: text/xml; charset=utf-8
-
-<?xml version="1.0" encoding="UTF-8"?>
-
-%{
-fn statpost {
-	f = $1
-	uri = `{echo $f | sed 's,^'$sitedir',,'}
-	title=`{basename $f | sed 's/^[0-9\-]*_(.*)\.md$/\1/; s/_/ /g' }
-	date=`{/bin/date -Rd `{basename $f |sed 's/(^[0-9\-]*).*/\1/; s/-[0-9]$//'}}
-	# TODO: use mtime(1) and ls(1) instead of lunix's stat(1)
-	stat=`{stat -c '%Y %U' $f}
-	#mdate=`{/bin/date -Rd $stat(1)} # Not used because it is unreliable
-	uri=$baseuri^`{cleanname `{echo -n $uri | sed 's/\.(md|tpl)//g'}}
-	by=$stat(2)
-	ifs=() {
-		summary=`{awk -v max'='1024 '{
-			nc += 1 + length;
-			if(nc > max) {
-				print substr($0, 1, nc - max) "..."
-				exit
-			}
-			print
-		}' $f |fmt -j| sed 's/\]\]>/Fucking goddamn XML garbage/g'}
-	}
-}
-
-%}
-
-<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
-	<channel>
-		<atom:link href="%('http://'$site^$REQUEST_URI%)" rel="self" type="application/rss+xml" />
-		<title>%($siteTitle%)</title>
-		<link>%($uri%)</link>
-		<description>%($blogDesc%)</description>
-		<language>en-us</language>
-		<generator>Tom Duff's rc, and Kris Maglione's clever hackery</generator>
-%{
-		# <webMaster>[email protected] (Uriel)</webMaster>
-		for(f in `{sortedBlogPostList $blogDirs}) {
-			statpost $f
-			# Hack to aproximate the last build date 
-			#(use the mdate from last posted item)
-			# Commented out for now because maybe a wrong value is worse than no value
-			#if(~ $#last_build_date 0) {
-				#last_build_date='<lastBuildDate>'^$"mdate'</lastBuildDate>'
-				#echo $last_build_date
-			#}
-%}
-		<item>
-			<title>%($title%)</title>
-			<author>%($by%)@noreply.cat-v.org (%($by%))</author>
-			<link>%($uri%)</link>
-                        <guid isPermaLink="true">%($uri%)</guid>
-			<pubDate>%($date%)</pubDate>
-			<description><![CDATA[<pre>%($summary%)</pre>]]></description>
-		</item>
-%		}
-
-	</channel>
-</rss>
--- a/lib/sitemap.tpl
+++ /dev/null
@@ -1,65 +1,0 @@
-<h1>Site map</h1>
-
-%{
-tmpfile=/tmp/werc_sitemap_$pid.txt
-saveddf=$dirfilter
-
-fn get_md_title {
-    sed 's/^(................................................................[^ ]*).*$/\1/g; 1q' < $1 
-}
-
-fn get_html_title {
-    # H1 is not reliable because htmlroff doesn't use it :(
-    #desc=`{cat $1 | sed 32q | grep '<[Hh]1>' | sed 's/<[Hh]1>(.*)(<\/[Hh]1>|$)/\1/;s/<[^>]*>//g;1q'}
-    # Pick the first line of body  instead
-    desc=`{sed -n '/<[Bb][Oo][Dd][Yy]/,/./s/(<[^>]*>|$)//gp' < $1}
-    if(~ $#desc 0)
-        desc=`{sed 's/<[^>]*>//g; 1q' < $1}
-}
-
-fn get_file_title {
-        
-    if(~ $1 */) {
-        if(test -f $1/index.md)
-            get_md_title $1/index.md
-        if not if(test -f $1/index.html)
-            get_html_title $1/index.html
-    }
-    if not if(~ $1 *.md)
-        get_md_title $1
-    if not if(~ $1 *.html)
-        get_html_title $1
-    if not
-        echo ''
-}
-
-fn listDir {
-    d=$1
-    dirfilter=$saveddf
-    if(test -f $d/_werc/config)
-        . ./$d/_werc/config
-
-    if(~ $#perm_redir_to 0) {
-        echo '<ul class="sitemap-list">'
-
-        for(i in `{ls -dF $d^*/ $d^*.md $d^*.html $d^*.txt >[2]/dev/null | sed $dirfilter}) {
-            desc=`{get_file_title $i}
-            u=`{echo $i|sed 's!'$sitedir'!!; '$dirclean's!/index$!/!; '}
-            if(! ~ $#desc 0 && ! ~ $desc '')
-                desc=' - '$"desc
-            n=`{echo /$u|sed 's/_/ /g; s,.*/([^/]+)/?$,\1,'}
-            echo '<li><a href="'$base_url$u'">'^$"n^'</a>' $"desc '</li>' 
-            echo $base_url^$u >> $tmpfile
-            if(test -d $i)
-                @{ listDir $i }
-        }
-        echo '</ul>'
-    }
-}
-
-fltr_cache listDir $sitedir/
-
-if(test -s $tmpfile)
-    mv $tmpfile $sitedir/sitemap.txt &
-
-%}
--- /dev/null
+++ b/tpl/_debug.tpl
@@ -1,0 +1,29 @@
+% if(! ~ $#debug_shell 0) {
+<form method="POST" name="prompt">
+<input size="80" type="text" name="command" value="%($"post_arg_command%)" />
+<input type="submit" Value="Run" />
+</form>
+<script language="javascript"><!--
+document.prompt.command.focus()
+//--></script>
+
+%{
+fn evl {
+    # Buffering is probably messing this up:
+    #rc -c 'flag x +;{'^$post_arg_command'} |[2] awk ''{print ">> "$0}'''
+    rc -c 'flag s +; flag x +;'^$post_arg_command
+}
+    if(! ~ $#post_arg_command 0 && ! ~ $#post_arg_command '') {
+        echo '<hr /><pre>'
+        evl | escape_html |[2] awk '{print "<b>"$0"</b>"}' 
+        echo '</pre>'
+    }
+%}
+% }
+
+<hr /><pre>
+% env | escape_html
+</pre><hr />
+
+% umask
+
--- /dev/null
+++ b/tpl/_users/login.tpl
@@ -1,0 +1,18 @@
+<h1>User login</h1>
+<br />
+% if(check_user) {
+    You are logged in as: <b>%($logged_user%)</b>
+% }
+% if not {
+%    if (~ $REQUEST_METHOD POST)
+%        echo '<div class="notify_errors">Login failed!</div>'
+<form method="post" action="" style="text-align: right; float: left;">
+<fieldset>
+    <label>User name: <input type="text" name="user_name" value="%($"post_arg_user_name%)"/></label><br />
+    <label>User password: <input type="password" name="user_password" /></label><br />
+    <input name="s" type="submit" value="Login" />
+</fieldset>
+</form>
+% }
+
+<br style="clear:left" />
--- /dev/null
+++ b/tpl/sitemap.tpl
@@ -1,0 +1,65 @@
+<h1>Site map</h1>
+
+%{
+tmpfile=/tmp/werc_sitemap_$pid.txt
+saveddf=$dirfilter
+
+fn get_md_title {
+    sed 's/^(................................................................[^ ]*).*$/\1/g; 1q' < $1 
+}
+
+fn get_html_title {
+    # H1 is not reliable because htmlroff doesn't use it :(
+    #desc=`{cat $1 | sed 32q | grep '<[Hh]1>' | sed 's/<[Hh]1>(.*)(<\/[Hh]1>|$)/\1/;s/<[^>]*>//g;1q'}
+    # Pick the first line of body  instead
+    desc=`{sed -n '/<[Bb][Oo][Dd][Yy]/,/./s/(<[^>]*>|$)//gp' < $1}
+    if(~ $#desc 0)
+        desc=`{sed 's/<[^>]*>//g; 1q' < $1}
+}
+
+fn get_file_title {
+        
+    if(~ $1 */) {
+        if(test -f $1/index.md)
+            get_md_title $1/index.md
+        if not if(test -f $1/index.html)
+            get_html_title $1/index.html
+    }
+    if not if(~ $1 *.md)
+        get_md_title $1
+    if not if(~ $1 *.html)
+        get_html_title $1
+    if not
+        echo ''
+}
+
+fn listDir {
+    d=$1
+    dirfilter=$saveddf
+    if(test -f $d/_werc/config)
+        . ./$d/_werc/config
+
+    if(~ $#perm_redir_to 0) {
+        echo '<ul class="sitemap-list">'
+
+        for(i in `{ls -dF $d^*/ $d^*.md $d^*.html $d^*.txt >[2]/dev/null | sed $dirfilter}) {
+            desc=`{get_file_title $i}
+            u=`{echo $i|sed 's!'$sitedir'!!; '$dirclean's!/index$!/!; '}
+            if(! ~ $#desc 0 && ! ~ $desc '')
+                desc=' - '$"desc
+            n=`{echo /$u|sed 's/_/ /g; s,.*/([^/]+)/?$,\1,'}
+            echo '<li><a href="'$base_url$u'">'^$"n^'</a>' $"desc '</li>' 
+            echo $base_url^$u >> $tmpfile
+            if(test -d $i)
+                @{ listDir $i }
+        }
+        echo '</ul>'
+    }
+}
+
+fltr_cache listDir $sitedir/
+
+if(test -s $tmpfile)
+    mv $tmpfile $sitedir/sitemap.txt &
+
+%}