shithub: werc

Download patch

ref: 9681c09647c3d4ef8bd5ab25df88cf7176c55707
parent: d131af2763916440a3a958cfd68c5853fa2f3b50
author: sl <sl@stanleylieber.com>
date: Thu Feb 4 10:05:05 EST 2016

initial import of changes from 9front bikeshedding; bundle rc-httpd

diff: cannot open b/apps/paste//null: file does not exist: 'b/apps/paste//null' diff: cannot open b/bin/contrib/rc-httpd/handlers//null: file does not exist: 'b/bin/contrib/rc-httpd/handlers//null' diff: cannot open b/bin/contrib/rc-httpd//null: file does not exist: 'b/bin/contrib/rc-httpd//null'
--- a/.hgignore
+++ b/.hgignore
@@ -1,2 +1,3 @@
 syntax: glob
+etc/initrc.local
 sites/?*/
--- a/README
+++ b/README
@@ -14,7 +14,7 @@
 Requirements:
 
 * An http server that can handle CGIs
-* Plan 9 from User Space: http://plan9.us - Or 9base-5 or later: http://tools.suckless.org/9base
+* Plan 9 from User Space: http://swtch.com/plan9port - Or 9base-tip: http://tools.suckless.org/9base
 
 Note: Werc by default expects the Plan 9 tools to be installed under
 /usr/local/plan9/bin/, if you have installed them elsewhere you will need to
@@ -46,7 +46,7 @@
 customize it as needed. Site (and directory) specific options can be set in a
 sites/example.com/_werc/config file inside the site's directory. To customize
 templates and included files you can store your own version of the files in
-lib/ under sites/example.com/_werc/lib
+lib/ under sites/example.com/_werc/lib.
 
 The source tree for the werc website is included under sites/werc.cat-v.org as
 an example, feel free to use it as a template for your own site.
@@ -58,21 +58,17 @@
 Contact
 -------
 
-For comments, suggestions, bug reports or patches join the werc9 list in
-google groups: http://groups.google.com/group/werc9 or the irc channel #cat-v
-in irc.freenode.org
+For comments, suggestions, bug reports or patches join the werc mailing list
+at: http://werc.cat-v.org or the irc channel #cat-v in irc.freenode.org
 
 If you have a public website that uses werc I would love to hear about it and
 get feedback about you experience setting it up.
 
-If you want to be notified of new releases see the News section of the website
-or use Freshmeat: http://freshmeat.net/projects/werc/
-
 Thanks
 ------
 
-Garbeam, Kris Maglione, sqweek, soul9, mycroftiv, maht, yiyus and many others
-for their ideas, patches, testing and other contributions.
+Garbeam, Kris Maglione, sqweek, soul9, mycroftiv, maht, yiyus, cinap_lenrek,
+khm and many others for their ideas, patches, testing and other contributions.
 
 
 License
