include werc

This commit is contained in:
xfnw 2022-06-30 11:40:21 -04:00
parent 58fe4bd42c
commit 217f8d61a1
73 changed files with 5835 additions and 0 deletions

79
werc/README Normal file
View File

@ -0,0 +1,79 @@
werc - a minimalist document management system
----------------------------------------------
Werc is a content management system and web (anti-)framework designed to be simple to
use, simple to setup, simple to hack on, and not get in the way while allowing
users easy customization.
For more information see the official website: http://werc.cat-v.org/
Installation
------------
Requirements:
* An http server that can handle CGIs
* Plan 9 from User Space: http://swtch.com/plan9port,
Or 9base-tip: http://tools.suckless.org/9base,
Or frontbase: http://openbsd.stanleylieber.com/frontbase
Note: Werc by default expects the Plan 9 tools to be installed under
/bin/, if you have installed them elsewhere you will need to edit the
#! line in bin/werc.rc and customize the $plan9port variable in your
etc/initrc.local.
Instructions:
Untar werc at your desired location, configure httpd to use
/path-to-your-werc-installation/bin/werc.rc as a cgi-script, it is recommended
that you make werc.rc handle all non-static files (this can be done by setting
it up as your 404 handler) and setup your virtual hosts to handle static files
by setting the document root for the domain to
/path-to-werc-installation/sites/yourdomain.com/, and create a directory for
your web site under sites/ where you can start adding content right away.
If you will want to allow updates via the web interface (eg., for wiki or
comments apps) make sure all files under sites/ are writable by the user your
cgi will run as, usually www-data, for example by doing: chown -R :www-data
sites/; chmod -R g+w sites/
If your Plan 9 binaries are located somewhere else than the standard /bin/ you
will need to edit the first line of bin/werc.rc (Note that p9p in particular is
picky about where it is located, once you run ./INSTALL you should *not* move
it to a different directory without running ./INSTALL again.)
For general configuration options copy etc/initrc to etc/initrc.local and
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.
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.
For more details see the documentation section of the website:
http://werc.cat-v.org/docs/
Contact
-------
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.
Thanks
------
Garbeam, Kris Maglione, sqweek, soul9, mycroftiv, maht, yiyus, cinap_lenrek,
khm and many others for their ideas, patches, testing and other contributions.
License
-------
Werc is in the public domain.

142
werc/apps/blagh/app.rc Normal file
View File

@ -0,0 +1,142 @@
fn conf_enable_blog {
blagh_uri=$conf_wd
blagh_dirs=$*
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
}
fn blagh_init {
if(~ $#blagh_dirs 0 && ~ $req_path */[bB]log/*) {
blagh_uri=`{echo $req_path | sed 's,(/[bB]log/).*,\1,'}
blagh_dirs=( . )
}
# Should not match sub-dirs!
if(! ~ $#blagh_dirs 0) {
# && test -d / `{echo '-a -d '^$blagh_root^$blagh_dirs}
blagh_url=$base_url^$blagh_uri
blagh_root=$sitedir^$blagh_uri
if(check_user $conf_blog_editors) {
editor_mode=on
if(~ $"post_arg_date '')
post_date=`{datei|sed 's,-,/,g'}
if not
post_date=$post_arg_date
ll_add handlers_bar_left echo '<a href="'$blagh_uri'new_post">Make a new post</a>'
}
if(~ $req_path $blagh_uri) {
handler_body_main=blagh_body
u=$blagh_uri'index'
extraHeaders=$"extraHeaders ^ \
'<link rel="alternate" type="application/atom+xml" title="ATOM" href="'$"u'.atom" />
<link rel="alternate" type="application/rss+xml" title="RSS" href="'$"u'.rss" />
<link rel="alternate" type="application/json" title="JSON" href="'$"blagh_uri'feed.json" />'
}
if not if(~ $req_path $blagh_uri^index.atom)
blagh_setup_feed_handlers atom.tpl 'application/atom+xml'
if not if(~ $req_path $blagh_uri^index.rss)
blagh_setup_feed_handlers rss20.tpl 'text/xml; charset=utf-8'
if not if(~ $req_path $blagh_uri^feed.json)
blagh_setup_feed_handlers jsonfeed.tpl 'application/json; charset=utf-8'
if not if(~ $req_path $blagh_uri^new_post && ! ~ $#editor_mode 0) {
handler_body_main=( tpl_handler `{get_lib_file blagh/new_post.tpl apps/blagh/new_post.tpl} )
if(~ $REQUEST_METHOD POST) {
if(mkbpost $"post_arg_body $"post_date $"post_arg_title $post_arg_id)
post_redirect $blagh_uri
if not
notify_errors=$status
}
}
}
}
fn blagh_setup_feed_handlers {
handler_body_main=NOT_USED_by_blagh_feeds
res_tail=()
http_content_type=$2
headers=()
master_template=apps/blagh/$1 # Should we allow tempalte override?
}
fn blagh_body {
if (! ~ $"blogTitle '')
echo '<h1>'$"blogTitle'</h1>'
# Direct links to feeds are disabled because they are not very useful, add clutter and might waste pagerank.
# An user can add this on their own using handlers_body_head anyway.
#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
echo # Needed extra \n so markdown doesn't mess up the formatting, probably can be done in sed.
} | $formatter
# XXX BUG! Markdown [references] break because multiple markdown documents are merged. Should format each blog post independently.
# TODO: use fltr_cache directly, that can fix the previous bug plus provide a perf boost by caching title generation.
}
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 -e 's,/+\|/+,/,' -e $conf_max_posts_per_page^'q'
}
fn mkbpost {
bptext=$1
bpdate=$2
bptitle=$3
bpid=$4
_status=()
if(~ $"bptext '')
_status=($_status 'You need to provide a post body.')
if(! ~ $"bpdate [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9])
_status=($_status 'Invalid date: '''^$"bpdate^'''') # XXX Should make semantic check.
if(~ $#_status 0) {
umask 002 # Let group write
if(! ~ $"bpid '')
bpid=`{echo -n '-'^$bpid | sed 's/'$forbidden_uri_chars'+/_/g; 1q'}
ddir=$blagh_root^$bpdate^'/'
n=`{ls $ddir >[2]/dev/null |wc -l}
mkdir -p $ddir/$"n^$"bpid/
{
if(! ~ $"bptitle '') {
echo $bptitle
echo '========================================='
}
# TODO: Enable metadata
#echo '* Posted:' `{date}
#if(! ~ $#logged_user 0)
# echo '* Author: '$logged_user
echo
echo $bptext
}> $ddir/$"n^$"bpid/index.md
# Experimental support for http://pubsubhubbub.googlecode.com/
if(! ~ $"conf_blog_pubsubdub_hub '') {
ifs='' { p=`{echo $req_url|sed 's/new_post$/index.atom/'|url_encode } }
dprint hget -p 'hub.mode=publish&hub.url='^$"p $conf_blog_pubsubdub_hub
hget -d -h -p 'hub.mode=publish&hub.url='^$"p $conf_blog_pubsubdub_hub >[1=2] &
}
}
status=$_status
}
fn strip_title_from_md_file {
sed '1N; /^.*\n===*$/N; /.*\n===*\n$/d'
}

58
werc/apps/blagh/atom.tpl Normal file
View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
%{
# See for more info:http://www.tbray.org/ongoing/When/200x/2005/07/27/Atomic-RSS
fn statpost {
f = $1
post_uri=$base_url^`{cleanname `{echo $f | sed -e 's!^'$sitedir'!!'}}^'/'
title=`{read $f/index.md}
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} } }
}
# rfc3339 date when feed was last updated.
fupdated = `{ndate -a `{date `{mtime `{ls $blagh_root$blagh_dirs/[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]/[0-9] | tail -1} | awk '{print $1}'}}}
%}
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:thr="http://purl.org/syndication/thread/1.0">
% if(! ~ $"conf_blog_pubsubdub_hub '') {
% echo '<link rel="hub" href="'$conf_blog_pubsubdub_hub'" />'
% }
<link rel="self" href="%($base_url^$req_path%)"/>
<id>%($base_url^$req_path%)</id>
<icon><![CDATA[/favicon.ico]]></icon>
<title><![CDATA[%($siteTitle%)]]></title>
<subtitle><![CDATA[%($siteSubTitle%)]]></subtitle>
<updated>%($fupdated%)</updated>
<link href="."/>
% for(f in `{get_post_list $blagh_root$blagh_dirs}) {
% 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><![CDATA[%($title%)]]></title>
% # <link rel="replies" href="2899.atom" thr:count="0"/>
<author><name><![CDATA[%($by%)]]></name></author>
<content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
<![CDATA[%($summary%)]]>
</div></content>
% # rfc3339 date when entry was last updated.
% eupdated=`{ndate -a `{date `{mtime $f | awk '{print $1}'}}}
<updated>%($eupdated%)</updated>
</entry>
% }
</feed>
% exit

20
werc/apps/blagh/convert.rc Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env rc
path=($PLAN9/bin/ $path)
for(p in *.md) {
echo
echo '========================='
echo p $p
pp=`{echo $p | sed 's/^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])[\-_](.*).md$/\1 \2 \3 \4/' }
echo pp $pp
d=$pp(1)^'/'^$pp(2)^'/'^$pp(3)^'/'^$pp(4)^'/'
mkdir -p $d
echo $pp(4) | sed -e 's/^[0-9]_//; s/_/ /g;' > $d/index.md
echo '=================================' >> $d/index.md
echo >> $d/index.md
cat $p >> $d/index.md
}

View File

@ -0,0 +1,35 @@
{
"version": "https://jsonfeed.org/version/1",
"title": "%($siteTitle%)",
"home_page_url": "%($"base_url%)",
"feed_url": "%($"base_url^$"req_path%)",
"items": [
%{
fn statpost {
f = $1
post_uri=$base_url^`{cleanname `{echo $f | sed -e 's!^'$sitedir'!!'}}^'/'
title=`{read $f/index.md}
#ifs=() { summary=`{cat $f/index.md | crop_text 1024 ... | $formatter } }
ifs=() { summary=`{cat $f/index.md | strip_title_from_md_file | ifs=$difs {$formatter| sed 's/"/\\"/g' | tr -d '\012' } } }
}
%}
% #for(f in `{get_post_list $blagh_root$blagh_dirs}) {
%
% postlist=`{get_post_list $blagh_root$blagh_dirs}
% postcount=0
% for(f in $postlist) {
% statpost $f
{
"id": "%($post_uri%)",
"url": "%($post_uri%)",
"title": "%($title%)",
"content_html": "%($summary%)"
}
% postcount = `{echo $postcount 1+p | dc}
% if (! ~ $#postlist $postcount) { echo , }
% }
]
}
% exit

View File

@ -0,0 +1,11 @@
<div>
% notices_handler
<form method="POST"><fieldset>
<legend>Submit a new blog post</legend>
<textarea cols="94" rows=16" name="body">%($"post_arg_body%)</textarea><br />
<label>Title: <input size="64" type="text" name="title" value="%($"post_arg_title%)" /></label>
<label>Id: <input size="8" type="text" name="id" value="%($"post_arg_id%)" /></label>
<label>Date: <input size="10" maxlength="10" type="text" name="date" value="%($"post_date%)" /></label>
<input type="submit" value="Post" />
</fieldset></form>
</div>

43
werc/apps/blagh/rss20.tpl Normal file
View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
%{
fn statpost {
f = $1
post_uri = `{echo $f | sed 's,^'$sitedir',,'}
title=`{read $f/index.md}
post_uri=$base_url^`{cleanname `{echo $f | sed -e 's!^'$sitedir'!!'}}^'/'
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} }}
}
%}
<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><![CDATA[%($siteTitle%)]]></title>
<link>%($base_url^$req_path%)</link>
<description><![CDATA[%($blogDesc%)]]></description>
<language>en-us</language>
<generator><![CDATA[Tom Duff's rc, and Kris Maglione's clever hackery]]></generator>
%{
# <webMaster>uriel99+rss@gmail.com (Uriel)</webMaster>
# rfc2822 last time channel content changed.
lbd=`{ndate -m `{date `{mtime `{ls $blagh_root$blagh_dirs/[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]/[0-9] | tail -1} | awk '{print $1}'}}}
echo '<lastBuildDate>'$"lbd'</lastBuildDate>'
# rfc2822 publication date for content in the channel.
pubdate=`{ndate -m}
for(f in `{get_post_list $blagh_root$blagh_dirs}){
statpost $f
%}
<item>
<title><![CDATA[%($title%)]]></title>
<author><![CDATA[%($by%)@noreply.cat-v.org (%($by%))]]></author>
<link>%($post_uri%)</link>
<guid isPermaLink="true">%($post_uri%)</guid>
<pubDate>%($pubdate%)</pubDate>
<description> %($summary%) </description>
</item>
% }
</channel>
</rss>

103
werc/apps/bridge/app.rc Executable file
View File

@ -0,0 +1,103 @@
comment_file_types=(md html)
fn conf_enable_comments {
if(~ $1 -n) {
allow_new_user_comments=yes
shift
}
if not if(~ $1 -a) {
bridge_anon_comments=yes
}
enable_comments=yes
groups_allowed_comments=$*
conf_enable_app bridge
}
fn bridge_init {
if(~ $#enable_comments 1 && ! ~ `{ls $local_path.$comment_file_types >[2]/dev/null|wc -l} 0) {
comments_dir=$sitedir$req_path'_werc/comments'
if(~ $REQUEST_METHOD GET && test -d $comments_dir)
ll_add handlers_body_foot template apps/bridge/comments_list.tpl
if(check_user $groups_allowed_comments || {~ $#logged_user 0 && ~ 1 $#allow_new_user_comments $#bridge_anon_comments}) {
if(~ $#post_arg_bridge_post 1) {
ll_add handlers_body_foot template apps/bridge/foot.tpl
if(mk_new_comment $comments_dir)
post_redirect $base_url^$req_path
if not
saved_comment_text=$post_arg_comment_text
}
if not if(~ $REQUEST_METHOD GET)
ll_add handlers_body_foot template apps/bridge/foot.tpl
}
if not if(~ $REQUEST_METHOD GET)
ll_add handlers_body_foot echo '<hr><p>To post a comment you need to <a href="/_users/login">login</a> first.</p>'
}
}
fn validate_new_user {
usr=$1; pass=$2; pass2=$3
_status=()
if(~ $"usr '' || ! echo $usr |sed 1q|grep -s '^'$allowed_user_chars'+$')
_status='Requested user name is invalid, must match: '^$allowed_user_chars^'+'
if not if(test -d etc/users/$usr)
_status='Sorry, user name '''^$usr^''' already taken, please pick a different one.'
if(~ $"pass '' || ! ~ $"pass $"pass2)
_status=($_status 'Provided passwords don''t match.')
status=$_status
}
fn mk_new_comment {
_status=()
dir=$1
if(~ $"post_arg_comment_text '')
_status='Provide a comment!'
if not if(~ $#logged_user 0) {
if(! ~ $#allow_new_user_comments 0) {
if(validate_new_user $"post_arg_comment_user $post_arg_comment_passwd $post_arg_comment_passwd2) {
u=$post_arg_comment_user':'$post_arg_comment_passwd
dir=$comments_dir^'_pending'
# XXX: This doesn't work because we then do a redirect.
notify_notes='Saved comment and registration info, they will be enabled when approved by an admin.'
}
if not
_status=$status
}
if not if(! ~ $#bridge_anon_comments 0) {
if(~ $"post_arg_ima_robot 'not')
u='Glenda' # Anonymous
if not
_status='You are a robot!'
}
if not
_status='You need to log in to comment.'
}
if not if(check_user $groups_allowed_comments)
u=$logged_user
if not
_status='You are not a member of a group allowed to comment.'
if(~ $#_status 0) {
umask 002
dir=$dir'/'`{date -n} # FIXME Obvious race
mkdir -m 775 -p $dir &&
echo $u > $dir/user &&
echo $current_date_time > $dir/posted &&
echo $post_arg_comment_text > $dir/body
_s=$status
if(! ~ $"_s '') {
dprint 'ERROR XXX: Could not create comment: ' $_s
_status='Could not post comment due internal error, sorry.'
}
}
notify_errors=$_status
status=$_status
}

View File

@ -0,0 +1,13 @@
<hr>
<h2>Comments</h2>
% for(c in `{ls $comments_dir/}) {
% if(test -s $c/body) {
<div class="comment">
<h5>By: <i>%(`{cat $c/user}%)</i></b> (%(`{cat $c/posted}%))
</h5>
% cat $c/body | escape_html | sed 's,$,<br>,'
<hr></div>
% }
% }

37
werc/apps/bridge/foot.tpl Executable file
View File

@ -0,0 +1,37 @@
<hr>
% notices_handler
<form action="" method="post">
<textarea name="comment_text" id="comment_text" cols="80" rows="16">%($"saved_comment_text%)</textarea>
<br>
<input type="submit" name="bridge_post" value="Post a comment">
% if(~ $#logged_user 0) {
% if(~ $#allow_new_user_comments 1) {
<label>New user name:
<input type="text" name="comment_user" value="%($"post_arg_comment_user%)">
</label>
<label>Password:
<input type="password" name="comment_passwd" value="">
</label>
<label>Repeat password:
<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 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>
% }
% if not if(~ $#bridge_anon_comments 1) {
<label>Is <a href="http://glenda.cat-v.org">Glenda a cute bunny</a>?
<select name='ima_robot'>
<option value="yes">No</option>
<option value="not">Yes</option>
<option value="foobar">I hate bunnies!</option>
<option value="robot">I'm a robot!</option>
</select>
</label>
% }
% }
</form>

40
werc/apps/dirdir/app.rc Executable file
View File

@ -0,0 +1,40 @@
fn conf_enable_wiki {
enable_wiki=yes
wiki_editors_groups=$*
conf_enable_app dirdir
}
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 exist
# maybe it should, but for now we can fix it up here.
if(~ $lp */)
lp=$lp^'index'
dirdir_file=$lp.md
dirdir_dir=$dirdir_file^'_werc/dirdir/'
if(~ 1 $#post_arg_dirdir_edit $#post_arg_dirdir_preview)
handler_body_main=(tpl_handler `{get_lib_file dirdir/edit.tpl apps/dirdir/edit.tpl})
if not if(! ~ '' $"post_arg_dirdir_save $"post_arg_edit_text)
save_page
if not if(~ $"handler_body_main '' || {~ $REQUEST_METHOD GET && test -f $local_path.md})
ll_add handlers_bar_left tpl_handler apps/dirdir/sidebar_controls.tpl
}
}
fn save_page {
dirdir_verdir=$dirdir_dir/^`{date -n}^/
mkdir -p $dirdir_verdir
umask 002
# XXX Use a tmp file and mv(1) to ensure updates are atomic?
echo $logged_user > $dirdir_verdir/author
echo $post_arg_edit_text > $dirdir_verdir/data
echo $post_arg_edit_text > $dirdir_file
post_redirect $base_url^$req_path
#notify_notes='Saved <a href="'$"req_path'">'$"req_path'</a>!'
}

25
werc/apps/dirdir/edit.tpl Executable file
View File

@ -0,0 +1,25 @@
<div>
<h1>Editing: <a href="%($req_path%)">%($req_path%)</a></h1>
<br>
<form action="" method="POST">
<textarea name="edit_text" id="edit_text" cols="80" rows="43">%{
# FIXME Extra trailing new lines get added to the content somehow, should avoid it.
if(~ $#post_arg_edit_text 0 && test -f $dirdir_file)
cat $dirdir_file | escape_html
if not
echo -n $post_arg_edit_text | escape_html
%}</textarea>
<br>
<input type="submit" name="dirdir_save" value="Save">
<input type="submit" name="dirdir_preview" value="Preview">
<small>DirDir documents are written using <a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a>.</small>
</form>
</div>
% if(! ~ $"post_arg_dirdir_preview '') {
<h2>Preview:</h2>
<div id="preview">
% echo $post_arg_edit_text | $formatter
</div>
% }

View File

@ -0,0 +1,3 @@
<form action="" method="POST">
<input type="submit" name="dirdir_edit" value="Edit page" />
</form>

View File

@ -0,0 +1,20 @@
The default path for site search is /_search/. Assuming you want to keep
that default, you could enable site search like so:
mkdir -p /www/werc/sites/MYSITE/_search/_werc/
echo 'conf_enable_duckduckgo' > /www/werc/sites/MYSITE/_search/_werc/config
mkdir -p /www/werc/sites/MYSITE/_werc/lib/
cp /www/werc/apps/duckduckgo/footer.inc.sample /www/werc/sites/MYSITE/_werc/lib/footer.inc
Searches will POST to /_search/ and from there get redirected to Duck Duck
Go with a site:$SERVER_NAME prefix. To have the search path URL be some-
thing different, you'll have to edit line 23 of app.rc to point to the new
path.
TODO:
* Make it automatically work no matter which directory the app is enabled in.
* OR make the search path a configuration option.
* Provide a template for non-footer deployment
* Enable the search path itself to serve a search form to GET requests

30
werc/apps/duckduckgo/app.rc Executable file
View File

@ -0,0 +1,30 @@
fn conf_enable_duckduckgo {
enable_duckduckgo=yes
conf_enable_app duckduckgo
pageTitle='Site Search'
}
fn duckduckgo_init {
get_post_args q
if (! ~ $#q 0) {
redirect_string = 'https://duckduckgo.com/?q=site:'$SERVER_NAME^'+'^$"q
http_redirect $redirect_string '302 Found'
}
if not {
handler_body_main='duckduckgo_body'
}
}
fn duckduckgo_body {
echo '
<h1>Site search</h1>
<h2>using DuckDuckGo</h2>
<form action="/_search/" method="POST">
<label for="searchtext">Site search:</label>
<input type="text" id="searchtext" name="q" placeholder="Search text...">
<input type="submit" value="Search">
</form>'
}

View File

@ -0,0 +1,3 @@
<div><a href="http://werc.cat-v.org">Powered by werc</a></div>
<div><form action="/_search/" method="POST"><label for="searchtext">Site search:</label> <input type="text" id="searchtext" name="q" placeholder="Enter search text..."><input type="submit" display="Search"></form></div>

10
werc/apps/hello/app.rc Executable file
View File

@ -0,0 +1,10 @@
fn hello_init {
if(~ $req_path /hello) {
handler_body_main='hello_body'
pageTitle='Hi title!'
}
}
fn hello_body {
echo 'Hello world!'
}

45
werc/apps/paste/app.rc Executable file
View File

@ -0,0 +1,45 @@
fn conf_enable_wercpaste {
paste_url=$conf_wd
if (~ $#paste_dir 0) { paste_dir=`{pwd} }
conf_enable_app wercpaste
}
fn wercpaste_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
# env var size limit is 16kb, this thing dies with larger input.
echo $"post_arg_paste > $paste_dir^/^$now^.^$cksum
# uncomment the following line to redirect to the pasted file
#post_redirect $base_url^$paste_url^$now^.^$cksum
# uncomment the following line instead to just return the url
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
}
}
# drop a textbox
if (~ $REQUEST_METHOD GET ) { handler_body_main='begforpaste' }
}
fn begforpaste {
echo '<article class="pastebox">
<h3 style="text-align: center">pasted data is not publically indexed</h3>
<form action="'$paste_url'" method="post" style="margin:2em">
<textarea name="paste" cols="120" rows="20" required style="display: block; margin: 0 auto 0 auto" ></textarea><br>
<input type="submit" name="submit" value="SUBMIT" style="display: block; margin: 0 auto 0 auto" ><br><br>
<span style="display: none"><input type="text" name="url" value="url" > (do not change) </span>
</form>
</article>
'
}

89
werc/apps/wman/app.rc Executable file
View File

@ -0,0 +1,89 @@
fn conf_enable_wman {
wman_tmac=an
wman_base_uri=$conf_wd
wman_man_path=$*
if(~ $#wman_man_path 0)
wman_man_path=$wman_base_uri
conf_enable_app wman
}
wman_junk_filter='/(\/(INDEX|\.cvsignore|_.*)|\.9p|\.html)$/d; s!/man([0-9]+/[^/]+)$!/\1!; '
fn wman_ls_pages {
ls $* \
| sed $dirfilter^$wman_junk_filter^' s/\.([0-9]|9p)$//; s!/0intro$!/intro!' \
| sort -u
}
fn wman_init {
ifs=$ifs^'/' { p=`{echo $req_path | sed 's!^'^$wman_base_uri^'!!'} }
wman_cat=$p(1)
wman_page=$p(2)
if(~ $#wman_unix_mode 1) {
wman_cp='man'
wman_pe=.^$"wman_cat
}
if(! ~ $"wman_cat '') {
wman_cat_path=$wman_man_path^/^$"wman_cp^$p(1)
if(! ~ $"wman_page '') {
wman_page_file=$wman_page^$"wman_pe
# Hack to handle 0intro files.
if(~ $wman_page intro && test -f $wman_cat_path^/0^$"wman_page_file)
wman_page_file=0^$"wman_page_file
wman_page_file=$wman_cat_path^/^$"wman_page_file
x=`{echo $"req_path|sed 's%.*/([^/]+)/'$"wman_cat'/'^$"wman_page^'%\1%; s%_% %g'}
pageTitle=$wman_page' page from Section '$wman_cat' of the '^$"x' manual'
}
}
wman_cat_list=`{ls -F $wman_man_path/*/ \
| sed -e $wman_junk_filter -e 's!.*/([^/]+)/[^/]+$!\1!; /[0-9]+/!d' \
| sort -un}
synth_paths=($wman_base_uri$wman_cat_list'/')
if(~ $req_path $wman_base_uri && ~ $"handler_body_main '')
handler_body_main=(tpl_handler apps/wman/section_list.tpl)
if not if(~ $req_path $wman_base_uri^*) {
#^*/[a-z0-9]*[a-z]* $wman_base_uri^*/*[a-z]*[a-z0-9] $wman_base_uri^*/[a-z])
if(echo $req_path | grep -s '^'^$wman_base_uri^'/*[0-9]+/[0-9a-z\-\+\.]+$')
if(test -f $wman_page_file) # Check for 404
handler_body_main=(tpl_handler apps/wman/man_page.tpl)
if not if(~ $req_path $wman_base_uri^*/)
handler_body_main=(tpl_handler apps/wman/page_list.tpl)
if not if(~ $p(2) [A-Z]* [0-9][A-Z]*) # Correct badly capitalized links
perm_redirect $wman_base_uri^$p(1)^/^`{echo $p(2) |tr 'A-Z' 'a-z'}
}
# Search
ll_add handlers_body_head tpl_handler apps/wman/search.tpl
if(! ~ $"post_arg_wman_search '') {
s=`{echo $post_arg_wman_search | sed 's/[^a-zA-Z0-9\-\.]+//g; s/\.+/./g; 1q'}
ifs='' { wman_search_results=`{wman_ls_pages $wman_man_path/*/*^$"s^*} }
if(! ~ $"post_arg_go '' && ~ `{echo -n $wman_search_results|wc -l} 1)
post_redirect $wman_base_uri^`{echo $wman_search_results|awk -F/ '{print $(NF-1)"/"$NF}'}
}
}
fn wman_get_section_desc {
cat $wman_man_path/^$"wman_cp^$1/0intro* >[2]/dev/null| sed '1,2d; s!intro \\- [Ii]ntroduction to !!; 3q;'
}
fn wman_page_gen {
#troff -manhtml $1| troff2html -t 'Plan 9 from User Space'
troff -N -m$wman_tmac $1 | wman_out_filter
}
fn wman_out_filter {
wman_default_out_filter
}
fn wman_default_out_filter {
# col -x syntax is the same for UNIX and Plan 9.
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}' \
| col -x
}

3
werc/apps/wman/man_page.tpl Executable file
View File

@ -0,0 +1,3 @@
<pre>
% wman_page_gen $wman_page_file
</pre>

11
werc/apps/wman/page_list.tpl Executable file
View File

@ -0,0 +1,11 @@
% d=`{wman_get_section_desc $wman_cat}
<h1>Manual pages - Section %($wman_cat%): %($"d%)</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\">" }'
%}
</ul>

20
werc/apps/wman/search.tpl Executable file
View File

@ -0,0 +1,20 @@
<form action="" method="POST">
<fieldset>
<input type="text" name="wman_search" value="%($"s%)" />
<input type="submit" name="go" value="Feel Lucky" />
<input type="submit" value="Search" />
% if(! ~ $"post_arg_wman_search '') {
% if(~ $"wman_search_results '') {
No matches found for <i>'%($post_arg_wman_search%)'</i>.
% }
% if not {
<ul>
% echo $wman_search_results|awk -F/ '$(NF-1) ~ "^[0-9]+$" {printf "<li><a href=\"'$wman_base_uri'%s/%s\" />%s(%s)</a></li>", $(NF-1),$NF, $NF, $(NF-1)}'
</ul>
% }
% }
</fieldset>
</form>

11
werc/apps/wman/section_list.tpl Executable file
View File

@ -0,0 +1,11 @@
<h1>Manual Sections</h1>
<ul style="text-transform: capitalize;">
% for(c in $wman_cat_list) {
<li><a href="%($c%)/"><b>Section: %($c%)</b></a>
% wman_get_section_desc $c
% if(~ $status '' '|')
% echo '(<a href="'$c'/intro">intro</a>)'
</li>
% }
</ul>

33
werc/bin/aux/addwuser.rc Executable file
View File

@ -0,0 +1,33 @@
#!/bin/rc
if(! ~ $#werc_root 0)
cd $werc_root
fn usage {
if(! ~ $#* 0)
echo $0: $* >[1=2]
echo 'Usage:' $0 'user_name user_password [groups ...]' >[1=2]
exit usage
}
if(! test -d etc/users/)
usage 'Run for root of werc installation or set $werc_root'
user_name=$1
shift
user_pass=$1
shift
user_groups=$*
if(~ $"user_name '' || ~ $"user_pass '')
usage
mkdir etc/users/$user_name
echo $user_pass > etc/users/$user_name/password
if(! ~ $#user_groups 0)
for(g in $user_groups) {
mkdir -p etc/users/$g
echo $user_name >> etc/users/$g/members
}

64
werc/bin/aux/bpst.rc Executable file
View File

@ -0,0 +1,64 @@
#!/bin/rc
path=( $PLAN9/bin $path )
base=.
if(~ $#user 0)
user=`{whoami}
file=(); title=();
bloguser=$user
while(! ~ $#* 0) {
switch($1) {
case -u
base=/gsoc/www/people/$user/blog/
case -b
shift
base=$1
case -f
shift
file=$1
}
shift
}
if(~ $"EDITOR '')
EDITOR=vi
if(~ $#file 0 || ! test -f $file) {
file=/tmp/blogtmp.$pid
rm $file >[2]/dev/null
touch $file
}
$EDITOR $file
ispell $file
rm $file.bak >[2]/dev/null
fn mkbpost {
umask 002 # Let group write
bptext=$1
if(! ~ $#2 0)
bpid=`{echo -n '-'^$"bpid | sed 's/'$forbidden_uri_chars'+/_/g; 1q'}
d=`{/bin/date +%F|sed 's,-,/,g'}
ddir=$blagh_root^$d^'/'
n=`{ls $ddir >[2]/dev/null |wc -l}
mkdir -p $ddir/$"n^$"bpid/
{
# TODO: Enable metadata
#echo '* Posted:' `{date}
#if(! ~ $#logged_user 0)
# echo '* Author: '$logged_user
cat $bptext
}> $ddir/$"n^$"bpid/index.md
}
forbidden_uri_chars='[^a-zA-Z0-9_+\-\/\.]'
blagh_root=$base
if(test -s $file)
mkbpost $file
if not
echo Empty file!

14
werc/bin/aux/gensitemaptxt.rc Executable file
View File

@ -0,0 +1,14 @@
#!/bin/rc
# DEPRECATED: sitemap.tpl now generates and updates a sitemap.txt when requested, and is also more smart than this simplistic script.
for(d in sites/*/) {
echo $d
9 du -a $d | awk '/\.(md|html)$/ { print $2 }; {}' | 9 sed -e 's/\.(md|html)$//' -e 's,/index$,/,' -e 's,^sites/,http://,' > $d/sitemap.txt
if(! test -f $d/robots.txt) {
echo generating missing robots.txt for $d
echo $d|sed 's,sites/,Sitemap: http://,; s/$/sitemap.txt/;' > $d/robots.txt
cat $d/robots.txt
}
}

16
werc/bin/aux/runtsts.rc Executable file
View File

@ -0,0 +1,16 @@
#!/bin/rc
tstdom='http://test.cat-v.org'
cd sites/tst.cat-v.org
tstfiles=`{du -a |awk '/\.tst$/ { print $2 }; {} ' | sed 's/^\.//; s/\.tst$//'}
for(f in $tstfiles) {
ifs='
' { tsts=`{cat ./$f.tst} }
for(t in $tsts) {
echo tst $t
}
}

236
werc/bin/cgilib.rc Executable file
View File

@ -0,0 +1,236 @@
# Useful CGI stuff
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 {
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 perm_redirect { http_redirect $1 '301 Moved Permanantly' }
fn post_redirect { http_redirect $1 '303 See Other' }
# Note: should check if content type is application/x-www-form-urlencoded?
# Should compare with http://www.shelldorado.com/scripts/cmds/urlgetopt.txt
fn load_post_args {
if(~ $REQUEST_METHOD POST && ~ $#post_args 0) {
ifs='&
' for(pair in `{cat}) {
ifs='=' { pair=`{echo -n $pair} }
n='post_arg_'^`{echo $pair(1)|nurldecode|tr -cd 'a-zA-Z0-9_'}
post_args=( $post_args $n )
ifs=() { $n=`{echo -n $pair(2)|nurldecode|tr -d ' '} }
}
pair=()
}
if not
status='No POST or post args already loaded'
}
# Status is () if at least one arg is found. DEPRECATED: access vars directly.
fn get_post_args {
load_post_args
_status='No post arg matches'
for(n in $*) {
v=post_arg_$n
if(! ~ $#$v 0) {
$n=$$v
_status=()
}
}
status=$_status
}
# This seems slightly improve performance, but might depend on httpd buffering behavior.
fn awk_buffer {
awk '{
buf = buf $0"\n"
if(length(buf) > 1400) {
printf "%s", buf
buf = ""
}
}
END { printf "%s", buf }'
}
fn nurldecode { urlencode -d || url_decode} # GROSS
fn url_decode {
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 nurlencode { urlencode || url_encode } # GROSS
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.
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 ""
}
' $*
}
# Cookies
fn set_cookie {
# TODO: should check input values more carefully
name=$1
val=$2
extraHttpHeaders=( $extraHttpHeaders 'Set-cookie: '^$"name^'='^$"val^'; path=/;' )
}
fn get_cookie {
ifs=';' { co=`{echo $HTTP_COOKIE} }
# XXX: we might be adding a trailing new line?
# The ' ?' is needed to deal with '; ' inter-cookie delimiter
{ for(c in $co) echo $c } | sed -n 's/^ ?'$1'=//p'
}
fn static_file {
echo -n 'Content-Type: '
select_mime $1
echo
exec cat $1
}
fn select_mime {
m='text/plain'
if(~ $1 *.css)
m='text/css'
if not if(~ $1 *.ico)
m='image/x-icon'
if not if(~ $1 *.png)
m='image/png'
if not if(~ $1 *.jpg *.jpeg)
m='image/jpeg'
if not if(~ $1 *.gif)
m='image/gif'
if not if(~ $1 *.pdf)
m='application/pdf'
echo $m
}
##############################################
# Generic rc programming helpers
# Manage nested lists
fn ll_add {
_l=$1^_^$#$1
$_l=$*(2-)
$1=( $$1 $_l )
}
# Add to the head: dangerous if you shrink list by hand!
fn ll_addh {
_l=$1^_^$#$1
$_l=$*(2-)
$1=( $_l $$1 )
}
NEW_LINE='
'
# crop_text [max_lenght [ellipsis]]
# TODO: Option to crop only at word-delimiters.
fn crop_text {
m=512
e='...'
if(! ~ $#1 0)
m=$1
if(! ~ $#2 0)
e=$2
awk -v 'max='^$"m -v 'ellipsis='$e '
{
nc += 1 + length;
if(nc > max) {
print substr($0, 1, nc - max) " " ellipsis
exit
}
print
}'
}

27
werc/bin/contrib/fix-rc-scripts Executable file
View File

@ -0,0 +1,27 @@
#!/usr/local/plan9/bin/rc
# Fix rc shell scripts to find rc without launching env every time.
# Invoke with rc and plan9 versions of grep and ed in $PATH
# If your system lacks which (e.g. some gnu/linux)
# substitute the full path to rc in this line:
rc=/usr/local/plan9/bin/rc
firstline='#!'$"rc
if(~ $#* 0) files = *
if not files = $*
myname = `{basename $0}
for(file in $files) {
if(test -d $file) $0 $file/*
if not if(~ $file *$myname) {}
if not if(sed 1q $file | grep '^#!/.*[/ ]rc$' > /dev/null) {
{
echo 1c
echo $firstline
echo .
echo wq
} | ed $file > /dev/null
}
}

12
werc/bin/contrib/hgweb.config Executable file
View File

@ -0,0 +1,12 @@
[web]
style = gitweb
allow_archive = bz2
#[paths]
#w9 = /gsoc/hg/w9/
[collections]
#allow_archive = bz2 zip
/gsoc/hg = /gsoc/hg/
#/var/hg = /var/hg/

47
werc/bin/contrib/hgwebdir.cgi Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
#
# An example CGI script to export multiple hgweb repos, edit as necessary
# send python tracebacks to the browser if an error occurs:
import cgitb
cgitb.enable()
# adjust python path if not a system-wide install:
#import sys
#sys.path.insert(0, "/path/to/python/lib")
# If you'd like to serve pages with UTF-8 instead of your default
# locale charset, you can do so by uncommenting the following lines.
# Note that this will cause your .hgrc files to be interpreted in
# UTF-8 and all your repo files to be displayed using UTF-8.
#
#import os
#os.environ["HGENCODING"] = "UTF-8"
from mercurial.hgweb.hgwebdir_mod import hgwebdir
from mercurial.hgweb.request import wsgiapplication
import mercurial.hgweb.wsgicgi as wsgicgi
# The config file looks like this. You can have paths to individual
# repos, collections of repos in a directory tree, or both.
#
# [paths]
# virtual/path = /real/path
# virtual/path = /real/path
#
# [collections]
# /prefix/to/strip/off = /root/of/tree/full/of/repos
#
# collections example: say directory tree /foo contains repos /foo/bar,
# /foo/quux/baz. Give this config section:
# [collections]
# /foo = /foo
# Then repos will list as bar and quux/baz.
#
# Alternatively you can pass a list of ('virtual/path', '/real/path') tuples
# or use a dictionary with entries like 'virtual/path': '/real/path'
def make_web_app():
return hgwebdir("hgweb.config")
wsgicgi.launch(wsgiapplication(make_web_app))

1447
werc/bin/contrib/markdown.pl Executable file

File diff suppressed because it is too large Load Diff

427
werc/bin/contrib/md2html.awk Executable file
View File

@ -0,0 +1,427 @@
#!/bin/awk -f
#
# by: Jesus Galan (yiyus) 2009
#
# Usage: md2html.awk file.md > file.html
# See: http://4l77.com/src/md2html.awk
function eschtml(t) {
gsub("&", "\\&amp;", t);
gsub("<", "\\&lt;", t);
return t;
}
function oprint(t){
if(nr == 0)
print t;
else
otext = otext "\n" t;
}
function subref(id){
for(; nr > 0 && sub("<<" id, ref[id], otext); nr--);
if(nr == 0 && otext) {
print otext;
otext = "";
}
}
function nextil(t) {
if(!match(t, /[`<&\[*_\\-]|(\!\[)/))
return t;
t1 = substr(t, 1, RSTART - 1);
tag = substr(t, RSTART, RLENGTH);
t2 = substr(t, RSTART + RLENGTH);
if(ilcode && tag != "`")
return eschtml(t1 tag) nextil(t2);
# Backslash escaping
if(tag == "\\"){
if(match(t2, /^[\\`*_{}\[\]()#+\-\.!]/)){
tag = substr(t2, 1, 1);
t2 = substr(t2, 2);
}
return t1 tag nextil(t2);
}
# Dashes
if(tag == "-"){
if(sub(/^-/, "", t2))
tag = "&#8212;";
return t1 tag nextil(t2);
}
# Inline Code
if(tag == "`"){
if(sub(/^`/, "", t2)){
if(!match(t2, /``/))
return t1 "&#8221;" nextil(t2);
ilcode2 = !ilcode2;
}
else if(ilcode2)
return t1 tag nextil(t2);
tag = "<code>";
if(ilcode){
t1 = eschtml(t1);
tag = "</code>";
}
ilcode = !ilcode;
return t1 tag nextil(t2);
}
if(tag == "<"){
# Autolinks
if(match(t2, /^[^ ]+[\.@][^ ]+>/)){
url = eschtml(substr(t2, 1, RLENGTH - 1));
t2 = substr(t2, RLENGTH + 1);
linktext = url;
if(match(url, /@/) && !match(url, /^mailto:/))
url = "mailto:" url;
return t1 "<a href=\"" url "\">" linktext "</a>" nextil(t2);
}
# Html tags
if(match(t2, /^[A-Za-z\/!][^>]*>/)){
tag = tag substr(t2, RSTART, RLENGTH);
t2 = substr(t2, RLENGTH + 1);
return t1 tag nextil(t2);
}
return t1 "&lt;" nextil(t2);
}
# Html special entities
if(tag == "&"){
if(match(t2, /^#?[A-Za-z0-9]+;/)){
tag = tag substr(t2, RSTART, RLENGTH);
t2 = substr(t2, RLENGTH + 1);
return t1 tag nextil(t2);
}
return t1 "&amp;" nextil(t2);
}
# Images
if(tag == "!["){
if(!match(t2, /(\[.*\])|(\(.*\))/))
return t1 tag nextil(t2);
match(t2, /^[^\]]*/);
alt = substr(t2, 1, RLENGTH);
t2 = substr(t2, RLENGTH + 2);
if(match(t2, /^\(/)){
# Inline
sub(/^\(/, "", t2);
match(t2, /^[^\)]+/);
url = eschtml(substr(t2, 1, RLENGTH));
t2 = substr(t2, RLENGTH + 2);
title = "";
if(match(url, /[ ]+\".*\"[ ]*$/)) {
title = substr(url, RSTART, RLENGTH);
url = substr(url, 1, RSTART - 1);
match(title, /\".*\"/);
title = " title=\"" substr(title, RSTART + 1, RLENGTH - 2) "\"";
}
if(match(url, /^<.*>$/))
url = substr(url, 2, RLENGTH - 2);
return t1 "<img src=\"" url "\" alt=\"" alt "\"" title " />" nextil(t2);
}
else{
# Referenced
sub(/^ ?\[/, "", t2);
id = alt;
if(match(t2, /^[^\]]+/))
id = substr(t2, 1, RLENGTH);
t2 = substr(t2, RLENGTH + 2);
if(ref[id])
r = ref[id];
else{
r = "<<" id;
nr++;
}
return t1 "<img src=\"" r "\" alt=\"" alt "\" />" nextil(t2);
}
}
# Links
if(tag == "["){
if(!match(t2, /(\[.*\])|(\(.*\))/))
return t1 tag nextil(t2);
match(t2, /^[^\]]*(\[[^\]]*\][^\]]*)*/);
linktext = substr(t2, 1, RLENGTH);
t2 = substr(t2, RLENGTH + 2);
if(match(t2, /^\(/)){
# Inline
match(t2, /^[^\)]+(\([^\)]+\)[^\)]*)*/);
url = substr(t2, 2, RLENGTH - 1);
pt2 = substr(t2, RLENGTH + 2);
title = "";
if(match(url, /[ ]+\".*\"[ ]*$/)) {
title = substr(url, RSTART, RLENGTH);
url = substr(url, 1, RSTART - 1);
match(title, /\".*\"/);
title = " title=\"" substr(title, RSTART + 1, RLENGTH - 2) "\"";
}
if(match(url, /^<.*>$/))
url = substr(url, 2, RLENGTH - 2);
url = eschtml(url);
return t1 "<a href=\"" url "\"" title ">" nextil(linktext) "</a>" nextil(pt2);
}
else{
# Referenced
sub(/^ ?\[/, "", t2);
id = linktext;
if(match(t2, /^[^\]]+/))
id = substr(t2, 1, RLENGTH);
t2 = substr(t2, RLENGTH + 2);
if(ref[id])
r = ref[id];
else{
r = "<<" id;
nr++;
}
pt2 = t2;
return t1 "<a href=\"" r "\" />" nextil(linktext) "</a>" nextil(pt2);
}
}
# Emphasis
if(match(tag, /[*_]/)){
ntag = tag;
if(sub("^" tag, "", t2)){
if(stag[ns] == tag && match(t2, "^" tag))
t2 = tag t2;
else
ntag = tag tag
}
n = length(ntag);
tag = (n == 2) ? "strong" : "em";
if(match(t1, / $/) && match(t2, /^ /))
return t1 tag nextil(t2);
if(stag[ns] == ntag){
tag = "/" tag;
ns--;
}
else
stag[++ns] = ntag;
tag = "<" tag ">";
return t1 tag nextil(t2);
}
}
function inline(t) {
ilcode = 0;
ilcode2 = 0;
ns = 0;
return nextil(t);
}
function printp(tag) {
if(!match(text, /^[ ]*$/)){
text = inline(text);
if(tag != "")
oprint("<" tag ">" text "</" tag ">");
else
oprint(text);
}
text = "";
}
BEGIN {
blank = 0;
code = 0;
hr = 0;
html = 0;
nl = 0;
nr = 0;
otext = "";
text = "";
par = "p";
}
# References
!code && /^ *\[[^\]]*\]:[ ]+/ {
sub(/^ *\[/, "");
match($0, /\]/);
id = substr($0, 1, RSTART - 1);
sub(id "\\]:[ ]+", "");
title = "";
if(match($0, /\".*\"$/))
title = "\" title=\"" substr($0, RSTART + 1, RLENGTH - 2);
sub(/[ ]+\".*\"$/, "");
url = eschtml($0);
ref[id] = url title;
subref(id);
next;
}
# html
!html && /^<(address|blockquote|center|dir|div|dl|fieldset|form|h[1-6r]|\
isindex|menu|noframes|noscript|ol|p|pre|table|ul|!--)/ {
if(code)
oprint("</pre></code>");
for(; !text && block[nl] == "blockquote"; nl--)
oprint("</blockquote>");
match($0, /^<(address|blockquote|center|dir|div|dl|fieldset|form|h[1-6r]|\
isindex|menu|noframes|noscript|ol|p|pre|table|ul|!--)/);
htag = substr($0, 2, RLENGTH - 1);
if(!match($0, "(<\\/" htag ">)|((^<hr ?\\/?)|(--)>$)"))
html = 1;
if(html && match($0, /^<hr/))
hr = 1;
oprint($0);
next;
}
html && (/(^<\/(address|blockquote|center|dir|div|dl|fieldset|form|h[1-6r]|\
isindex|menu|noframes|noscript|ol|p|pre|table|ul).*)|(--)>$/ ||
(hr && />$/)) {
html = 0;
hr = 0;
oprint($0);
next;
}
html {
oprint($0);
next;
}
# List and quote blocks
# Remove indentation
{
for(nnl = 0; nnl < nl; nnl++)
if((match(block[nnl + 1], /[ou]l/) && !sub(/^( | )/, "")) || \
(block[nnl + 1] == "blockquote" && !sub(/^> ?/, "")))
break;
}
nnl < nl && !blank && text && ! /^ ? ? ?([*+-]|([0-9]+\.)+)( +| )/ { nnl = nl; }
# Quote blocks
{
while(sub(/^> /, ""))
nblock[++nnl] = "blockquote";
}
# Horizontal rules
{ hr = 0; }
(blank || (!text && !code)) && /^ ? ? ?([-*_][ ]*)([-*_][ ]*)([-*_][ ]*)+$/ {
if(code){
oprint("</pre></code>");
code = 0;
}
blank = 0;
nnl = 0;
hr = 1;
}
# List items
block[nl] ~ /[ou]l/ && /^$/ {
blank = 1;
next;
}
{ newli = 0; }
!hr && (nnl != nl || !text || block[nl] ~ /[ou]l/) && /^ ? ? ?[*+-]( +| )/ {
sub(/^ ? ? ?[*+-]( +| )/, "");
nnl++;
nblock[nnl] = "ul";
newli = 1;
}
(nnl != nl || !text || block[nl] ~ /[ou]l/) && /^ ? ? ?([0-9]+\.)+( +| )/ {
sub(/^ ? ? ?([0-9]+\.)+( +| )/, "");
nnl++;
nblock[nnl] = "ol";
newli = 1;
}
newli {
if(blank && nnl == nl && !par)
par = "p";
blank = 0;
printp(par);
if(nnl == nl && block[nl] == nblock[nl])
oprint("</li><li>");
}
blank && ! /^$/ {
if(match(block[nnl], /[ou]l/) && !par)
par = "p";
printp(par);
par = "p";
blank = 0;
}
# Close old blocks and open new ones
nnl != nl || nblock[nl] != block[nl] {
if(code){
oprint("</pre></code>");
code = 0;
}
printp(par);
b = (nnl > nl) ? nblock[nnl] : block[nnl];
par = (match(b, /[ou]l/)) ? "" : "p";
}
nnl < nl || (nnl == nl && nblock[nl] != block[nl]) {
for(; nl > nnl || (nnl == nl && pblock[nl] != block[nl]); nl--){
if(match(block[nl], /[ou]l/))
oprint("</li>");
oprint("</" block[nl] ">");
}
}
nnl > nl {
for(; nl < nnl; nl++){
block[nl + 1] = nblock[nl + 1];
oprint("<" block[nl + 1] ">");
if(match(block[nl + 1], /[ou]l/))
oprint("<li>");
}
}
hr {
oprint("<hr>");
next;
}
# Code blocks
code && /^$/ {
if(blanK)
oprint("");
blank = 1;
next;
}
!text && sub(/^( | )/, "") {
if(blanK)
oprint("");
blank = 0;
if(!code)
oprint("<code><pre>");
code = 1;
$0 = eschtml($0);
oprint($0);
next;
}
code {
oprint("</pre></code>");
code = 0;
}
# Setex-style Headers
text && /^=+$/ {printp("h1"); next;}
text && /^-+$/ {printp("h2"); next;}
# Atx-Style headers
/^#+/ && (!newli || par=="p" || /^##/) {
for(n = 0; n < 6 && sub(/^# */, ""); n++)
sub(/#$/, "");
par = "h" n;
}
# Paragraph
/^$/ {
printp(par);
par = "p";
next;
}
# Add text
{ text = (text ? text " " : "") $0; }
END {
if(code){
oprint("</pre></code>");
code = 0;
}
printp(par);
for(; nl > 0; nl--){
if(match(block[nl], /[ou]l/))
oprint("</li>");
oprint("</" block[nl] ">");
}
gsub(/<<[^\"]*/, "", otext);
print(otext);
}

View File

@ -0,0 +1,6 @@
#!/bin/rc
if(~ $REMOTE_USER ''){
extra_headers=($extra_headers 'WWW-Authenticate: Basic realm="'$"SERVER_NAME'"')
error 401
exit
}

View File

@ -0,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
}

View File

@ -0,0 +1,111 @@
#!/bin/rc
PATH_INFO=`{echo $PATH_INFO | urldecode.awk}
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>'

View File

@ -0,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

View File

@ -0,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>'

View File

@ -0,0 +1,43 @@
#!/bin/rc
full_path=`{echo $"FS_ROOT^$"PATH_INFO | urldecode.awk}
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 || file -i $full_path} # GROSS
}
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

View File

@ -0,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

View File

@ -0,0 +1,5 @@
#!/bin/rc
if(~ $PATH_INFO */)
exec dir-index $params
if not
exec serve-static

View File

@ -0,0 +1,39 @@
# taken from werc
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
}

View File

@ -0,0 +1,106 @@
#!/bin/rc
rc_httpd_dir=/werc/bin/contrib/rc-httpd
path=($PLAN9/bin $rc_httpd_dir/handlers $PATH)
cgi_path=$PLAN9/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;\?.*;;
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)
}
switch($SERVER_NAME){
case */* ..
error 400
exit
}
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

View File

@ -0,0 +1,20 @@
#!/bin/rc
rfork n
# Route requests to werc.
# Change paths to match your system.
if(~ $SERVER_NAME 9base.werc.cat-v.org)
PLAN9=/usr/local/9base
if(~ $SERVER_NAME frontbase.werc.cat-v.org)
PLAN9=/usr/local/plan9front
if(~ $SERVER_NAME plan9port.werc.cat-v.org)
PLAN9=/usr/local/plan9
if(~ $SERVER_NAME *){
PATH_INFO=$location
FS_ROOT=/werc/sites/$SERVER_NAME
exec static-or-cgi /werc/bin/werc.rc
}
if not
error 503

7
werc/bin/contrib/tcp80 Executable file
View File

@ -0,0 +1,7 @@
#!/bin/rc
# For use with listen(8).
# Change paths to match your system.
# Eitdit rc-httpd/rc-httpd to match your system.
PLAN9=/usr/local/plan9
PATH=($PATH /home/sl/www/werc/bin/contrib)
exec /home/sl/www/werc/bin/contrib/rc-httpd/rc-httpd >>[2]/var/log/rc-httpd

39
werc/bin/contrib/urldecode.awk Executable file
View File

@ -0,0 +1,39 @@
#!/bin/awk -f
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
}
print decoded
}

126
werc/bin/contrib/urlencode.awk Executable file
View File

@ -0,0 +1,126 @@
# Taken from http://www.shelldorado.com/scripts/cmds/urlencode
##########################################################################
# Title : urlencode - encode URL data
# Author : Heiner Steven (heiner.steven@odn.de)
# Date : 2000-03-15
# Requires : awk
# Categories : File Conversion, WWW, CGI
# SCCS-Id. : @(#) urlencode 1.4 06/10/29
##########################################################################
# Description
# Encode data according to
# RFC 1738: "Uniform Resource Locators (URL)" and
# RFC 1866: "Hypertext Markup Language - 2.0" (HTML)
#
# This encoding is used i.e. for the MIME type
# "application/x-www-form-urlencoded"
#
# Notes
# o The default behaviour is not to encode the line endings. This
# may not be what was intended, because the result will be
# multiple lines of output (which cannot be used in an URL or a
# HTTP "POST" request). If the desired output should be one
# line, use the "-l" option.
#
# o The "-l" option assumes, that the end-of-line is denoted by
# the character LF (ASCII 10). This is not true for Windows or
# Mac systems, where the end of a line is denoted by the two
# characters CR LF (ASCII 13 10).
# We use this for symmetry; data processed in the following way:
# cat | urlencode -l | urldecode -l
# should (and will) result in the original data
#
# o Large lines (or binary files) will break many AWK
# implementations. If you get the message
# awk: record `...' too long
# record number xxx
# consider using GNU AWK (gawk).
#
# o urlencode will always terminate it's output with an EOL
# character
#
# Thanks to Stefan Brozinski for pointing out a bug related to non-standard
# locales.
#
# See also
# urldecode
##########################################################################
PN=`basename "$0"` # Program name
VER='1.4'
: ${AWK=awk}
Usage () {
echo >&2 "$PN - encode URL data, $VER
usage: $PN [-l] [file ...]
-l: encode line endings (result will be one line of output)
The default is to encode each input line on its own."
exit 1
}
Msg () {
for MsgLine
do echo "$PN: $MsgLine" >&2
done
}
Fatal () { Msg "$@"; exit 1; }
set -- `getopt hl "$@" 2>/dev/null` || Usage
[ $# -lt 1 ] && Usage # "getopt" detected an error
EncodeEOL=no
while [ $# -gt 0 ]
do
case "$1" in
-l) EncodeEOL=yes;;
--) shift; break;;
-h) Usage;;
-*) Usage;;
*) break;; # First file name
esac
shift
done
LANG=C export LANG
$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.
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 ""
}
' "$@"

30
werc/bin/contrib/webserver.rc Executable file
View File

@ -0,0 +1,30 @@
#!/bin/rc
# A web server in rc by maht
# Originally from http://www.proweb.co.uk/~matt/rc/webserver.rc
ifs=' '
request=`{sed 1q}
url=$request(2)
file=`{echo $url | sed 's/http:\/\/[^\/]*//' | tr -d \012}
if(test -d $file){
file=$file ^'/index.html'
}
if(test -e $file) {
response='200'
}
if not {
response='404'
file='404.html'
}
echo 'HTTP/1.1 ' ^$response
echo 'Date: ' `{date}
echo 'Server: rc shell'
echo 'Content-Length: ' `{cat $file | wc -c | tr -d ' '}
echo 'Content-Type: ' `{file -i $file | awk '{ print $2 }'}
echo 'Connection: close'
echo
cat $file

152
werc/bin/corehandlers.rc Executable file
View File

@ -0,0 +1,152 @@
# Werc builtin handlers
fn nav_tree {
if(! ~ $#sideBarNavTitle 0)
echo '<p class="sideBarTitle">'$"sideBarNavTitle':</p>'
# Ignore stderr, last path element might be a file that doesn't exist (eg., foo for foo.md)
# /./ to deal with p9p's ls failure to follow dir symlinks otherwise
ls -F $sitedir/./$req_paths_list >[2]/dev/null \
| {
sed $dirfilter'/\/[^_.\/][^\/]*(\.(md|txt|html)|\/)$/!d; s!^'$sitedir'!!; '$dirclean
if(! ~ $#synth_paths 0) echo $synth_paths | tr ' ' $NEW_LINE
} | sort -u | awk -F/ '
function p(x, y, s) { for(i=0; i < x-y; i+=1) print s }
BEGIN { lNF=2; print "<ul>" }
{
d = ""
if(match($0, "/$"))
d = "/"
sub("/$", "") # Strip trailing / for dirs so NF is consistent
p(NF, lNF, "<li><ul>")
p(lNF, NF, "</ul></li>")
lNF = NF
bname = $NF d
path = $0 d
gsub(/[\-_]/, " ", bname)
# To avoid false matches add trailing / even for plain files to act as delimiter
pa = path
gsub(/[^\/]$/, "&/", pa)
if(index(ENVIRON["req_path"] "/", pa) == 1)
print "<li><a href=\"" path "\" class=\"thisPage\">&raquo;<i> " bname "</i></a></li>"
else
print "<li><a href=\"" path "\">&rsaquo; " bname "</a></li>"
}
END { p(lNF, 2, "</ul></li>"); print "</ul>" }'
}
fn link_bar {
if(~ $1 -t) {
echo '<p class="sideBarTitle">'$2'</p>'
shift; shift
}
echo '<ul>'
while(! ~ $#* 0) {
echo '<li><a href="'$2'">- '$1'</a></li>'
shift; shift
}
echo '</ul>'
}
fn md_handler { $formatter $1 }
fn tpl_handler { template $* }
fn html_handler {
# body states: 0 = no <body> found, 2 = after <body>, 1 = after <body></body>, -1 = after </body>
awk 'gsub(".*<[Bb][Oo][Dd][Yy][^>]*>", "") > 0 {body=2}
gsub("</ *[Bb][Oo][Dd][Yy][^>]*>.*", "") > 0 {print; body=body-1}
body==2 {print}
body==0 {buf=buf "\n" $0}
END {if(body<=0) {print buf}}' < $1
}
fn txt_handler {
# Note: Words are not broken, even if they are way beyond 82 chars long
echo '<pre>'
sed 's/</\&lt;/g; s/>/\&gt;/g' < $1 | fmt -l 82 -j
echo '</pre>'
}
fn dir_listing_handler {
d=`{basename -d $1}
if(~ $#d 0)
d='/'
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' s,.*/([^/]+/?)$,<li><a href="\1">\1</a></li>,'
echo '</ul>'
}
fn notices_handler {
for(type in notify_errors notify_notes notify_success)
for(n in $$type)
echo '<div class="'$type'"><b>'$"n'</b></div>'
}
fn setup_handlers {
if(test -f $local_path.md) {
local_file=$local_path.md
handler_body_main=(md_handler $local_file)
}
if not if(test -f $local_path.tpl) {
local_file=$local_path.tpl
handler_body_main=(tpl_handler $local_file)
}
if not if(test -f $local_path.html) {
local_file=$local_path.html
handler_body_main=(html_handler $local_file)
}
# Global tpl (eg sitemap.tpl), should take precedence over txt handler!
if not if(test -f tpl^$req_path^.tpl)
# XXX Should we set $local_file for global .tpls?
handler_body_main=(tpl_handler tpl^$req_path^.tpl)
if not if(test -f $local_path.txt) {
local_file=$local_path.txt
handler_body_main=(txt_handler $local_file)
}
# XXX Should check that $enabled_apps exist in $werc_apps?
# XXX Should split init of apps that provide main handler (eg., blog) and apps that don't (eg., comments)?
if(! ~ $#enabled_apps 0)
for(a in $enabled_apps)
$a^'_init'
if(! ~ $#handler_body_main 0)
{ } # We are done
# Dir listing
if not if(~ $local_path */index) {
handler_body_main=(dir_listing_handler $req_path)
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'
}
# Canonize explicit .html urls, the web server might handle this first!
if not if(~ $local_path *.html && test -f $local_path)
perm_redirect `{ echo $req_path|sed 's/.html$//' }
# Fallback static file handler
if not if(test -f $local_path)
static_file $local_path
if not if(~ $req_path /pub/* && test -f .$req_path)
static_file .$req_path
# File not found
if not
setup_404_handler
}
# This function allows config files to define their own 404 handlers.
fn setup_404_handler {
handler_body_main=(tpl_handler `{get_lib_file 404.tpl})
echo 'Status: 404 Not Found'
dprint 'NOT FOUND: '$SERVER_NAME^$"REQUEST_URI^' - '^$"HTTP_REFERER^' - '^$"HTTP_USER_AGENT
}
fn run_handlers { for(h in $*) run_handler $$h }
fn run_handler { $*(1) $*(2-) }

37
werc/bin/fltr_cache.rc Executable file
View File

@ -0,0 +1,37 @@
#!/bin/rc
fn fltr_cache {
a=()
tmpf=()
proc=$1
shift
if(~ $#* 0) {
tmpf=/tmp/fmttmp.$pid
f=$tmpf
score=`{{tee $tmpf || exit 1} | sha1sum}
}
if not {
f=$1
if(~ $f */) {
score=`{du -an $f | sha1sum || exit 1} # XXX using -n(bytes) instead of -t(lastmod) because sitemap proc touches files in tree.
a=$f
f=/dev/null
}
if not {
score=`{sha1sum $f || exit 1}
score=$score(1)
}
}
cachedir=/tmp/fltr_cache/$score
mkdir -p $cachedir >[2]/dev/null
if(test -s $cachedir/$proc)
cat $cachedir/$proc
if not
if($proc $a < $f | tee $cachedir/$pid)
mv $cachedir/$pid $cachedir/$proc
rm $tmpf $cachedir/$pid >[2]/dev/null &
}

55
werc/bin/template.awk Executable file
View File

@ -0,0 +1,55 @@
#!/bin/awk -f
function pr(str) {
if(lastc !~ "[{(]")
gsub(/'/, "''", str)
printf "%s", str
}
function trans(c) {
printf "%s", end
lastc = c
end = "\n"
if(c == "%")
end = ""
else if(c == "(")
printf "echo -n "
else if(c ~ "[})]") {
end = "'\n"
printf "echo -n '"
}
}
BEGIN {
lastc = "{"
trans("}")
}
END {
print end
}
/^%/ && $0 !~ /^%[{()}%]/ && lastc !~ /[({]/ {
trans("%")
print substr($0, 2)
next
}
{
if(lastc == "%")
trans("}")
n = split($0, a, "%")
pr(a[1])
for(i=2; i<=n; i++) {
c = substr(a[i], 1, 1)
rest = substr(a[i], 2)
if((lastc !~ "[({]" && c ~ "[({]") ||
(lastc == "{" && c == "}") ||
(lastc == "(" && c == ")"))
trans(c)
else if(c == "%")
pr("%")
else
pr("%" c)
pr(rest)
}
pr("\n")
}

138
werc/bin/werc.rc Executable file
View File

@ -0,0 +1,138 @@
#!/bin/rc
. ./cgilib.rc
. ./werclib.rc
. ./wercconf.rc
. ./corehandlers.rc
. ./fltr_cache.rc
cd ..
forbidden_uri_chars='[^a-zA-Z0-9_+\-\/\.,:]'
difs=$ifs # Used to restore default ifs when needed
# Expected input: ls -F style, $sitedir/path/to/files/
# <ls -F+x><symlink hack><Useless?><hiden files >
dirfilter='s/\*$//; s,/+\./+,/,g; s,^\./,,; /\/[._][^\/]/d; /'$forbidden_uri_chars'/d; /\/sitemap\.xml$/d; /\/index\.(md|html|txt|tpl)$/d; /\/(robots|sitemap)\.txt$/d; /_werc\/?$/d; '
dirclean=' s/\.(md|html|txt)$//; '
# Careful, the proper p9p path might not be set until initrc.local is sourced
path=(. /bin ./bin)
res_tail='</body></html>'
http_content_type='text/html'
ll_add handlers_bar_left nav_tree
werc_apps=( apps/* )
werc_root=`{pwd}
sitesdir=sites
. ./etc/initrc
if(test -f etc/initrc.local)
. ./etc/initrc.local
for(a in $werc_apps)
. ./$a/app.rc
fn werc_exec_request {
site=$SERVER_NAME
base_url=http://$site:$SERVER_PORT
sitedir=$sitesdir/$site
headers=`{get_lib_file headers.tpl}
master_template=`{get_lib_file default_master.tpl}
current_date_time=`{date}
# 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 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
local_file=''
ifs='/' { args=`{echo -n $req_path} }
# Preload post args for templates where cgi's stdin is not accessible
if(~ $REQUEST_METHOD POST) {
load_post_args
login_user
}
if(~ $req_path */index)
perm_redirect `{echo $req_path | sed 's,/index$,/,'}
if(~ $local_path */) {
if(test -d $local_path)
local_path=$local_path^'index'
# XXX: This redir might step on apps with synthetic dirs.
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/[.,;:)]$//'}
if not if(test -d $local_path)
perm_redirect $req_path^'/'
if(! ~ $#args 0)
ifs=$NEW_LINE { pageTitle=`{ echo $args|sed -e 's/ / - /g' -e 's/([a-z])-([a-z])/\1 \2/g' -e 's/_/ /g' } }
cd $sitedir
req_paths_list='/' # Note: req_paths_list doesn't include 'stnythetic' dirs.
conf_wd='/' # Used in config files to know where we are in the document tree.
if(test -f _werc/config)
. _werc/config
for(i in $args) {
conf_wd=$conf_wd^$i
req_paths_list=($req_paths_list $conf_wd)
if(test -d $i) {
conf_wd=$conf_wd'/'
cd $i
if(test -f _werc/config)
. _werc/config
}
}
cd $werc_root
if(~ $#perm_redir_to 1)
perm_redirect $perm_redir_to
for(l in $perm_redir_patterns) {
p=$$l
r=$p(1)
# If target is absolute, then patern must match whole string
if(~ $p(2) http://* https://*)
r='^'$r
t=`{ echo $req_path | sed 's!'^$r^'!'^$p(2)^'!' } # Malicious danger!
if(! ~ $"t '' $req_path)
perm_redirect $t
}
setup_handlers
# Set Page title
if(! ~ $local_file '') {
t=`{get_file_title $local_file}
if(! ~ $"t '')
pageTitle=$t
}
# XXX Is this never true? because we set pageTitle earlier based on url.
if(~ $"pageTitle '')
pageTitle=$"siteTitle' '$"siteSubTitle
# if not
# pageTitle=$"pageTitle' | '$"siteTitle' '$"siteSubTitle
for(h in $extraHttpHeaders)
echo $h
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
if(~ $REQUEST_METHOD HEAD)
exit
template $headers $master_template # | awk_buffer
echo $res_tail
}
werc_exec_request

5
werc/bin/werc_errlog_wrap.rc Executable file
View File

@ -0,0 +1,5 @@
#!/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

19
werc/bin/wercconf.rc Executable file
View File

@ -0,0 +1,19 @@
# To be used from config files
fn conf_perm_redirect {
if(~ $#* 1)
perm_redir_to=$1
if not
ll_addh perm_redir_patterns $1 $2
}
fn conf_hide_paths {
for(i in $*)
dirfilter=$dirfilter^'/'^`{echo $sitedir$conf_wd$i|sed 's!/+!\\/!g'}^'/d; '
}
# Usually will be called from within conf_enable_foo
fn conf_enable_app {
# Note: maybe we should add test -d apps/$1/?
if(! ~ $1 $enabled_apps)
enabled_apps=( $enabled_apps $1 )
}

393
werc/bin/werclib.rc Executable file
View File

@ -0,0 +1,393 @@
fn get_lib_file {
if(! ~ $#sitedir 0 && test -f $sitedir/_werc/lib/$1)
echo -n $sitedir/_werc/lib/$1
if not if(! ~ $#masterSite 0 && test -f $sitesdir/$masterSite/_werc/lib/$1)
echo -n $sitesdir/$masterSite/_werc/lib/$1
if not if(test -f lib/$1)
echo -n lib/$1
if not if(~ $#* 2)
echo -n $2
if not
status='Can''t find lib file: '$1
}
fn template { awk -f bin/template.awk $* | rc $rcargs }
# Auth code
# TODO: check http://cookies.lcs.mit.edu/pubs/webauth:tr.pdf
allowed_user_chars='[a-zA-Z0-9_]'
# Cookie format: WERC_USER: name:timestamp:hash(name.timestamp.password)
# login_user can't be used from a template because it sets a cookie
fn login_user {
# Note: we set the cookie even if it is already there.
if(get_user $*)
set_cookie werc_user $"logged_user^':0:'^$"logged_password
}
# Check login status, if called with group arg we check membership too
fn check_user {
get_user
g=($* admin)
_status=$status
if(! ~ $"_status '')
_status=(Not logged in: $"_status)
if not if(! ~ $#* 0 && ! ~ $logged_user $* && ! grep -s '^'^$logged_user^'$' $werc_root/etc/users/$g/members >[2]/dev/null)
_status=(User $logged_user not in: $*)
status=$_status
}
# If not logged in, try to get user login info from POST or from cookie
fn get_user {
if(~ $#logged_user 0) {
if(~ $#* 2) {
user_name=$1
user_password=$2
}
if not if(~ $REQUEST_METHOD POST)
get_post_args user_name user_password
if(~ $#user_name 0) {
ifs=':' { cu=`{ifs=$difs {get_cookie werc_user} | tr -d $NEW_LINE} }
if(! ~ $#cu 0) {
user_name=$cu(1)
user_password=$cu(3)
}
}
auth_user $user_name $user_password
}
if not
status=()
}
# Check if user_name and user_password represent a valid user account
# If valid, 'log in' by setting logged_user
fn auth_user {
user_name=$1
user_password=$2
pfile=$werc_root/etc/users/$"user_name/password
if(~ $#user_name 0 || ~ $#user_password 0)
status=('Auth: missing user name or pass: '^$"user_name^' / '^$"user_password)
if not if(! test -f $pfile)
status=('Auth: cant find '^$pfile)
if not if(! test -s $pfile || ! ~ $user_password `{cat $pfile})
status=('Auth: Pass '$user_password' doesnt match '^`{cat $pfile})
if not {
logged_user=$user_name
logged_password=$user_password
dprint Auth: success
status=()
}
}
fn user_controls {
echo User: $"logged_user
}
# .md '(meta-)data' extract
fn get_md_file_attr {
sed -n '/^\* '$2': /p; /^\* '$2': /q; /^$/q' < $1
}
# File title extraction
fn get_md_title {
#sed 's/^(................................................................[^ ]*).*$/\1/g; 1q' < $1
sed -n -e '1N; /^.*\n===*$/N; /.*\n===*\n *$/!b' -e 's/\n==*\n//p' < $1
}
fn get_html_title {
t=`{sed -n '32q; s/^.*<[Tt][Ii][Tt][Ll][Ee]> *([^<]+) *(<\/[Tt][Ii][Tt][Ll][Ee]>.*)?$/\1/p' < $1}
# 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}
echo $t
}
fn get_file_title {
if (~ $1 *.md)
get_md_title $1
if not if(~ $1 *.html)
get_html_title $1
if not 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
}
}
fn ndate {
if(~ $#* 7)
date=$*(2-)
if not
date=`{date}
switch($date(2)){
case Jan; mo=01
case Feb; mo=02
case Mar; mo=03
case Apr; mo=04
case May; mo=05
case Jun; mo=06
case Jul; mo=07
case Aug; mo=08
case Sep; mo=09
case Oct; mo=10
case Nov; mo=11
case Dec; mo=12
}
switch($date(3)){
case [0-9]
da=0^$date(3)
case *
da=$date(3)
}
switch($date(5)){
case A; tz=+0100
case ADT; tz=-0300
case AFT; tz=+430
case AKDT; tz=-0800
case AKST; tz=-0900
case ALMT; tz=+0600
case AMST; tz=-0300
case AMT; tz=-0400
case ANAST; tz=+1200
case ANAT; tz=+1200
case AQTT; tz=+0500
case ART; tz=-0300
case AST; tz=-0400
case AZOST; tz=+0000
case AZOT; tz=-0100
case AZST; tz=+0500
case AZT; tz=+0400
case B; tz=+0200
case BNT; tz=+0800
case BOT; tz=-0400
case BRST; tz=-0200
case BRT; tz=-0300
case BST; tz=+0100
case BTT; tz=+0600
case C; tz=+0300
case CAST; tz=+0800
case CAT; tz=+0200
case CCT; tz=+0630
case CDT; tz=-0500
case CEST; tz=+0200
case CET; tz=+0100
case CHADT; tz=+1345
case CHAST; tz=+1245
case CKT; tz=-1000
case CLST; tz=-0300
case CLT; tz=-0400
case COT; tz=-0500
case CST; tz=-0600
case CVT; tz=-0100
case CXT; tz=+0700
case ChST; tz=+1000
case D; tz=+0400
case DAVT; tz=+0700
case E; tz=+0500
case EASST; tz=-0500
case EAST; tz=-0600
case EAT; tz=+0300
case ECT; tz=-0500
case EDT; tz=-0400
case EEST; tz=+0300
case EET; tz=+0200
case EGST; tz=+0000
case EGT; tz=-0100
case EST; tz=-0500
case ET; tz=-0500
case F; tz=+0600
case FJST; tz=+1300
case FJT; tz=+1200
case FKST; tz=-0300
case FKT; tz=-0400
case FNT; tz=-0200
case G; tz=+0700
case GALT; tz=-0600
case GAMT; tz=-0900
case GET; tz=+0400
case GFT; tz=-0300
case GILT; tz=+1200
case GMT; tz=+0000
case GST; tz=+0400
case GYT; tz=-0400
case H; tz=+0800
case HAA; tz=-0300
case HAC; tz=-0500
case HADT; tz=-0900
case HAE; tz=-0400
case HAP; tz=-0700
case HAR; tz=-0600
case HAST; tz=-1000
case HAT; tz=-0230
case HAY; tz=-0800
case HKT; tz=+0800
case HLV; tz=-0430
case HNA; tz=-0400
case HNC; tz=-0600
case HNE; tz=-0500
case HNP; tz=-0800
case HNR; tz=-0700
case HNT; tz=-0330
case HNY; tz=-0900
case HOVT; tz=+0700
case I; tz=+0900
case ICT; tz=+0700
case IDT; tz=+0300
case IOT; tz=+0600
case IRDT; tz=+0430
case IRKST; tz=+0900
case IRKT; tz=+0800
case IRST; tz=+0330
case IST; tz=+0200
case JST; tz=+0900
case K; tz=+1000
case KGT; tz=+0600
case KRAST; tz=+0800
case KRAT; tz=+0700
case KST; tz=+0900
case KUYT; tz=+0400
case L; tz=+1100
case LHDT; tz=+1100
case LHST; tz=+1030
case LINT; tz=+1400
case M; tz=+1200
case MAGST; tz=+1200
case MAGT; tz=+1100
case MART; tz=-0930
case MAWT; tz=+0500
case MDT; tz=-0600
case MHT; tz=+1200
case MMT; tz=+0630
case MSD; tz=+0400
case MSK; tz=+0300
case MST; tz=-0700
case MUT; tz=+0400
case MVT; tz=+0500
case MYT; tz=+0800
case N; tz=-0100
case NCT; tz=+1100
case NDT; tz=-0230
case NFT; tz=+1130
case NOVST; tz=+0700
case NOVT; tz=+0600
case NPT; tz=+0545
case NST; tz=-0330
case NUT; tz=-1100
case NZDT; tz=+1300
case NZST; tz=+1200
case O; tz=-0200
case OMSST; tz=+0700
case OMST; tz=+0600
case P; tz=-0300
case PDT; tz=-0700
case PET; tz=-0500
case PETST; tz=+1200
case PETT; tz=+1200
case PGT; tz=+1000
case PHOT; tz=+1300
case PHT; tz=+0800
case PKT; tz=+0500
case PMDT; tz=-0200
case PMST; tz=-0300
case PONT; tz=+1100
case PST; tz=-0800
case PT; tz=-0800
case PWT; tz=+0900
case PYST; tz=-0300
case PYT; tz=-0400
case Q; tz=-0400
case R; tz=-0500
case RET; tz=+0400
case S; tz=-0600
case SAMT; tz=+0400
case SAST; tz=+0200
case SBT; tz=+1100
case SCT; tz=+0400
case SGT; tz=+0800
case SRT; tz=-0300
case SST; tz=-1100
case T; tz=-0700
case TAHT; tz=-1000
case TFT; tz=+0500
case TJT; tz=+0500
case TKT; tz=-1000
case TLT; tz=+0900
case TMT; tz=+0500
case TVT; tz=+1200
case U; tz=-0800
case ULAT; tz=+0800
case UYST; tz=-0200
case UYT; tz=-0300
case UZT; tz=+0500
case V; tz=-0900
case VET; tz=-0430
case VLAST; tz=+1100
case VLAT; tz=+1000
case VUT; tz=+1100
case W; tz=-1000
case WAST; tz=+0200
case WAT; tz=+0100
case WDT; tz=+0900
case WEST; tz=+0100
case WET; tz=+0000
case WFT; tz=+1200
case WGST; tz=-0200
case WGT; tz=-0300
case WIB; tz=+0700
case WIT; tz=+0900
case WITA; tz=+0800
case WST; tz=+0800
case WT; tz=+0000
case X; tz=-1100
case Y; tz=-1200
case YAKST; tz=+1000
case YAKT; tz=+0900
case YAPT; tz=+1000
case YEKST; tz=+0600
case YEKT; tz=+0500
case Z; tz=+0000
}
switch($1){
case -a # rfc3339
tz=`{echo $tz | sed 's/00$/:00/'}
echo $date(6)^-$mo-$da^T^$date(4)^$tz
case -i # iso-8601 lite
echo $date(6)^-$mo-$da
case -m # rfc2822
echo $date(1)^, $da $date(2) $date(6) $date(4) $tz
case -t # iso-8601
echo $date(6)^-$mo-$da^T^$date(4)^$tz
}
}
##########################################################################
##########################################################################
#app_blog_methods = ( _post index.rss )
#fn app_blog__post {
# echo
#}
#
#app_blog___default {
# if (~ $blog)
# call_app blogpost
#}
#
## --
#app_blogpost_methods = ( comment _edit )
#
#fn app_blogpost_comment {
# call_app comments
#}
#
## --
#app_comments_methods = ( _post _edit )
#
#fn app_comments___default {
#
#}

40
werc/etc/initrc Normal file
View File

@ -0,0 +1,40 @@
# This file contains the default werc settings.
#
# DO NOT EDIT, to customize copy to etc/initrc.local and edit at will.
#
# Some settings can also be set for a specific site or directory in their
# respective _werc/config or their $masterSite/_werc/config file.
# General options
# Location of your Plan 9 from User Space installation (usually /usr/local/plan9)
PLAN9=
plan9port=$PLAN9
# If you use 9base, it should point to your 9base root, try for example:
#plan9port=/usr/lib/9base # This is the default 9base install path in Debian.
# If rc has not been copied to /bin/rc you will also need to change
# the #! line in bin/*.rc!
# Path, make sure the plan9port /bin directory is included before /bin
# Keep '.' in path! It is needed.
path=($plan9port/bin . ./bin ./bin/contrib /bin /usr/bin)
# Set this to your favorite markdown formatter, eg., markdown.pl (fltr_cache
# takes as an argument a filter, in the default configuration markdown.pl, that
# caches output) Note that some werc components assume a markdown-like
# formatter, but all major functionality should should be formatter agnostic.
#formatter=(fltr_cache markdown.pl)
formatter=(fltr_cache md2html.awk) # no perl for old men
# Enable debugging, to disable set to ()
debug=true
# Globally enabled apps
enabled_apps=()
# Default site variables, must be set in initrc.local or _werc/config, only siteTitle is required.
#masterSite=cat-v.org # Not required!
#siteTitle='cat-v'
#siteSubTitle='Considered harmful'

View File

@ -0,0 +1 @@
This is just a dummy file to force hg to preserve this directory that is used to store user and group account information.

4
werc/lib/404.tpl Normal file
View File

@ -0,0 +1,4 @@
<h1>The requested document at '<i>%($"req_path%)</i>' doesn't exist</h1>
<img src="/pub/404coffee.png" alt="404 error: coffee not found">
<h4>maybe it is in the <a href="/sitemap">sitemap</a>?</h4>
<hr>

View File

@ -0,0 +1,26 @@
<header>
<nav>
% cat `{ get_lib_file top_bar.inc }
</nav>
<h1><a href="/">%($"siteTitle%) <span id="headerSubTitle">%($"siteSubTitle%)</span></a></h1>
</header>
% if(! ~ $#handlers_bar_left 0) {
<nav id="side-bar">
% for(h in $handlers_bar_left) {
<div>
% run_handler $$h
</div>
% }
</nav>
% }
<article>
% run_handlers $handlers_body_head
% run_handler $handler_body_main
% run_handlers $handlers_body_foot
</article>
<footer>
% cat `{ get_lib_file footer.inc }
</footer>

2
werc/lib/footer.inc Normal file
View File

@ -0,0 +1,2 @@
<div><a href="http://werc.cat-v.org/">Powered by werc</a></div>

31
werc/lib/headers.tpl Normal file
View File

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html>
<head>
<title>%($pageTitle%)</title>
<link rel="stylesheet" href="/pub/style/style.css" type="text/css" media="screen, handheld" title="default">
<link rel="shortcut icon" href="/favicon.ico" type="image/vnd.microsoft.icon">
% if(test -f $sitedir/_werc/pub/style.css)
% echo ' <link rel="stylesheet" href="/_werc/pub/style.css" type="text/css" media="screen" title="default">'
<meta charset="UTF-8">
% # Legacy charset declaration for backards compatibility with non-html5 browsers.
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
% if(! ~ $#meta_description 0)
% echo ' <meta name="description" content="'$"meta_description'">'
% if(! ~ $#meta_keywords 0)
% echo ' <meta name="keywords" content="'$"meta_keywords'">'
% h = `{get_lib_file headers.inc}
% if(! ~ $#h 0)
% cat $h
%($"extraHeaders%)
</head>
<body>

8
werc/lib/top_bar.inc Normal file
View File

@ -0,0 +1,8 @@
<div class="left>
<a href="https://owen.bellz.org">escape</a> |
<a href="//withdrawal.owen.bellz.org">withdrawal</a>
</div>
<div class="right">
<a href="/sitemap">site map</a>
</div>

BIN
werc/pub/404coffee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 KiB

BIN
werc/pub/default_favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
werc/pub/style/imgs/sgl.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,292 @@
/***********************************************
* TITLE: Sinorca Alterative Screen Stylesheet *
* URI : sinorca/sinorca-screen-alt.css *
* MODIF: 2003-May-13 18:48 +0800 *
***********************************************/
/* ##### Common Styles ##### */
body {
color: black;
background-color: white;
font-family: verdana, helvetica, arial, sans-serif;
font-size: 71%; /* Enables font size scaling in MSIE */
margin: 0;
padding: 0;
}
html > body {
font-size: 8.5pt;
}
acronym, .titleTip {
border-bottom: 1px dotted rgb(153,153,153);
cursor: help;
margin: 0;
padding: 0 0 0.4px 0;
}
.doNotDisplay {
display: none;
}
.smallCaps {
font-size: 110%;
font-variant: small-caps;
}
/* ##### Header ##### */
.superHeader {
color: white;
background-color: rgb(100,135,220);
height: 2em;
}
.superHeader a {
color: white;
background-color: transparent;
text-decoration: none;
font-size: 91%;
margin: 0;
padding: 0 0.5ex 0 0.25ex;
}
.superHeader a:hover {
text-decoration: underline;
}
.superHeader .left {
position: absolute;
left: 1.5mm;
top: 0.75ex;
}
.superHeader .right {
position: absolute;
right: 1.5mm;
top: 0.75ex;
}
.midHeader {
color: rgb(39,78,144);
background-color: rgb(140,170,230);
}
.headerTitle {
font-size: 337%;
font-weight: normal;
margin: 0 0 0 4mm;
padding: 0.25ex 0;
}
.subHeader {
color: white;
background-color: rgb(0,51,153);
margin: 0;
padding: 1ex 1ex 1ex 1.5mm;
}
.subHeader a {
color: white;
background-color: transparent;
text-decoration: none;
font-weight: bold;
margin: 0;
padding: 0 0.75ex 0 0.5ex;
}
.subHeader a:hover {
text-decoration: underline;
}
.superHeader .highlight, .subHeader .highlight {
color: rgb(253,160,91);
background-color: transparent;
}
/* ##### Side Boxes ##### */
#side-bar {
width: 14em;
margin: 2.5em 0 0 1.25mm;
float: left;
clear: left;
}
body > #side-bar {
margin-left: 2.5mm; /* Circumvents a rendering bug in MSIE (6.0) */
}
.sideBarTitle {
color: white;
background-color: rgb(100,135,220);
font-weight: bold;
margin: 0;
padding: 0.4ex 0 0.4ex 0.6ex;
}
#side-bar ul {
list-style-type: none;
list-style-position: outside;
margin: 0;
padding: 0 0 2.25em 0;
}
#side-bar li {
margin: 0;
padding: 0.1ex 0; /* Circumvents a rendering bug (?) in MSIE (6.0) */
}
#side-bar a, .thisPage {
color: rgb(0,102,204);
background-color: transparent;
text-decoration: none;
font-weight: bold;
margin: 0;
padding: 1.3ex 2ex;
display: block;
}
.thisPage {
color: black;
background-color: transparent;
}
#side-bar a:hover {
color: white;
background-color: rgb(100,135,220);
text-decoration: none;
}
.sideBarText {
line-height: 1.5em;
margin: 0 0 2.5em 0;
padding: 1ex 0.5ex 0 0.5ex;
display: block;
}
.sideBarText + .sideBarText { /* Not recognised by MSIE (6.0) */
margin-top: -1.5em;
}
#side-bar .sideBarText a {
text-decoration: underline;
font-weight: normal;
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 {
color: black;
background-color: transparent;
text-align: justify;
line-height: 1.5em;
margin: -1em 0 0 15em;
padding: 0.5mm 5mm 5mm 5mm;
}
#bodyText {
margin: 0 0 0 15.5em;
padding: 2mm 5mm 2mm 5mm;
}
#main-copy p {
margin: 1em 1ex 2em 1ex;
padding: 0;
}
#main-copy a {
color: rgb(0,102,204);
background-color: transparent;
text-decoration: underline;
}
#main-copy a:hover {
text-decoration: none;
}
#main-copy h1 {
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: 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);
font-size: 91%;
margin: 0;
padding: 1em 2.5mm 2.5ex 2.5mm;
clear: both;
}
#footer .left {
text-align: left;
line-height: 1.45em;
float: left;
clear: left;
}
#footer .right {
text-align: right;
line-height: 1.45em;
}
#footer a {
color: white;
background-color: transparent;
text-decoration: underline;
}
#footer a:hover {
text-decoration: none;
}

19
werc/pub/style/style.css Normal file
View File

@ -0,0 +1,19 @@
body { display: flex; flex-wrap: wrap; font-family: Helvetica, Verdana, Arial, 'Liberation Sans', FreeSans, sans-serif; }
header { flex-basis: 100%; flex-shrink: 0; }
article { flex-basis: 60%; padding-left: 1em; }
footer { flex-basis: 100%; flex-shrink: 0; }
header nav { display: flex; justify-content: space-between; }
nav a, header a { text-decoration: none ; color: inherit; }
nav a:hover { text-decoration: underline; }
h1, h2, h3, h4, h5 { font-weight: normal; }
header h1 span { margin-left: 1em; font-size: 50%; font-style: italic; }
body > nav { flex-basis: content; padding-right: 1vw; min-width: 16em; }
nav ul { display: flex; flex-direction: column; list-style-type: none; list-style-position: outside; padding-left: 0; }
nav li ul { padding-left: 0.6em }
img { width: 100%; }
footer { display: flex; justify-content: space-between; }
@media screen and (max-width: 600px) {
textarea {
width: 100%;
}
}

330
werc/pub/style/style.werc140.css Executable file
View File

@ -0,0 +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;
}

330
werc/pub/style/style_old.css Executable file
View File

@ -0,0 +1,330 @@
/* Old Default style */
/* ##### Common Styles ##### */
body {
color: black;
XXXbackground-color: rgb(240,240,240);
background-color: white;
font-family: verdana, helvetica, arial, sans-serif;
font-size: 71%; /* Enables font size scaling in MSIE */
margin: 0;
padding: 0;
}
html > body {
font-size: 8.5pt;
}
acronym, .titleTip {
border-bottom: 1px dotted rgb(153,153,153);
cursor: help;
margin: 0;
padding: 0 0 0.4px 0;
}
.doNotDisplay {
display: none;
}
.smallCaps {
font-size: 110%;
font-variant: small-caps;
}
/* ##### Header ##### */
.superHeader {
color: white;
background-color: rgb(100,135,220);
height: 2em;
}
.superHeader a {
color: white;
background-color: transparent;
text-decoration: none;
font-size: 91%;
margin: 0;
padding: 0 0.5ex 0 0.25ex;
}
.superHeader a:hover {
text-decoration: underline;
}
.superHeader .left {
position: absolute;
left: 1.5mm;
top: 0.75ex;
}
.superHeader .right {
position: absolute;
right: 1.5mm;
top: 0.75ex;
}
.midHeader {
color: rgb(39,78,144);
background-color: rgb(140,170,230);
border: solid 0 black;
border-width: 0.3em 0;
}
.headerTitle {
color: black;
font-size: 337%;
font-weight: normal;
margin: 0 0 0 4mm;
padding: 0.25ex 0;
}
#headerSubTitle {
font-size: 50%;
font-style: italic;
}
.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;
text-decoration: none;
font-weight: bold;
margin: 0;
padding: 0 0.75ex 0 0.5ex;
}
.subHeader a:hover {
text-decoration: underline;
}
.superHeader .highlight, .subHeader .highlight {
color: rgb(253,160,91);
background-color: transparent;
}
/* ##### Side Bar ##### */
#side-bar {
width: 15em;
float: left;
clear: left;
border-right: 1px solid rgb(153,153,153);
}
#side-bar div {
border-bottom: 1px solid rgb(153,153,153);
}
.sideBarTitle {
font-weight: bold;
margin: 0 0 0.5em 2.5mm;
padding: 1em 0 0 0;
}
#side-bar ul {
list-style-type: none;
list-style-position: outside;
margin: 0;
padding: 0 0 1.1em 0;
}
#side-bar li {
margin: 0;
padding: 0.1ex 0; /* Circumvents a rendering bug (?) in MSIE 6.0 */
}
#side-bar a, .thisPage {
color: rgb(0,102,204);
background-color: transparent;
XXXtext-decoration: none;
margin: 0;
padding: 0.55em 1ex 0.55em 5mm;
display: block;
}
.thisPage {
color: black;
background-color: white;
padding-left: 5mm;
XXXborder-top: 1px solid rgb(153,153,153);
XXXborder-bottom: 1px solid rgb(153,153,153);
font-weight: 600;
}
#side-bar a:hover {
color: white;
background-color: rgb(100,135,220);
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 {
text-decoration: underline;
margin: 0;
padding: 0;
display: inline;
}
#side-bar .sideBarText a:hover {
color: rgb(0,102,204);
background-color: transparent;
text-decoration: none;
}
.lighterBackground {
color: inherit;
background-color: white;
}
/* ##### Main Copy ##### */
#main-copy {
max-width: 90em;
color: black;
background-color: white;
text-align: justify;
line-height: 1.5em;
margin: 0 0 0 15em;
padding: 0.5mm 5mm 5mm 5mm;
border-left: 1px solid rgb(153,153,153);
}
#main-copy p {
margin: 1em 1ex 2em 1ex;
padding: 0;
}
#main-copy a {
color: rgb(0,102,204);
background-color: transparent;
text-decoration: underline;
}
#main-copy a:hover {
text-decoration: none;
}
#main-copy h1 {
color: white;
background-color: rgb(100,135,220);
font-size: 100%;
font-weight: bold;
margin: 3em 0 0 0;
padding: 0.5ex 0 0.5ex 1ex;
}
#main-copy .topOfPage {
color: white;
background-color: transparent;
font-size: 91%;
font-weight: bold;
text-decoration: none;
margin: 2.5ex 1ex 0 0; /* For MSIE */
padding: 0;
float: right;
}
#main-copy > .topOfPage {
margin: 2.75ex 1ex 0 0; /* For fully standards-compliant user agents */
}
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);
font-size: 91%;
margin: 0;
padding: 1em 2.5mm 2.5ex 2.5mm;
clear: both;
}
#footer .left {
line-height: 1.45em;
float: left;
clear: left;
}
#footer .right {
text-align: right;
line-height: 1.45em;
}
#footer a {
color: white;
background-color: transparent;
text-decoration: underline;
}
#footer a:hover {
text-decoration: none;
}
/* GENERAL */
/* Spam */
.spam {
text-align: center;
}
/* Tables */
table {
border: solid 1px black;
}
th {
background-color: #abc;
border: solid 1px black;
}
td {
background-color: #def;
border: solid 1px black;
}
hr {
border-width: 0px 0px 0.1em 0px;
border-color: black;
}
.spam table, .spam th, .spam td {
border: none;
}
/* Code */
pre {
margin-left: 2em;
}

View File

29
werc/tpl/_debug.tpl Normal file
View File

@ -0,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

18
werc/tpl/_users/login.tpl Normal file
View File

@ -0,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">

67
werc/tpl/sitemap.tpl Normal file
View File

@ -0,0 +1,67 @@
<h1>Site map</h1>
%{
tmpfile=/tmp/werc_sitemap_$pid.txt
tmpfilex=/tmp/werc_sitemapx_$pid.txt
saveddf=$dirfilter
MON2NUM='s/Jan/01/; s/Feb/02/; s/Mar/03/; s/Apr/04/; s/May/05/; s/Jun/06/; s/Jul/07/; s/Aug/08/; s/Sep/09/; s/Oct/10/; s/Nov/11/; s/Dec/12/;'
fn get_mdate {
t=`{mtime $1}
t=`{date $t(1) | sed -e $MON2NUM -e 's/ ([0-9]) / 0\1 /g'} # Make sure day of the month is two digits.
echo $t(6)^'-'^$t(2)^'-'^$t(3)
}
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="'$u'">'^$"n^'</a>' $"desc '</li>'
echo $base_url^$u >> $tmpfile
echo '<url><loc>'$base_url^$u'</loc><lastmod>'^`{get_mdate $i}^'</lastmod></url>' >> $tmpfilex
if(test -d $i)
@{ listDir $i }
}
echo '</ul>'
}
}
fltr_cache listDir $sitedir/
if(test -s $tmpfile) {
mv $tmpfile $sitedir/sitemap.txt &
}
if not if(test -f $tmpfile)
rm $tmpfile
if(test -s $tmpfilex) {
{
echo '<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
cat $tmpfilex
rm $tmpfilex &
echo '</urlset>'
# TODO Enable automaic search engine update notification.
#hget 'http://google.com/ping?sitemap='^`{url_encode $base_url'/sitemap.gz'} > /dev/null
} | gzip > $sitedir/sitemap.gz &
#} > $sitedir/sitemap.xml &
}
if not if(test -f $tmpfilex)
rm $tmpfilex
%}