--- a/apps/blagh/app.rc
+++ b/apps/blagh/app.rc
@@ -26,7 +26,7 @@
         if(check_user $conf_blog_editors) {
             editor_mode=on
             if(~ $"post_arg_date '')
-                post_date=`{/bin/date +%F|sed 's,-,/,g'}
+                post_date=`{date -i|sed 's,-,/,g'}  # date -i is 9front/9base only
             if not
                 post_date=$post_arg_date
             ll_add handlers_bar_left echo '<a href="'$blagh_uri'new_post">Make a new post</a>'
--- a/apps/blagh/atom.tpl
+++ b/apps/blagh/atom.tpl
@@ -5,18 +5,18 @@
 fn statpost {
     f = $1
 
-    updated = `{/bin/date --rfc-3339'=seconds' -r $f |tr ' ' 'T'} 
+    updated = `{date -t `{mtime $f | awk '{print $1}'}} # date -t is 9front/9base only
     post_uri=$base_url^`{cleanname `{echo $f | sed -e 's!^'$sitedir'!!'}}^'/'
     title=`{read $f/index.md}
     # 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}
+    #stat=`{stat -c '%Y %U' $f}
     #mdate=`{/bin/date -Rd `{mtime $f|awk '{print $1}' }} # Not used because it is unreliable
-    by=$stat(2)
+    by=`{ls -m $f | sed 's/^\[//g; s/].*$//g' >[2]/dev/null}
     #ifs=() { summary=`{cat $f/index.md | crop_text 1024 ... | $formatter } }
     ifs=() { summary=`{cat $f/index.md | strip_title_from_md_file | ifs=$difs {$formatter} } }
 }
-updated = `{/bin/date --rfc-3339'=seconds' |sed 's/ /T/'} 
+updated = `{date -t} # date -t is 9front/9base only
 %}
 
 <feed xmlns="http://www.w3.org/2005/Atom"
@@ -28,10 +28,10 @@
 
     <link rel="self" href="%($base_url^$req_path%)"/>
     <id>%($base_url^$req_path%)</id>
-    <icon>/favicon.ico</icon>
+    <icon><![CDATA[/favicon.ico]]></icon>
 
-    <title>%($siteTitle%)</title>
-    <subtitle>%($siteSubTitle%)</subtitle>
+    <title><![CDATA[%($siteTitle%)]]></title>
+    <subtitle><![CDATA[%($siteSubTitle%)]]></subtitle>
 
 % # <updated>2008-09-24T12:47:00-04:00</updated>
     <updated>%($updated%)</updated>
@@ -44,13 +44,13 @@
 % # 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>
+        <title><![CDATA[%($title%)]]></title>
 % # <link rel="replies" href="2899.atom" thr:count="0"/>
-        <author><name>%($by%)</name></author>
+        <author><name><![CDATA[%($by%)]]></name></author>
 
 
         <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
-            %($summary%)
+            <![CDATA[%($summary%)]]>
         </div></content>
 
         <updated>%($updated%)</updated>
@@ -61,79 +61,3 @@
 </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>rubys@intertwingly.net</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/apps/blagh/rss20.tpl
+++ b/apps/blagh/rss20.tpl
@@ -6,13 +6,13 @@
 
     post_uri = `{echo $f | sed 's,^'$sitedir',,'}
     #title=`{basename $f | sed 's/^[0-9\-]*_(.*)\.md$/\1/; s/_/ /g' }
-        title=`{read $f/index.md}
-    date=`{/bin/date -Rd `{echo $f|sed 's,.*/([0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9])/.*,\1,'}}
+    title=`{read $f/index.md}
+    date=`{date `{mtime $f | awk '{print $1}'}} # rss 2.0 spec says pubDate should conform to rfc822
     # TODO: use mtime(1) and ls(1) instead of lunix's stat(1)
-    stat=`{stat -c '%Y %U' $f}
+    #stat=`{stat -c '%Y %U' $f}
     #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)
+    by=`{ls -m $f | sed 's/^\[//g; s/].*$//g' >[2]/dev/null}
     ifs=() {summary=`{ cat $f/index.md |strip_title_from_md_file| ifs=$difs {$formatter | escape_html} }}
 }
 
@@ -21,11 +21,11 @@
 <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
     <channel>
         <atom:link href="%($base_url^$req_path%)" rel="self" type="application/rss+xml" />
-        <title>%($siteTitle%)</title>
+        <title><![CDATA[%($siteTitle%)]]></title>
         <link>%($base_url^$req_path%)</link>
-        <description>%($blogDesc%)</description>
+        <description><![CDATA[%($blogDesc%)]]></description>
         <language>en-us</language>
-        <generator>Tom Duff's rc, and Kris Maglione's clever hackery</generator>
+        <generator><![CDATA[Tom Duff's rc, and Kris Maglione's clever hackery]]></generator>
 %{
         # <webMaster>uriel99+rss@gmail.com (Uriel)</webMaster>
         for(f in `{get_post_list $blagh_root$blagh_dirs}) {
@@ -39,12 +39,12 @@
             #}
 %}
         <item>
-            <title>%($title%)</title>
-            <author>%($by%)@noreply.cat-v.org (%($by%))</author>
+            <title><![CDATA[%($title%)]]></title>
+            <author><![CDATA[%($by%)@noreply.cat-v.org (%($by%))]]></author>
             <link>%($post_uri%)</link>
             <guid isPermaLink="true">%($post_uri%)</guid>
             <pubDate>%($date%)</pubDate>
-            <description>%($summary%)</description>
+            <description><![CDATA[%($summary%)]]></description>
         </item>
 %        }
 
--- a/apps/dirdir/app.rc
+++ b/apps/dirdir/app.rc
@@ -7,7 +7,7 @@
 fn dirdir_init {
     if(! ~ $#enable_wiki 0 && check_user $wiki_editors_groups) {
         lp=$local_path
-        # werc.rc doesn't append /index when $local_path doesn't exit
+        # werc.rc doesn't append /index when $local_path doesn't exist
         # maybe it should, but for now we can fix it up here.
         if(~ $lp */) 
             lp=$lp^'index'
--- /dev/null
+++ b/apps/paste/app.rc
@@ -1,0 +1,33 @@
+fn conf_enable_paste {
+        paste_url=$conf_wd
+        paste_dir=`{pwd}
+        conf_enable_app paste
+}
+
+fn paste_init {
+        if (~ $REQUEST_METHOD POST && ~ $post_arg_url url && ~ $req_path $paste_url ) { # incoming paste 
+                now=`{ date -n }
+                cksum=`{ echo $"post_arg_paste | sum | awk '{ print $1 }' }
+                if (~ $cksum '1715a8eb' ) { # empty paste; discard
+                        post_redirect $base_url^$paste_url
+                }
+                if not {  # save and redirect
+                        # TODO: stop using echo
+                        echo $"post_arg_paste > $paste_dir^/^$now^.^$cksum
+#cat<<EOF > $paste_dir^/^$now^.^$cksum
+#$"post_arg_paste
+#EOF
+                        #post_redirect $base_url^$paste_url^$now^.^$cksum
+                        echo 'Content-type: text/plain'; echo ''; exec echo $base_url^$paste_url^$now^.^$cksum
+                }
+        } 
+        if not { # show a paste if there is one
+                if (test -r $werc_root/$local_path && ~ $QUERY_STRING raw ) { 
+                                echo 'Content-type: text/plain'; echo ''; exec cat $werc_root/$local_path
+                        } 
+        }
+
+# if we haven't done anything by now, we don't care.  we just exit and let
+# werc handle it from here.
+}
+
--- a/apps/wman/app.rc
+++ b/apps/wman/app.rc
@@ -71,7 +71,6 @@
 
 fn wman_page_gen {
     #troff -manhtml $1| troff2html -t 'Plan 9 from User Space'
-    # Using GNU col here to remove nroffs garbage (eg., from .ft B); p9p has no col(1) :(
     troff -N -m$wman_tmac $1 | wman_out_filter
 }
 
@@ -80,10 +79,11 @@
 }
 
 fn wman_default_out_filter {
+    # Using GNU col here to remove nroffs garbage (eg., from .ft B); p9p has no col(1) :(
     escape_html \
     | sed 's!([\.\-a-zA-Z0-9]+)\(('^`{echo $wman_cat_list|tr ' ' '|'}^')\)!<a href="../\2/\1">&</a>!g' \
-    | awk '/^$/ {if(n != 1) print; n=1; next} /./ {n=0; print}'
-
+    | awk '/^$/ {if(n != 1) print; n=1; next} /./ {n=0; print}' \
+    | col -x
 }
 
 
--- a/apps/wman/man_page.tpl
+++ b/apps/wman/man_page.tpl
@@ -1,7 +1,3 @@
 <pre>
-%{
-
-wman_page_gen $wman_page_file
-
-%}
+% wman_page_gen $wman_page_file
 </pre>
--- a/apps/wman/page_list.tpl
+++ b/apps/wman/page_list.tpl
@@ -1,11 +1,8 @@
-% d=`{wman_get_section_desc $wman_cat} 
-<h1>Manual pages - Section %($wman_cat%): %($"d%)</h1>
 
+% d=`{wman_get_section_desc $wman_cat}
+<h1>Manual pages - Section
+% echo $wman_cat': '$"d2
+</h1>
 <ul style="float:left">
-%{
-wman_ls_pages $wman_cat_path \
-    | awk -F/ '{ print "<li><a href=\""$(NF)"\">"$(NF)"</a></li>" }
-    NR%20 == 0 { print "</ul><ul style=\"float: left\">" }'
-%}
+% wman_ls_pages $wman_cat_path | awk -F/ '{ print "<li><a href=\""$(NF)"\">"$(NF)"</a></li>" } NR%20 == 0 { print "</ul><ul style=\"float: left\">" }'
 </ul>
-
--- a/bin/cgilib.rc
+++ b/bin/cgilib.rc
@@ -2,7 +2,7 @@
 
 fn dprint { echo $* >[1=2] }
 fn dprintv { { for(v in $*) { echo -n $v^'#'^$#$v^'=' $$v '; '  }; echo } >[1=2] }
-
+fn echo {if(! ~ $1 -n || ! ~ $2 '') /bin/echo $*}
 fn escape_html { sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g' $* }
 
 fn http_redirect {
@@ -12,7 +12,7 @@
         t=$"base_url^$1
     if not
         t=$"base_url^$"req_path^$1
-    echo 'Status: '^$2^'
+    exec /bin/echo 'Status: '^$2^'
 Location: '^$t^'
 
 '
@@ -29,7 +29,7 @@
         ifs='&
 '       for(pair in `{cat}) {
             ifs='=' { pair=`{echo -n $pair} }
-            n='post_arg_'^`{echo $pair(1)|tr -cd 'a-zA-Z0-9_'}
+            n='post_arg_'^`{echo $pair(1)|urldecode|tr -cd 'a-zA-Z0-9_'}
             post_args=( $post_args $n )
             ifs=() { $n=`{echo -n $pair(2)|urldecode|tr -d '
'} }
         }
@@ -64,89 +64,52 @@
     END { printf "%s", buf }'
 }
 
-fn urldecode {
-awk '
-BEGIN {
-    hextab ["0"] = 0; hextab ["8"] = 8;
-    hextab ["1"] = 1; hextab ["9"] = 9;
-    hextab ["2"] = 2; hextab ["A"] = hextab ["a"] = 10
-    hextab ["3"] = 3; hextab ["B"] = hextab ["b"] = 11;
-    hextab ["4"] = 4; hextab ["C"] = hextab ["c"] = 12;
-    hextab ["5"] = 5; hextab ["D"] = hextab ["d"] = 13;
-    hextab ["6"] = 6; hextab ["E"] = hextab ["e"] = 14;
-    hextab ["7"] = 7; hextab ["F"] = hextab ["f"] = 15;
-}
-{
-    decoded = ""
-    i = 1
-    len = length ($0)
-    while ( i <= len ) {
-        c = substr ($0, i, 1)
-        if ( c == "%" ) {
-            if ( i+2 <= len ) {
-                c1 = substr ($0, i+1, 1)
-                c2 = substr ($0, i+2, 1)
-                if ( hextab [c1] == "" || hextab [c2] == "" ) {
-                    print "WARNING: invalid hex encoding: %" c1 c2 | "cat >&2"
-                } else {
-                    code = 0 + hextab [c1] * 16 + hextab [c2] + 0
-                    c = sprintf ("%c", code)
-                    i = i + 2
-                }
-            } else {
-                print "WARNING: invalid % encoding: " substr ($0, i, len - i)
-            }
-        } else if ( c == "+" ) {
-            c = " "
-        }
-        decoded = decoded c
-        ++i
-    }
-    printf "%s", decoded
-}
-'
-}
+fn urldecode { /bin/urlencode -d }
 
-fn url_encode {
-    awk '
-    BEGIN {
-    # We assume an awk implementation that is just plain dumb.
-    # We will convert an character to its ASCII value with the
-    # table ord[], and produce two-digit hexadecimal output
-    # without the printf("%02X") feature.
+#fn urldecode {
+#awk '
+#BEGIN {
+#    hextab ["0"] = 0; hextab ["8"] = 8;
+#    hextab ["1"] = 1; hextab ["9"] = 9;
+#    hextab ["2"] = 2; hextab ["A"] = hextab ["a"] = 10
+#    hextab ["3"] = 3; hextab ["B"] = hextab ["b"] = 11;
+#    hextab ["4"] = 4; hextab ["C"] = hextab ["c"] = 12;
+#    hextab ["5"] = 5; hextab ["D"] = hextab ["d"] = 13;
+#    hextab ["6"] = 6; hextab ["E"] = hextab ["e"] = 14;
+#    hextab ["7"] = 7; hextab ["F"] = hextab ["f"] = 15;
+#}
+#{
+#    decoded = ""
+#    i = 1
+#    len = length ($0)
+#    while ( i <= len ) {
+#        c = substr ($0, i, 1)
+#        if ( c == "%" ) {
+#            if ( i+2 <= len ) {
+#                c1 = substr ($0, i+1, 1)
+#                c2 = substr ($0, i+2, 1)
+#                if ( hextab [c1] == "" || hextab [c2] == "" ) {
+#                    print "WARNING: invalid hex encoding: %" c1 c2 | "cat >&2"
+#                } else {
+#                    code = 0 + hextab [c1] * 16 + hextab [c2] + 0
+#                    c = sprintf ("%c", code)
+#                    i = i + 2
+#                }
+#            } else {
+#                print "WARNING: invalid % encoding: " substr ($0, i, len - i)
+#            }
+#        } else if ( c == "+" ) {
+#            c = " "
+#        }
+#        decoded = decoded c
+#        ++i
+#    }
+#    printf "%s", decoded
+#}
+#'
+#}
 
-    EOL = "%0A"     # "end of line" string (encoded)
-    split ("1 2 3 4 5 6 7 8 9 A B C D E F", hextab, " ")
-    hextab [0] = 0
-    for ( i=1; i<=255; ++i ) ord [ sprintf ("%c", i) "" ] = i + 0
-    if ("'^$"EncodeEOL^'" == "yes") EncodeEOL = 1; else EncodeEOL = 0
-    }
-    {
-    encoded = ""
-    for ( i=1; i<=length ($0); ++i ) {
-        c = substr ($0, i, 1)
-        if ( c ~ /[a-zA-Z0-9.-]/ ) {
-        encoded = encoded c     # safe character
-        } else if ( c == " " ) {
-        encoded = encoded "+"   # special handling
-        } else {
-        # unsafe character, encode it as a two-digit hex-number
-        lo = ord [c] % 16
-        hi = int (ord [c] / 16);
-        encoded = encoded "%" hextab [hi] hextab [lo]
-        }
-    }
-    if ( EncodeEOL ) {
-        printf ("%s", encoded EOL)
-    } else {
-        print encoded
-    }
-    }
-    END {
-        #if ( EncodeEOL ) print ""
-    }
-' $* 
-}
+fn url_encode { /bin/urlencode $* }
 
 # Cookies
 fn set_cookie {
@@ -165,10 +128,10 @@
 
 
 fn static_file {
-    echo 'Content-Type: '`{select_mime $1}
+    echo -n 'Content-Type: '
+    select_mime $1
     echo
-    cat $1
-    exit
+    exec cat $1
 }
 
 fn select_mime {
--- /dev/null
+++ b/bin/contrib/rc-httpd/handlers/__mkfstmp
@@ -1,0 +1,14 @@
+#!/bin/rc
+cgiargs=$*
+
+fn error{
+	if(~ $1 404)
+		exec cgi $cgiargs
+	if not
+		$rc_httpd_dir/handlers/error $1
+}
+
+if(~ $location */)
+	exec cgi $cgiargs
+if not
+	exec serve-static
--- /dev/null
+++ b/bin/contrib/rc-httpd/handlers/authorize
@@ -1,0 +1,6 @@
+#!/bin/rc
+if(~ $REMOTE_USER ''){
+	extra_headers=($extra_headers 'WWW-Authenticate: Basic realm="'$"SERVER_NAME'"')
+	error 401
+	exit
+}
--- /dev/null
+++ b/bin/contrib/rc-httpd/handlers/cgi
@@ -1,0 +1,46 @@
+#!/bin/rc
+fn filter_headers{
+	response=(200 OK)
+	lines=''
+	done=false
+	while(~ $done false){
+		line=`{getline}
+		head=`{echo $line | awk '{print tolower($1)}'}
+		if(~ $head status:*)
+			response=`{echo $line | awk '{$1="" ; print}'}
+		if not if(~ $line '')
+			done=true
+		if not
+			lines=$"lines^$"line^$cr^'
+'
+	}
+	echo 'HTTP/1.1' $"response^$cr
+	echo -n $"lines
+	do_log $response(1)
+}
+
+fn run_cgi {
+	path=$cgi_path exec $"cgi_bin $params || echo 'Status: 500'
+}
+
+cgi_bin=$1
+cgi_dir=.
+if(! ~ $#* 1)
+	cgi_dir=$*($#*)
+if not if(~ $"cgi_bin /*){
+	cgi_dir=`{basename -d $"cgi_bin}
+	cgi_dir=$"cgi_dir
+}
+if(! ~ $"cgi_bin */*)
+	cgi_bin=./$"cgi_bin
+if(! builtin cd $"cgi_dir >[2]/dev/null || ! test -x $"cgi_bin){
+	error 500
+	exit
+}
+
+run_cgi | {
+	filter_headers
+	emit_extra_headers
+	echo $cr
+	exec cat
+}
--- /dev/null
+++ b/bin/contrib/rc-httpd/handlers/dir-index
@@ -1,0 +1,111 @@
+#!/bin/rc
+PATH_INFO=`{echo $PATH_INFO | urlencode -d}
+full_path=$"FS_ROOT^$"PATH_INFO
+full_path=$"full_path
+if(! test -d $full_path){
+	error 404
+	exit
+}
+if(! test -r $full_path -x $full_path){
+	error 503
+	exit
+}
+do_log 200
+builtin cd $full_path
+if(~ $"NOINDEXFILE ^ $"NOINDEX ''){
+	ifile=index.htm*
+	if(! ~ $ifile(1) *'*'){
+		PATH_INFO=$ifile(1)
+		FS_ROOT=''
+		exec serve-static
+	}
+}
+title=`{echo $SITE_TITLE | sed s,%s,^$"PATH_INFO^,}
+title=$"title
+lso=()
+switch($2){
+case size
+	# ls has no option to sort by size
+	# could pipe it through sort, I suppose
+case date
+	lso=-t
+}
+echo 'HTTP/1.1 200 OK'^$cr
+emit_extra_headers
+echo 'Content-type: text/html'^$cr
+echo $cr
+echo '<html>
+<head>
+<title>'^$title^'</title>
+<style type="text/css">
+	.size {
+		text-align: right;
+		padding-right: 4pt;
+	}
+	.day {
+		text-align: right;
+		padding-right: 3pt;
+	}
+	.datetime {
+		text-align: right;
+	}
+	.name {
+		text-align: right;
+		padding-left: 3pt;
+	}
+</style>
+</head>
+<body>'
+echo '<h1>'^$title^'</h1>'
+if(! ~ $PATH_INFO /)
+	echo '<a href="../">Parent directory</a>'
+echo '<table>'
+ls -lQ $lso | awk '
+function urlencode(loc){
+	# very minimal encoding, just enough for our static-file purposes
+	url=loc
+	gsub("%", "%25", url)		# this one first!
+	gsub("\\$", "%24", url)
+	gsub("&", "%26", url)
+	gsub("\\+", "%2B", url)
+	gsub("\\?", "%3F", url)
+	gsub(" ", "%20", url)
+	gsub("\"", "%22", url)
+	gsub("#", "%23", url)
+	return url
+}
+function hrsize(size){
+	if(size > 1073741824) return sprintf("%.1fGB", size/1073741824)
+	if(size > 10485760) return sprintf("%iMB", size/1048576)
+	if(size > 1048576) return sprintf("%.1fMB", size/1048576)
+	if(size > 10240) return sprintf("%iKB", size/1024)
+	if(size > 1024) return sprintf("%.1fKB", size/1024)
+	return sprintf("%iB", size)
+}
+/^(-|a)/ {
+	print "<tr>"
+	print "<td class=\"size\">"hrsize($6)"</td>"
+	print "<td class=\"month\">"$7"</td>"
+	print "<td class=\"day\">"$8"</td>"
+	print "<td class=\"datetime\">"$9"</td>"
+	$1="" ; $2="" ; $3="" ; $4="" ; $5="" ; $6="" ; $7="" ; $8="" ; $9=""
+	sub("^ *?", "")
+	print "<td><a class=\"file name\" href=\""urlencode($0)"\">"$0"</a></td>"
+	print "</tr>"
+	$0=""
+}
+/^d/ {
+	print "<tr>"
+	print "<td class=\"size\"> </td>"
+	print "<td class=\"month\">"$7"</td>"
+	print "<td class=\"day\">"$8"</td>"
+	print "<td class=\"datetime\">"$9"</td>"
+	$1="" ; $2="" ; $3="" ; $4="" ; $5="" ; $6="" ; $7="" ; $8="" ; $9=""
+	sub("^ *?", "")
+	print "<td><a class=\"dir name\" href=\""urlencode($0)"/\">"$0"/</a></td>"
+	print "</tr>"
+}'
+echo '</table>
+
+</body>
+</html>'
--- /dev/null
+++ b/bin/contrib/rc-httpd/handlers/error
@@ -1,0 +1,43 @@
+#!/bin/rc
+# DO NOT make this script callable directly from the web!
+fn do_error{
+	echo 'HTTP/1.1 '^$1^$cr
+	emit_extra_headers
+	echo 'Content-type: text/html'^$cr
+	echo $cr
+	echo '<html>
+<head>
+<title>'^$1^'</title>
+</head>
+<body>
+<h1>'^$1^'</h1>'
+	echo $2
+	echo '<p><i>rc-httpd at' $SERVER_NAME '</i>'
+	echo '
+	</body>
+	</html>
+	'
+}
+
+fn 401{
+	do_error '401 Unauthorized' \
+	'The requested path '^$"location^' requires authorization.'
+}
+
+fn 404{
+	do_error '404 Not Found' \
+	'The requested path '^$"location^' was not found on this server.'
+}
+
+fn 500{
+	do_error '500 Internal Server Error' \
+	'The server has encountered an internal misconfiguration and is unable to satisfy your request.'
+}
+
+fn 503{
+	do_error '503 Forbidden' \
+	'You do not have permission to access '^$"location^' on this server.'
+}
+
+do_log $1
+$1
--- /dev/null
+++ b/bin/contrib/rc-httpd/handlers/redirect
@@ -1,0 +1,30 @@
+#!/bin/rc
+if(~ $#2 0){
+	error 500
+	exit
+}
+switch($1){
+case perm*
+	do_log 301
+	echo 'HTTP/1.1 301 Moved Permanently'^$cr
+case temp*
+	do_log 302
+	echo 'HTTP/1.1 302 Moved Temporarily'^$cr
+case seeother
+	do_log 303
+	echo 'HTTP/1.1 303 See Other'^$cr
+case *
+	error 500
+	exit
+}
+echo 'Location: ' ^ $2 ^ $cr
+emit_extra_headers
+echo 'Content-type: text/html'^$cr
+echo $cr
+echo '<html><body>'
+if(~ $#3 0)
+	echo 'Browser did not accept redirect.'
+if not
+	echo $3
+echo '<a href="'^$"location^'/">Click here</a>'
+echo '</body></html>'
--- /dev/null
+++ b/bin/contrib/rc-httpd/handlers/serve-static
@@ -1,0 +1,43 @@
+#!/bin/rc
+full_path=`{echo $"FS_ROOT^$"PATH_INFO | urlencode -d}
+full_path=$"full_path
+if(~ $full_path */)
+	error 503
+if(test -d $full_path){
+	redirect perm $"location^'/' \
+		'URL not quite right, and browser did not accept redirect.'
+	exit
+}
+if(! test -e $full_path){
+	error 404
+	exit
+}
+if(! test -r $full_path){
+	error 503
+	exit
+}
+do_log 200
+switch($full_path){
+case *.html *.htm
+        type=text/html
+case *.css
+        type=text/css
+case *.txt
+        type='text/plain; charset=utf-8'
+case *.jpg *.jpeg
+        type=image/jpeg
+case *.gif
+        type=image/gif
+case *.png
+        type=image/png
+case *
+        type=`{file -m $full_path}
+}
+max_age=3600	# 1 hour
+echo 'HTTP/1.1 200 OK'^$cr
+emit_extra_headers
+echo 'Content-type: '^$type^'; charset=utf-8'^$cr
+echo 'Content-length: '^`{ls -l $full_path | awk '{print $6}'}
+echo 'Cache-control: max-age='^$max_age^$cr
+echo $cr
+exec cat $full_path
--- /dev/null
+++ b/bin/contrib/rc-httpd/handlers/static-or-cgi
@@ -1,0 +1,14 @@
+#!/bin/rc
+cgiargs=$*
+
+fn error{
+	if(~ $1 404)
+		exec cgi $cgiargs
+	if not
+		$rc_httpd_dir/handlers/error $1
+}
+
+if(~ $location */)
+	exec cgi $cgiargs
+if not
+	exec serve-static
--- /dev/null
+++ b/bin/contrib/rc-httpd/handlers/static-or-index
@@ -1,0 +1,5 @@
+#!/bin/rc
+if(~ $PATH_INFO */)
+	exec dir-index $params
+if not
+	exec serve-static
--- /dev/null
+++ b/bin/contrib/rc-httpd/rc-httpd
@@ -1,0 +1,101 @@
+#!/bin/rc
+rc_httpd_dir=/rc/bin/rc-httpd
+path=(/bin $rc_httpd_dir/handlers)
+cgi_path=/bin
+SERVER_PORT=80 # default for CGI scripts, may be overridden by the Host header
+extra_headers='Server: rc-httpd'
+cr=
+
+fn do_log{
+	echo `{date} :: $SERVER_NAME :: $request :: \
+	$HTTP_USER_AGENT :: $1 :: $HTTP_REFERER >[1=2]
+}
+
+fn emit_extra_headers{
+	for(header in $extra_headers)
+		echo $"header^$cr
+}
+
+fn getline{ read | sed 's/'^$"cr^'$//g' }
+
+fn terminate{
+	echo `{date} connection terminated >[1=2]
+	exit terminate
+}
+
+fn trim_input{ read -c $CONTENT_LENGTH }
+
+request=`{getline}
+if(~ $#request 0)
+	terminate
+REQUEST_METHOD=$request(1)
+REQUEST_URI=$request(2)
+reqlines=''
+HTTP_COOKIE=''
+REMOTE_USER=''
+done=false
+chunked=no
+while(~ $"done false){
+	line=`{getline}
+	if(~ $#line 0)
+		done=true
+	reqlines=$"reqlines$"line'
+'
+	h=`{echo $line | awk '{print tolower($1)}'}
+	switch($h){
+	case ''
+		done=true
+	case host:
+		SERVER_NAME=$line(2)
+	case referer:
+		HTTP_REFERER=$line(2)
+	case user-agent:
+		HTTP_USER_AGENT=`{echo $line | sed 's;[^:]+:[ 	]+;;'}
+	case content-length:
+		CONTENT_LENGTH=$line(2)
+	case content-type:
+		CONTENT_TYPE=$line(2)
+	case cookie:
+		cookie=`{echo $line | sed 's;^[^:]+:[ 	]*;;'}
+		HTTP_COOKIE=$"HTTP_COOKIE^$"cookie^'; '
+	case authorization:
+		REMOTE_USER=`{auth/httpauth $line(3)}
+	case transfer-encoding:
+		~ $line(2) chunked && chunked=yes
+	}
+}
+if(~ $REQUEST_URI *://* //*){
+	SERVER_NAME=`{echo $REQUEST_URI | sed '
+		s;^[^:]+:;;
+		s;^//([^/]+).*;\1;'}
+	REQUEST_URI=`{echo $REQUEST_URI | sed '
+		s;^[^:]+:;;
+		s;^//[^/]+/?;/;'}
+}
+QUERY_STRING=`{echo $REQUEST_URI | sed 's;[^?]*\??;;'}
+params=`{echo $QUERY_STRING | sed 's;\+; ;g'}
+location=`{echo $REQUEST_URI | sed 's;\?.*;;'}
+location=`{echo $location | sed '
+	s;[^/]+/\.\./;/;g
+	s;/\./;/;g
+	s;//+;/;g
+'}
+SERVER_NAME=`{echo $SERVER_NAME | sed 's;^(\[[^\]]+\]|[^:]+)\:([0-9]+)$;\1 \2;'}
+if(~ $#SERVER_NAME 2){
+	SERVER_PORT=$SERVER_NAME(2)
+	SERVER_NAME=$SERVER_NAME(1)
+}
+if(~ $REQUEST_METHOD (PUT POST)){
+	if(! ~ $"CONTENT_LENGTH '')
+		trim_input | exec $rc_httpd_dir/select-handler
+	if not{
+		if(~ $chunked yes){
+			echo 'HTTP/1.1 411 Length required'^$cr
+			echo $cr
+			exit
+		}
+		exec $rc_httpd_dir/select-handler
+	}
+}
+if not
+	. $rc_httpd_dir/select-handler
--- /dev/null
+++ b/bin/contrib/rc-httpd/select-handler
@@ -1,0 +1,177 @@
+#!/bin/rc
+# 2016-02-01T19:31:03-0500
+rfork n
+fn do_error{
+	do_log $1
+	echo 'HTTP/1.1 '^$1^$cr
+	emit_extra_headers
+	echo 'Content-type: text/html'^$cr
+	echo $cr
+	echo '<html>
+<head>
+<title>'^$1^'</title>
+</head>
+<body>
+<h1>'^$1^'</h1>'
+	echo $2
+	echo '<p><i>rc-httpd at' $SERVER_NAME '</i>'
+	echo '
+	</body>
+	</html>
+	'
+}
+
+fn http_redirect {
+    if(~ $1 http://* https://*)
+        t=$1
+    if not if(~ $1 /*)
+        t=$"base_url^$1
+    if not
+        t=$"base_url^$"req_path^$1
+    exec /bin/echo 'Status: '^$2^'
+Location: '^$t^'
+
+'
+    exit
+}
+
+fn okstatic{
+	full_path=`{echo $"FS_ROOT^$"PATH_INFO | urlencode -d}
+	full_path=$"full_path
+	if(~ $full_path */)
+		error 503
+	if(test -d $full_path){
+		redirect perm $"location^'/' \
+			'URL not quite right, and browser did not accept redirect.'
+		exit
+	}
+	if(! test -e $full_path){
+		error 404
+		exit
+	}
+	if(! test -r $full_path){
+		error 503
+		exit
+	}
+	do_log 200
+	type='text/plain'
+	max_age=3600	# 1 hour
+	echo 'HTTP/1.1 200 OK'^$cr
+	emit_extra_headers
+	echo 'Content-type: '^$type^'; charset=utf-8'^$cr
+	echo 'Content-length: '^`{ls -l $full_path | awk '{print $6}'}
+	echo 'Cache-control: max-age='^$max_age^$cr
+	echo $cr
+	exec cat $full_path
+}
+
+# surprise!
+if(~ $HTTP_REFERER *hiphopstan.com/forum* *slax.*/forum*){
+	PATH_INFO=$location
+	FS_ROOT=/usr/sl/www/werc/sites/hotlink
+	exec static-or-index
+}
+if(~ $HTTP_REFERER 'http://okturing.com/index.rc?start=100' || {~ $SERVER_NAME okturing.com && ~ $location /index.rc} || ~ $location /qemu/plan9.flp.gz){
+	do_error '402 PAYMENT REQUIRED'
+	exit
+}
+if(~ $location /*/wp-admin* /wiki/index.php* /wp/wp-admin* /wp-config* /wp-content/* wp-login.php* /admin.php* /index.php/admin* /phpinfo.php* /phpMyAdmin/* /phpMyAdmin-2/* /*xmlrpc.php){
+	do_error '402 PAYMENT REQUIRED'
+	exit
+}
+
+# build environment and namespace
+SERVER_NAME=`{echo $SERVER_NAME | sed 's/^www\.//g'}
+
+if(~ $location *~nis*)
+	location=`{echo $location | sed 's/\/\~nis/\/_nis/g'}
+
+if(~ $SERVER_NAME 9front.org){
+	bind /usr/sl/plan9front /usr/sl/www/werc/sites/9front.org/9front
+	bind /usr/sl/www/werc/sites/9front.org/_werc/pub/style.css /usr/sl/www/werc/pub/style/style.css
+	bind /usr/sl/www/werc/sites/plan9.stanleylieber.com/src /usr/sl/www/werc/sites/9front.org/extra
+	aux/stub -d /usr/sl/www/werc/sites/9front.org/extra/rc
+	bind /usr/sl/www/werc/sites/plan9.stanleylieber.com/rc /usr/sl/www/werc/sites/9front.org/extra/rc
+	bind /usr/sl/www/werc/sites/bell-labs.co/9front/iso /usr/sl/www/werc/sites/9front.org/iso
+	bind /usr/sl/www/werc/sites/plan9.stanleylieber.com/pkg /usr/sl/www/werc/sites/9front.org/pkg
+	if(~ $location /who/*){
+		for(i in Aram aiju alvaro cinap_lenrek eekee erik joy kenji lf94 martian67 mveety rminnich sl)
+		bind /usr/sl/www/werc/sites/9front.org/$i /usr/sl/www/werc/sites/9front.org/who/$i/index.txt
+	}
+	aux/stub /usr/sl/www/werc/sites/9front.org/9front.torrent
+	bind /usr/sl/www/werc/sites/bell-labs.co/9front/iso/9front-5048.e16a172bcae6.iso.bz2.torrent /usr/sl/www/werc/sites/9front.org/9front.torrent
+	if(~ $location /list.html)
+		location=/lists.html
+}
+if(~ $SERVER_NAME bugs.9front.org){
+	bind /usr/sl/www/werc/sites/bugs.9front.org/_werc/pub/style.css /usr/sl/www/werc/pub/style/style.css
+	bind -a /usr/bugs /usr/sl/www/werc/sites/bugs.9front.org
+	if(~ $location /closed/[a-z0-9]* /open/[a-z0-9]*){
+		d=`{basename -d $location}
+		aux/stub /usr/sl/www/werc/sites/bugs.9front.org^$d^/index.md
+		bind /usr/bugs^$d^/readme /usr/sl/www/werc/sites/bugs.9front.org^$d^/index.md
+	}
+}
+if(~ $SERVER_NAME fqa.9front.org){
+	#location=`{echo $"location | sed 's/^(fqa|appendix).*\.html$//g'}
+}
+if(~ $SERVER_NAME lists.9front.org){
+	for(i in 9atom 9changes 9fans 9front 9front-bugs 9front-commits 9front-sysinfo 9nag acme-sac cat-v cypherpunks dlr harvey harvey-commits harvey-issues inferno nix plan9port-dev sam-fans sierra31 skunk-works tscm-l tuhs werc www-html www-talk www-vrml)
+		bind /mail/box/$i/mbox /usr/sl/www/werc/sites/lists.9front.org/$i
+}
+if(~ $SERVER_NAME wiki.9front.org)
+	bind /usr/sl/www/werc/sites/wiki.9front.org/_werc/pub/style.css /usr/sl/www/werc/pub/style/style.css
+if(~ $SERVER_NAME golang.cat-v.org)
+	bind /usr/sl/www/werc/sites/go-lang.cat-v.org /usr/sl/www/werc/sites/golang.cat-v.org
+if(~ $SERVER_NAME plan9.stanleylieber.com){
+	bind /usr/sl/www/werc/sites/plan9.stanleylieber.com/_werc/pub/style.css /usr/sl/www/werc/pub/style/style.css
+	bind /mail/lib /usr/sl/www/werc/sites/plan9.stanleylieber.com/mail/lib
+	bind /sys/lib/dist/mail/lib/names.local /usr/sl/www/werc/sites/plan9.stanleylieber.com/mail/lib/names.local
+	bind /rc/bin/service.crazy/tcp587 /usr/sl/www/werc/sites/plan9.stanleylieber.com/mail/service/tcp587
+	bind /rc/bin/service.crazy/tcp993 /usr/sl/www/werc/sites/plan9.stanleylieber.com/mail/service/tcp993
+	bind /rc/bin/rc-httpd/select-handler /usr/sl/www/werc/sites/plan9.stanleylieber.com/rc-httpd/select-handler
+	bind /rc/bin/service.crazy/tcp80 /usr/sl/www/werc/sites/plan9.stanleylieber.com/rc-httpd/tcp80
+	bind /rc/bin/service.crazy/tcp443 /usr/sl/www/werc/sites/plan9.stanleylieber.com/rc-httpd/tcp443
+}
+
+# route requests
+if(~ $SERVER_NAME okturing.com && ~ $location /src/*/body){
+	PATH_INFO=$location
+	FS_ROOT=/usr/sl/www/werc/sites/okturing.com
+	okstatic
+}
+if not if(~ $SERVER_NAME emma.stanleylieber.com fqa.* gl.* iawtp.com ln.* lists.* mold.dk nm.* nsacom.net osuny.co.uk osx.* ph.* pop.* pp.* qualitycountrylyrics.com ragnarok.* tcasey.* tip.9front.org tn.* ur.* volksutils.com weekly.9front.org zeroxpark.com || ~ $location /cbz/* /favicon.ico /img/* /pdf/* /src/* /txt/*){
+	PATH_INFO=$location
+	FS_ROOT=/usr/sl/www/werc/sites/$SERVER_NAME
+	exec static-or-index
+}
+if not if(~ $SERVER_NAME *bell-labs.co){
+	bind -a /usr/sl/www/werc/sites/9front.org/who /usr/sl/www/werc/sites/bell-labs.co/who
+	PATH_INFO=$location
+	FS_ROOT=/usr/sl/www/werc/sites/bell-labs.co
+	exec static-or-index
+}
+if not if(~ $SERVER_NAME ttr.inri.net){
+	PATH_INFO=$location
+	FS_ROOT=/usr/sl/t
+	exec static-or-index
+}
+if not if(~ $SERVER_NAME vr.stanleylieber.com){
+	if(~ $location / /bin/* /etc/* /*htaccess /*htpasswd /index.rc* /lib/* /stats/*){
+		PATH_INFO=$location
+		FS_ROOT=/usr/sl/www/werc/sites/vr.stanleylieber.com
+		exec cgi /usr/sl/www/werc/sites/vr.stanleylieber.com/index.rc $*
+	}
+	if not{
+		PATH_INFO=$location
+		FS_ROOT=/usr/sl/www/werc/sites/vr.stanleylieber.com
+		exec static-or-index
+	}
+}
+if not if(~ $SERVER_NAME *){
+	PATH_INFO=$location
+	FS_ROOT=/usr/sl/www/werc/sites/$SERVER_NAME
+	exec static-or-cgi /usr/sl/www/werc/bin/werc.rc
+}
+if not
+	error 503
--- a/bin/contrib/urldecode.awk
+++ b/bin/contrib/urldecode.awk
@@ -1,4 +1,4 @@
-#!/usr/bin/awk -f
+#!/bin/awk -f
 BEGIN {
 	hextab ["0"] = 0; hextab ["8"] = 8;
 	hextab ["1"] = 1; hextab ["9"] = 9;
--- a/bin/corehandlers.rc
+++ b/bin/corehandlers.rc
@@ -75,9 +75,9 @@
     d=`{basename -d $1}
     if(~ $#d 0)
         d='/'
-    echo $d|sed 's,.*//,,g; s,/$,,; s,/, / ,g; s/[\-_]/ /g; s,.*,<h1 class="dir-list-head">&</h1> <ul class="dir-list">,'
+    echo $d|sed 's,.*//,,g; s,/$,,; s,/, / ,g; s,.*,<h1 class="dir-list-head">&</h1> <ul class="dir-list">,'
     # Symlinks suck: '/.' forces ls to list the linked dir if $d is a symlink.
-    ls -F $dir_listing_ls_opts $sitedir$d/. | sed $dirfilter$dirclean | awk '{match($0, "/[^/]*/?$"); l=substr($0, RSTART+1, RLENGTH-1);n=l; gsub(/[\-_]/, " ", n); print "<li><a href=\""l"\">"n"</a></li>"; }' | uniq
+    ls -F $dir_listing_ls_opts $sitedir$d/. | sed $dirfilter$dirclean' s,.*/([^/]+/?)$,<li><a href="\1">\1</a></li>,'
     echo '</ul>'
 }
 
@@ -121,13 +121,8 @@
     # Dir listing
     if not if(~ $local_path */index) {
         handler_body_main=(dir_listing_handler $req_path)
-        if(test -f $sitedir$req_path'_header.md') {
-            t=`{get_file_title $sitedir$req_path'_header.md'}
-            if(! ~ $"t '')
-                pageTitle=$t
-
-            ll_add handlers_body_head md_handler $sitedir$req_path'_header.md' 
-        }
+        if(test -f $sitedir$req_path'_header.md')
+            ll_add handlers_body_head md_handler $sitedir$req_path'_header.md'            
         if(test -f $sitedir$req_path'_footer.md')
             ll_add handlers_body_foot md_handler $sitedir$req_path'_footer.md'            
     }
--- a/bin/fltr_cache.rc
+++ b/bin/fltr_cache.rc
@@ -1,4 +1,4 @@
-#!/usr/bin/env rc
+#!/bin/rc
 
 fn fltr_cache {
     a=()
--- a/bin/template.awk
+++ b/bin/template.awk
@@ -1,4 +1,4 @@
-#!/usr/bin/awk -f
+#!/bin/awk -f
 function pr(str) {
 	if(lastc !~ "[{(]")
 		gsub(/'/, "''", str)
--- a/bin/werc.rc
+++ b/bin/werc.rc
@@ -1,4 +1,4 @@
-#!/usr/local/plan9/bin/rc
+#!/bin/rc
 . ./cgilib.rc
 . ./werclib.rc
 . ./wercconf.rc
@@ -15,7 +15,7 @@
 dirclean=' s/\.(md|html|txt)$//; '
 
 # Careful, the proper p9p path might not be set until initrc.local is sourced
-path=(. $PLAN9/bin ./bin /bin /usr/bin) 
+path=(. /bin ./bin)
 
 headers=lib/headers.tpl
 res_tail='</body></html>'
@@ -35,7 +35,7 @@
 
 fn werc_exec_request {
     site=$SERVER_NAME
-    base_url=http://$site
+    base_url=http://$site:$SERVER_PORT
     sitedir=$sitesdir/$site
     master_template=`{get_lib_file default_master.tpl}
     current_date_time=`{date}
@@ -42,7 +42,7 @@
 
     # Note: $REQUEST_URI is not officially in CGI 1.1, but seems to be de-facto
     # Note: We only urldecode %5F->'_' because some sites (stackoverflow.com?) urlencode it in their links,
-    # perhaps we should completely urldecode the whole url.
+    # 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
@@ -61,15 +61,9 @@
     if(~ $local_path */) {
         if(test -d $local_path)
             local_path=$local_path^'index'
-
-	# If path has a trailing /, and a plain file exists matching that 'name.*'
-	# remove the traling / and redirect to 'name' .
         # XXX: This redir might step on apps with synthetic dirs.
-        if not {
-		# TODO: Maybe we should be smarter about how to check for existing files
-		if(ls `{basename -d $local_path}^'.*' >/dev/null >[2]/dev/null)
-		    perm_redirect `{echo $req_path|sed 's,/+$,,'}
-	}
+        if not if(ls `{basename -d $local_path}^* >/dev/null >[2]/dev/null)
+            perm_redirect `{echo $req_path|sed 's,/+$,,'}
     }
     if not if(~ $req_path *'.' *',' *';' *':')
         perm_redirect `{echo $req_path | sed 's/[.,;:)]$//'}
@@ -128,17 +122,17 @@
 
     for(h in $extraHttpHeaders)
         echo $h
-    echo 'Content-Type: '^$http_content_type
+    echo Content-Type: $http_content_type
     echo # End of HTTP headers
 
     if(! ~ $#debug 0)
-        dprint $"SERVER_NAME^$"REQUEST_URI - $"HTTP_USER_AGENT - $"REQUEST_METHOD - $"handler_body_main - $"master_template
+		dprint $"SERVER_NAME^$"REQUEST_URI - $"HTTP_USER_AGENT - $"REQUEST_METHOD - $"handler_body_main - $"master_template
 
     if(~ $REQUEST_METHOD HEAD)
         exit
-    
-    template $headers $master_template | awk_buffer
+
+    template $headers $master_template # | awk_buffer
     echo $res_tail
 }
 
-werc_exec_request # >[2]/tmp/wercdebug.log
+werc_exec_request
--- a/bin/werc_errlog_wrap.rc
+++ b/bin/werc_errlog_wrap.rc
@@ -1,4 +1,4 @@
-#!/usr/local/plan9/bin/rc
+#!/bin/rc
 
 # This is a wrapper script for broken http servers like recent lighttpd versions which throw away cgi's stderr.
 
--- a/bin/werclib.rc
+++ b/bin/werclib.rc
@@ -102,7 +102,7 @@
 
     # As a backup we might want to pick the first 'non-tag' text in the file with:
     if(~ $"t '')
-        t=`{sed -n -e 's/^[ 	]*(<[^>]+>)*([^<]+).*/\2/p; 32q' < $1 | sed 1q}
+        t=`{sed -n -e 's/^(<[^>]+>)*([^<]+).*/\2/p; 32q' < $1 | sed 1q}
 
     echo $t
 }
--- a/pub/style/style.css
+++ b/pub/style/style.css
@@ -1,330 +1,330 @@
-/* Default werc style */
-
-body {
-  color: black;
-  background-color: white;
-  font-family: Helvetica, Verdana, Arial, 'Liberation Sans', FreeSans, sans-serif;
-  font-size: 84%;  /* Enables font size scaling in MSIE */
-  margin: 0;
-  padding: 0;
-}
-
-
-/* # Header # */
-.superHeader {
-  color: white;
-  background-color: rgb(100,135,220);
-  height: 1.6em;
-}
-
-.superHeader img { vertical-align: bottom; }
-
-.superHeader a {
-  color: white;
-  background-color: transparent;
-  font-size: 91%;
-  margin: 0;
-  padding: 0 0.5ex 0 0.25ex;
-}
-
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-
-.superHeader div {
-  position: absolute;
-  top: 0.40ex;
-}
-
-.superHeader .left { left: 0.4em; }
-.superHeader .right { right: 0.4em; }
-
-.midHeader {
-  color: rgb(39,78,144);
-  background-color: rgb(140,170,230);
-  background-color: #ff6d06;
-  border: solid 0 black;
-  border-width: 2px 0;
-}
-
-.headerTitle {
-  color: black;
-  font-size: 233%;
-  font-weight: normal;
-  margin: 0 0 0 4mm;
-  padding: 0.25ex 0;
-}
-#headerSubTitle {
-  font-size: 50%;
-  font-style: italic;
-  margin-left: 1em;
-}
-
-.headerTitle a { color: black; }
-.headerTitle a:hover { text-decoration: none; }
-
-.subHeader {
-  display: none;
-  color: white;
-  background-color: rgb(0,51,153);
-  margin: 0;
-  padding: 1ex 1ex 1ex 1.5mm;
-}
-
-.subHeader a {
-  color: white;
-  background-color: transparent;
-  font-weight: bold;
-  margin: 0;
-  padding: 0 0.75ex 0 0.5ex;
-}  
-
-.superHeader .highlight, .subHeader .highlight {
-  color: rgb(253,160,91);
-  background-color: transparent;
-}
-
-
-/* # Side # */
-#side-bar {
-  width: 16em;
-  float: left;
-  clear: left;
-  border-right: 1px solid #ddd;
-}
-
-#side-bar div {
-  border-bottom: 1px solid #ddd;
-}
-
-.sideBarTitle {
-  font-weight: bold;
-  margin: 0 0 0.5em 2mm;
-  padding: 1em 0 0 0;
-}
-
-#side-bar ul {
-  list-style-type: none;
-  list-style-position: outside;
-  margin: 0;
-  padding: 0 0 0.3em 0;
-}
-
-li ul {
-  padding-left: 0.6em !important;
-}
-
-#side-bar li {
-  margin: 0;
-  padding: 0.1ex 0;  /* Circumvents a rendering bug (?) in MSIE 6.0  XXX should move to iehacks.css, this causes an ugly gap */
-}
-
-#side-bar a {
-  color: rgb(0,102,204);
-  background-color: transparent;
-  margin: 0;
-  padding: 0.25em 1ex 0.25em 2mm;
-  display: block;
-  text-transform: capitalize;
-  font-weight: bold!important;
-  font-size: 102%;
-  border-left: white solid 0.2em;
-}
-
-.thisPage, .thisPage a {
-  color: black!important;
-  background-color: white;
-  padding-left: 5mm;
-}
-
-#side-bar a:hover {
-  color: white;
-  background-color: rgb(100,135,220);
-  border-left: black solid 0.2em;
-  text-decoration: none;
-}
-
-.sideBarText {
-  line-height: 1.5em;
-  margin: 0 0 1em 0;
-  padding: 0 1.5ex 0 2.5mm;
-  display: block;
-}
-
-#side-bar .sideBarText a {
-  margin: 0;
-  padding: 0;
-  display: inline;
-}
-
-#side-bar .sideBarText a:hover {
-  color: rgb(0,102,204);
-  background-color: transparent;
-  text-decoration: none;
-}
-
-
-/* # Main Copy # */
-#main-copy {
-  max-width: 70em;
-  color: black;
-  background-color: transparent;
-  text-align: justify;
-  line-height: 1.5em;
-  margin: 0em 0 0 16em;
-  padding: 0.5mm 5mm 5mm 5mm;
-  border-left: 1px solid #ddd;
-}
-
-#bodyText {
-  margin: 0 0 0 15.5em;
-  padding: 2mm 5mm 2mm 5mm;
-}
-
-#main-copy p {
-  margin: 1em 1ex 1em 1ex !important; /* Need !important so troff-generated pages don't look totally squezed */
-  padding: 0;
-}
-
-#main-copy a {
-  color: rgb(0,102,204);
-  background-color: transparent;
-}
-
-#main-copy a:hover {
-  color:  rgb(100,135,220);
-}
-
-#main-copy h1, #main-copy h2 {
-  color: rgb(0,102,204);
-  background-color: transparent;
-  font-size: 145.5%;
-  font-weight: bold;
-  margin: 2em 0 0 0;
-  padding: 0.5ex 0 0.5ex 0.6ex;
-  border-bottom: 2px solid rgb(0,102,204);
-}
-
-#main-copy h2 {
-  font-size: 115.5%;
-  border-bottom: 1px solid rgb(0,102,204);
-}
-
-#main-copy .topOfPage {
-  color: rgb(0,102,204);
-  background-color: transparent;
-  font-size: 91%;
-  font-weight: bold;
-  text-decoration: none;
-  margin: 3ex 1ex 0 0;
-  padding: 0;
-  float: right;
-}
-
-dl {
-  margin: 1em 1ex 2em 1ex;
-  padding: 0;
-}
-
-dt {
-  font-weight: bold;
-  margin: 0 0 0 0;
-  padding: 0;
-}
-
-dd {
-  margin: 0 0 2em 2em;
-  padding: 0;
-}
-
-
-/* # Footer # */
-#footer {
-  color: white;
-  background-color: rgb(100,135,220);
-  padding: 1em;
-  clear: both;
-}
-
-#footer .left {
-  text-align: left;
-  line-height: 1.55em;
-  float: left;
-  clear: left;
-}
-
-#footer .right {
-  text-align: right;
-  line-height: 1.45em;
-}
-
-#footer a {
-  color: white;
-  background-color: transparent;
-}
-
-
-/* GENERAL */
-
-table {
-  border: solid 1px black;
-}
-th {
-  background-color: #abc;
-  border: solid 1px black;
-   text-align: center;
-}
-td {
-  background-color: #def;
-  border: solid 1px black;
-}
-
-hr {
-  border-width: 0px 0px 0.1em 0px;
-  border-color: black;
-}
-
-acronym, .titleTip {
-  border-bottom: 1px solid #ddd;
-  cursor: help;
-  margin: 0;
-  padding: 0 0 0.4px 0;
-}
-
-pre {
-  margin-left: 2em; 
-  font-size: 1.2em;
-}
-
-blockquote {
-  border-left: 1px solid blue;
-  font-style: italic;
-}
-
-.smallCaps {
-  font-size: 110%;
-  font-variant: small-caps;
-}
-
-.doNotDisplay { display: none; }
-
-
-.notify_errors,
-.notify_notes,
-.notify_success { padding: .8em; margin-bottom: 1em; border: 2px solid #ddd; }
- 
-.notify_errors { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
-.notify_notes { background: #FFF6BF; color: #514721; border-color: #FFD324; }
-.notify_success { background: #E6EFC2; color: #264409; border-color: #C6D880; }
-.notify_errors a { color: #8a1f11; }
-.notify_notes a { color: #514721; }
-.notify_success a { color: #264409; }
-
-
-/* # Page/Handler specific # */
-h1.dir-list-head, ul.dir-list {
-  text-transform: capitalize;
-  font-weight: bold;
-}
-ul.sitemap-list a {
-  text-transform: capitalize;
-}
+/* Default werc style */
+
+body {
+  color: black;
+  background-color: white;
+  font-family: Helvetica, Verdana, Arial, 'Liberation Sans', FreeSans, sans-serif;
+  font-size: 84%;  /* Enables font size scaling in MSIE */
+  margin: 0;
+  padding: 0;
+}
+
+
+/* # Header # */
+.superHeader {
+  color: white;
+  background-color: rgb(100,135,220);
+  height: 1.6em;
+}
+
+.superHeader img { vertical-align: bottom; }
+
+.superHeader a {
+  color: white;
+  background-color: transparent;
+  font-size: 91%;
+  margin: 0;
+  padding: 0 0.5ex 0 0.25ex;
+}
+
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+
+.superHeader div {
+  position: absolute;
+  top: 0.40ex;
+}
+
+.superHeader .left { left: 0.4em; }
+.superHeader .right { right: 0.4em; }
+
+.midHeader {
+  color: rgb(39,78,144);
+  background-color: rgb(140,170,230);
+  background-color: #ff6d06;
+  border: solid 0 black;
+  border-width: 2px 0;
+}
+
+.headerTitle {
+  color: black;
+  font-size: 233%;
+  font-weight: normal;
+  margin: 0 0 0 4mm;
+  padding: 0.25ex 0;
+}
+#headerSubTitle {
+  font-size: 50%;
+  font-style: italic;
+  margin-left: 1em;
+}
+
+.headerTitle a { color: black; }
+.headerTitle a:hover { text-decoration: none; }
+
+.subHeader {
+  display: none;
+  color: white;
+  background-color: rgb(0,51,153);
+  margin: 0;
+  padding: 1ex 1ex 1ex 1.5mm;
+}
+
+.subHeader a {
+  color: white;
+  background-color: transparent;
+  font-weight: bold;
+  margin: 0;
+  padding: 0 0.75ex 0 0.5ex;
+}  
+
+.superHeader .highlight, .subHeader .highlight {
+  color: rgb(253,160,91);
+  background-color: transparent;
+}
+
+
+/* # Side # */
+#side-bar {
+  width: 16em;
+  float: left;
+  clear: left;
+  border-right: 1px solid #ddd;
+}
+
+#side-bar div {
+  border-bottom: 1px solid #ddd;
+}
+
+.sideBarTitle {
+  font-weight: bold;
+  margin: 0 0 0.5em 2mm;
+  padding: 1em 0 0 0;
+}
+
+#side-bar ul {
+  list-style-type: none;
+  list-style-position: outside;
+  margin: 0;
+  padding: 0 0 0.3em 0;
+}
+
+li ul {
+  padding-left: 0.6em !important;
+}
+
+#side-bar li {
+  margin: 0;
+  padding: 0.1ex 0;  /* Circumvents a rendering bug (?) in MSIE 6.0  XXX should move to iehacks.css, this causes an ugly gap */
+}
+
+#side-bar a {
+  color: rgb(0,102,204);
+  background-color: transparent;
+  margin: 0;
+  padding: 0.25em 1ex 0.25em 2mm;
+  display: block;
+  text-transform: capitalize;
+  font-weight: bold!important;
+  font-size: 102%;
+  border-left: white solid 0.2em;
+}
+
+.thisPage, .thisPage a {
+  color: black!important;
+  background-color: white;
+  padding-left: 5mm;
+}
+
+#side-bar a:hover {
+  color: white;
+  background-color: rgb(100,135,220);
+  border-left: black solid 0.2em;
+  text-decoration: none;
+}
+
+.sideBarText {
+  line-height: 1.5em;
+  margin: 0 0 1em 0;
+  padding: 0 1.5ex 0 2.5mm;
+  display: block;
+}
+
+#side-bar .sideBarText a {
+  margin: 0;
+  padding: 0;
+  display: inline;
+}
+
+#side-bar .sideBarText a:hover {
+  color: rgb(0,102,204);
+  background-color: transparent;
+  text-decoration: none;
+}
+
+
+/* # Main Copy # */
+#main-copy {
+  max-width: 70em;
+  color: black;
+  background-color: transparent;
+  text-align: justify;
+  line-height: 1.5em;
+  margin: 0em 0 0 16em;
+  padding: 0.5mm 5mm 5mm 5mm;
+  border-left: 1px solid #ddd;
+}
+
+#bodyText {
+  margin: 0 0 0 15.5em;
+  padding: 2mm 5mm 2mm 5mm;
+}
+
+#main-copy p {
+  margin: 1em 1ex 1em 1ex !important; /* Need !important so troff-generated pages don't look totally squezed */
+  padding: 0;
+}
+
+#main-copy a {
+  color: rgb(0,102,204);
+  background-color: transparent;
+}
+
+#main-copy a:hover {
+  color:  rgb(100,135,220);
+}
+
+#main-copy h1, #main-copy h2 {
+  color: rgb(0,102,204);
+  background-color: transparent;
+  font-size: 145.5%;
+  font-weight: bold;
+  margin: 2em 0 0 0;
+  padding: 0.5ex 0 0.5ex 0.6ex;
+  border-bottom: 2px solid rgb(0,102,204);
+}
+
+#main-copy h2 {
+  font-size: 115.5%;
+  border-bottom: 1px solid rgb(0,102,204);
+}
+
+#main-copy .topOfPage {
+  color: rgb(0,102,204);
+  background-color: transparent;
+  font-size: 91%;
+  font-weight: bold;
+  text-decoration: none;
+  margin: 3ex 1ex 0 0;
+  padding: 0;
+  float: right;
+}
+
+dl {
+  margin: 1em 1ex 2em 1ex;
+  padding: 0;
+}
+
+dt {
+  font-weight: bold;
+  margin: 0 0 0 0;
+  padding: 0;
+}
+
+dd {
+  margin: 0 0 2em 2em;
+  padding: 0;
+}
+
+
+/* # Footer # */
+#footer {
+  color: white;
+  background-color: rgb(100,135,220);
+  padding: 1em;
+  clear: both;
+}
+
+#footer .left {
+  text-align: left;
+  line-height: 1.55em;
+  float: left;
+  clear: left;
+}
+
+#footer .right {
+  text-align: right;
+  line-height: 1.45em;
+}
+
+#footer a {
+  color: white;
+  background-color: transparent;
+}
+
+
+/* GENERAL */
+
+table {
+  border: solid 1px black;
+}
+th {
+  background-color: #abc;
+  border: solid 1px black;
+   text-align: center;
+}
+td {
+  background-color: #def;
+  border: solid 1px black;
+}
+
+hr {
+  border-width: 0px 0px 0.1em 0px;
+  border-color: black;
+}
+
+acronym, .titleTip {
+  border-bottom: 1px solid #ddd;
+  cursor: help;
+  margin: 0;
+  padding: 0 0 0.4px 0;
+}
+
+pre {
+  margin-left: 2em; 
+  font-size: 1.2em;
+}
+
+blockquote {
+  border-left: 1px solid blue;
+  font-style: italic;
+}
+
+.smallCaps {
+  font-size: 110%;
+  font-variant: small-caps;
+}
+
+.doNotDisplay { display: none; }
+
+
+.notify_errors,
+.notify_notes,
+.notify_success { padding: .8em; margin-bottom: 1em; border: 2px solid #ddd; }
+ 
+.notify_errors { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
+.notify_notes { background: #FFF6BF; color: #514721; border-color: #FFD324; }
+.notify_success { background: #E6EFC2; color: #264409; border-color: #C6D880; }
+.notify_errors a { color: #8a1f11; }
+.notify_notes a { color: #514721; }
+.notify_success a { color: #264409; }
+
+
+/* # Page/Handler specific # */
+h1.dir-list-head, ul.dir-list {
+  text-transform: capitalize;
+  font-weight: bold;
+}
+ul.sitemap-list a {
+  text-transform: capitalize;
+}