Compare commits

...

70 commits

Author SHA1 Message Date
f6b884259b tls by default on ircrc 2022-06-30 12:36:51 -04:00
c97daf119d Merge branch 'front' of git://git.9front.org/plan9front/plan9front into front 2022-06-30 11:42:24 -04:00
217f8d61a1 include werc 2022-06-30 11:40:21 -04:00
akw@oneirism.org
6dbfe8c356 nusb/ptp: increase payload
A silly little patch, but some ptp devices (like the camera I own) use USB3 endpoints and subsequently require the data size of the payload to be 1024(-12 for metadata). This is reflected in libgphoto2's current ptp driver.
2022-06-30 01:58:01 +00:00
Ori Bernstein
e6d31c1715 spf: limit recursion depth to prevent ddos (thanks tijay, iashiq5)
An attacker may use an infinite number of SPF referrals in his/her SPF
setting and can send an email to your mail server which would make
your SMTP server make a lot of DNS queries.  By exploiting this
vulnerability, an attacker can block your SMTP queue, flood the
associated recursive resolver, or any DNS authoritative server.

According to RFC recommendations
(https://datatracker.ietf.org/doc/html/rfc7208#section-4.6), a few DNS
lookup limits exist that an SMTP server needs to maintain while
resolving an SPF record.  That is, SPF implementations MUST limit the
total number of query-causing terms to 10 and the number of void
lookups to 2 to avoid unreasonable load on the DNS.

from:

Taejoong “Tijay” Chung (tijay@vt.edu)
Ishtiaq Ashiq (iashiq5@vt.edu)
2022-06-30 01:24:27 +00:00
58fe4bd42c Merge branch 'front' of git://git.9front.org/plan9front/plan9front into front 2022-06-29 12:08:10 -04:00
e72da2cdc8 Merge remote-tracking branch 'origin/front' into front 2022-05-31 21:03:39 -04:00
647266573c Merge remote-tracking branch 'origin' into front 2022-05-30 21:56:43 -04:00
Jacob Moody
1d09995353 kernel: be more careful about argc for /dev/drivers writes
Not crashing on 'chdev &' is important.
2022-05-30 21:55:39 -04:00
Ori Bernstein
fe2cbbb402 git: performance enhancements
Inspired by some changes made in game of trees, I've
implemented a number of speedups in git9.

First, hashing the chunks during deltification with
murmurhash instead of sha1 speeds up the delta search
significantly.

The stretch function was micro-optimized a bit as well,
since that was taking a large portion of the time when
chunking.

Finally, the full path is not stored. We only care about
grouping files with the same name and path. We don't care
about the ordering. Therefore, only the hash of the path
xored with the hash of the diretory is kept, which saves
a bunch of mallocs and string munging.

This reduces the time spent repacking some test repos
significantly.

9front:
	% time git/repack
	deltifying 97473 objects: 100%
	writing 97473 objects: 100%
	indexing 97473 objects: 100%
	58.85u 1.39s 61.82r 	 git/repack

	% time /sys/src/cmd/git/6.repack
	deltifying 97473 objects: 100%
	writing 97473 objects: 100%
	indexing 97473 objects: 100%
	43.86u 1.29s 47.51r 	 /sys/src/cmd/git/6.repack

openbsd:

	% time git/repack
	deltifying 2092325 objects: 100%
	writing 2092325 objects: 100%
	indexing 2092325 objects: 100%
	1589.48u 45.03s 1729.18r 	 git/repack

	% time /sys/src/cmd/git/6.repack
	deltifying 2092325 objects: 100%
	writing 2092325 objects: 100%
	indexing 2092325 objects: 100%
	1238.68u 41.49s 1373.15r 	 /sys/src/cmd/git/6.repack

go:
	% time git/repack
	deltifying 529507 objects: 100%
	writing 529507 objects: 100%
	indexing 529507 objects: 100%
	345.32u 7.71s 369.25r     git/repack

	% time /sys/src/cmd/git/6.repack
	deltifying 529507 objects: 100%
	writing 529507 objects: 100%
	indexing 529507 objects: 100%
	248.07u 4.47s 257.59r 	 /sys/src/cmd/git/6.repack
2022-05-30 21:55:39 -04:00
Jacob Moody
674fcda107 aux/listen: Allow per service namespace files 2022-05-30 21:55:39 -04:00
Jacob Moody
9fe925eaa5 kernel: add /rc to devroot
This makes it much easier to run a rc
program without needing to bind in all of '#s/boot'.
2022-05-30 21:55:39 -04:00
Jacob Moody
96d07474b8 ip/ftpd: use chdev in /lib/namespace.ftp
/lib/namespace.ftp is used for anonmyous logins.
2022-05-30 21:55:39 -04:00
Jacob Moody
449fe7d8ed auth/newns: add chdev command 2022-05-30 21:55:39 -04:00
Jacob Moody
e6c589312e add chdev(1) 2022-05-30 21:55:39 -04:00
Jacob Moody
00b7d2385a kernel: add chdev command to devcons 2022-05-30 21:55:39 -04:00
glenda
54328d2e65 /lib/theo: This is a bug in a diff I put into snapshots. 2022-05-30 21:55:39 -04:00
Michael Forney
9b19835bd4 tmparse: remove incorrect isalpha definition
Checking the range of c|0x60 incorrectly classifies many characters
as alphabetic (digits, control characters 0x01-0x20, and punctuation
characters '!'-':'). This prevents tmparse from parsing dates with
a timezone bounded by those characters (for example, "12:11:56 (PDT)").

Instead, just reuse the isalpha macro provided by ctype.h.
2022-05-30 21:55:39 -04:00
Ori Bernstein
24e5308c2a kbmap: add latin american keymap (thanks ssf) 2022-05-30 21:55:39 -04:00
Ori Bernstein
7c71da0344 diff: clean up some comments
I did the tidying and fixes. Let's remove those
comments.
2022-05-30 21:55:39 -04:00
Ori Bernstein
8eb33669cd patch(1): remove bell labs patch scripts
bell labs is dead, and nobody is listening; 9front
has our own patch submission proces.
2022-05-30 21:55:39 -04:00
Ori Bernstein
8eedca7656 diff: retain original file names
When diffing non-regular files, like /dev/null,
pipes, and similar, diff will generate a temp
file to diff against. This is the right thing
to do, but the temp file leaks into the diff.

This patch retains the original file name all
the way through to diff output.
2022-05-30 21:55:39 -04:00
Ori Bernstein
1bb7aa60c4 diff: fix style
remove useless macros, bring formatting closer
to regular 9front formatting, and use the
ARGBEGIN/ARGEND macros. No functional change.
2022-05-30 21:55:39 -04:00
Ori Bernstein
9a1428f41a git: improve error on short read
we don't recover from an invalid packet, so just
sysfatal with a useful message.
2022-05-30 21:55:38 -04:00
Jacob Moody
8fbfba1ecf aux/listen: remove Service struct
An announcing process already knows what port
it announced on, we don't need to derive it
from every connection.
2022-05-30 21:55:38 -04:00
Jacob Moody
1da69597bd aux/listen: remove argument clutter
addr and proto are globals. protodir should be
a global. let's also avoid reading the remote
addr more then once.
2022-05-30 21:55:38 -04:00
Jacob Moody
a1e3ba1b6d aux/listen: remove unused cpu global 2022-05-30 21:55:38 -04:00
Jacob Moody
9e414386d4 kernel: remove dead case from devpipe
This code is checking the return of devwalk for
a walk resulting in a clone of an open pipe file. However,
devclone ensures that the chan we are cloning is not
currently open.
2022-05-30 21:55:38 -04:00
Jacob Moody
5f49f8ac9c aux/listen: remove special cases for depreciated datakit
The getserv function has remained mostly unchanged since
1ed. These checks are for handling string local ports
or lack thereof. This was used by datakit at the time,
where 'ports' were service strings. This made the default
datakit service 'login' when a connector did not give
a specific service. The checks for directory traversal also
seemed specifically to guard against a connector from providing
a malicious service string. With datakit gone all current protocols
use numerics as ports so these checks and defaults become uneeded.

1ed reference:
https://github.com/plan9foundation/plan9/tree/1e-1992-09-21
2022-05-30 21:55:38 -04:00
Ori Bernstein
b360a7c765 git/merge: correctly preserve permissions when merging
when doing a 3 way merge of a file, we also need to do a
merge of the permission bits to avoid clobberign them.
2022-05-30 21:55:38 -04:00
Sigrid Solveig Haflínudóttir
2bb988b1e2 icanhasvmx: our vmx requires unrestricted guest too 2022-05-30 21:55:38 -04:00
Jacob Moody
2c7f86e0d4 fork(2): fix commas in RFNOMNT references 2022-05-30 21:55:38 -04:00
cinap_lenrek
ecb5eab76b imx8: add xhci driver for the soc
This is mostly a copy of port/usbxhci.c with PCIWADDR() replaced
by PADDR() and the pci specific code stripped out.

This could be refactored at a later time.

There is a gpio line for the main hub reset that needs to be
asserted and some power management functions that are currently
done by u-boot (using "usb start" command).

We will do these ourselfs once we have the infrastructure for
it in place.
2022-05-30 21:55:38 -04:00
Sigrid Solveig Haflínudóttir
577a537d65 g: add .ha (Hare) 2022-05-30 21:55:38 -04:00
Jacob Moody
760f18ff4a kernel: disable wstat for devpipe 2022-05-30 21:55:38 -04:00
Jacob Moody
11c0f4e7dd fork(2): fix parens in RFNOMNT references (thanks Amavect) 2022-05-30 21:55:38 -04:00
Xiao-Yong Jin
5eda53a050 vt: increase buffer size
This patch increases the buffer sizes in vt(1) to reduce
the latency from the roundtrip between terminal and cpu
server.
2022-05-30 21:55:38 -04:00
Jacob Moody
32235c2e41 fork(2): Document RFNOMNT edge cases. 2022-05-30 21:55:37 -04:00
Alex Musolino
dcbede5281 inst/prepdisk: fix invocations of test(1)
Test(1) was fixed some time ago [1] to properly parse all of its
command line arguments.  As such, we need to be more careful about
using test(1) with globbing patterns.

[1] changeset b562b269ce
2022-05-30 21:55:37 -04:00
qwx
fc17fc5151 /rc/bin: amend usage misdirections (thanks umbraticus) 2022-05-30 21:55:37 -04:00
cinap_lenrek
5a7ed2e652 uartimx: fix interrupts
need to turn off transmitter fifo interrupt when we run out of
data to transmit, and set rx fifo ready fifo threshold to one.
2022-05-30 21:55:37 -04:00
cinap_lenrek
cb8e638d2a imx8: implement psci calls for system reset and multicore startup 2022-05-30 21:55:37 -04:00
cinap_lenrek
cb92827769 remove empty /sys/src/cmd/cpu.c 2022-05-30 21:55:37 -04:00
cinap_lenrek
22934e123c imx8: add work in progress i.MX8MQ kernel for the mntreform2 laptop
This is a work in progress port to the mntreform2 laptop.

Working so far:

- mmu (same as raspberry pi 3b+)
- arm generic timer
- gicv3
- uart1
- enet

With access to the uart, one can netboot this kernel in u-boot
using the following commands:

> dhcp
> bootm
2022-05-30 21:55:37 -04:00
cinap_lenrek
08a2cd30ba kernel: fix noteid change race condition from devproc while forking (thanks joe7)
devproc allows changing the noteid of another process
which opens a race condition in sysrfork(), when deciding
to inherit the noteid of "up" to the child and calling
pidalloc() later to take the reference, the noteid could
have been changed and the childs noteid could have been
freed already in the process.

this bug can only happen when one writes the /proc/n/noteid
file of a another process than your own that is in the
process of forking.

the noteid changing functionality of devproc seems questinable
and seems to be only used by ape's setpgrid() implementation.
2022-05-30 21:55:37 -04:00
umbraticus@prosimetrum.com
7d01c96ada sam: ignore autoindent in cmd window 2022-05-30 21:55:37 -04:00
phil9
4148ebcf0e mothra: make scrollbar style consistent with other applications
Scrollbar was drawn using a wide dark gutter over a white background
	whereas other applications (window, sam, ...) use a thinner scrollbar with an
	inverse colorscheme.
	This makes the scrollbar more consistent with other 9front applications.
2022-05-30 21:55:37 -04:00
phil9
cc9d153723 mothra: enable mousewheel scrolling in history list
In the history list, scrolling was only possible using the scrollbar
	but not the mousewheel like in the text panel.
2022-05-30 21:55:37 -04:00
Ori Bernstein
826d64c11c rc: squelch 'Write error' warning
When writing to a large variable in /env, we
spam 'Write error', in spite of the env var
working. This is new behavior, as of commit
b90036a062.

It produces a lot of scary, noisy warnings,
which are probably bogus.
2022-05-30 21:55:37 -04:00
Ori Bernstein
71d2bfabfc git/pull: remove '-b' and '-a' option
we do the right thing by default now, let's not
add knobs that nobody cares about.
2022-05-30 21:55:37 -04:00
Ori Bernstein
fd3ab99fa9 git/common.rc: create required directories
we forgot a couple of directories when branching.
2022-05-30 21:55:37 -04:00
Ori Bernstein
4f3d5d083b git: rename internal 'git/fetch' plumbing to 'git/get'
This caused some confusion, so to make it clear that
it's plumbing and has nothing to do with 'git fetch',
rename it.
2022-05-30 21:55:37 -04:00
Ori Bernstein
0375d0a3ab git/send: fill in 'theirs' object, even if we miss it
When pushing, git/send would sometimes decide we had all the
objects that we'd need to update the remote, and would try
to pack and send the entire history of the repository. This
is because we only set the 'theirs' ref when we had the object.

If we didn't have the object, we would set a zero hash,
then when deciding if we needed to force, we would think
that we were updating a new branch and send everything,
which would fail to update the remote.
2022-05-30 21:55:37 -04:00
Ori Bernstein
7ab99af0ea git/serve: log correct error message
Sending the packet on failure could junk the errstr,
so set it after we send the message.
2022-05-30 21:55:37 -04:00
Ori Bernstein
dba98f649c git/merge: preserve exec bit correctly
A while ago, qwx noticed that we clobbered the exec
bit when merging files. This is not what we want, so
we changed the operator precedence to avoid merging
dirty files implicitly.

But we do want to merge, because it's convenient for
maintaining permissions. So, instead, we should do a
3 way merge of the exec bit.

This patch does that, as well as reverting the rollback
of that change.

While we're here, we adjust the timestamps correctly
in git/branch.

This requires changes to git/fs, because without an open
handler, lib9p allows opening any file with any mode,
which confuses 'test -x'.
2022-05-30 21:55:36 -04:00
Ori Bernstein
6949c4a707 git/pull: fetch all branches (please test)
there was a diff that went in a while ago to improve
this, but it got backed out because it encounters a
bug in upstream git -- the spec says that a single
ACK should be sent when not using multi-ack modes,
but they send back multiple ones.

This commit brings back the functionality, and works
around the upstream git bug in two different ways.

First, it skips the packets up until it finds the
start of a pack header.

Second, it deduplicates the want messages, which
is what seems to trigger the duplicate ACKs that
cause us trouble.
2022-05-30 21:55:36 -04:00
umbraticus@prosimetrum.com
6730cc562d make = command's output plumbable
This patch makes sam's = cmd output what seems to me a more
useful (plumbable) format: /full/path/to/file:addr , where
addr is line(s) under = and rune(s) under =#.
2022-05-30 21:55:36 -04:00
glenda
c7e0f3b65f /rc/bin/9fs: case 9front: restore extra/ and iso/ 2022-05-30 21:55:36 -04:00
Michael Forney
d2942d4de3 git/import: squash leading/trailing/consecutive blanks and strip trailing space
This fixes importing patches with multiline commit messages generated
by git-format-patch.  It also matches commit message sanitation done
by git-am.
2022-05-30 21:55:36 -04:00
glenda
fe1b02113e /rc/bin/9fs: fix case 9config for new contrib.9front.org server. 2022-05-30 21:55:36 -04:00
cinap_lenrek
36b0897a42 aux/aout2uimage: fix text section size rounding (for data section alignment)
the a.out header is not considered part of the image and
should not be loaded. it also has no contribution to the
rounding of the text section size.
2022-05-30 21:55:36 -04:00
rodri
ddabc765ca 9pqueue(2): fix typo 2022-05-30 21:55:36 -04:00
cinap_lenrek
185ced3d1e sol(8): fix example (vncv vs vnc) 2022-05-30 21:55:36 -04:00
cinap_lenrek
4d1c1f5c6e ip/sol: handle timeout on password entry for digest auth 2022-05-30 21:55:36 -04:00
cinap_lenrek
eb81b7602c ip/torrent: fix silly readn() error check (thanks pr) 2022-05-30 21:55:36 -04:00
0b95092e3d add stats.mono 2022-04-16 22:24:37 -04:00
b070185c00 add dus 2022-04-16 22:22:06 -04:00
ada39fd98c newuser script 2022-04-16 22:17:11 -04:00
5e89dcb387 Merge branch 'newpatch' into front 2022-04-16 21:50:35 -04:00
be4132b248 add patches to git 2022-04-16 21:49:41 -04:00
90 changed files with 7506 additions and 50 deletions

10
rc/bin/dus Executable file
View file

@ -0,0 +1,10 @@
#!/bin/rc
# dus - disk usage summary for current dir
# usage: dus
du -s * | sort -nrk 1 | awk '{
if ($1 > 1073741824) printf("%7.2f %s\t%s\n", $1/1073741824, "Tb", $2)
else if ($1 > 1048576) printf("%7.2f %s\t%s\n", $1/1048576, "Gb", $2)
else if ($1 > 1024) printf("%7.2f %s\t%s\n", $1/1024, "Mb", $2)
else printf("%7.2f %s\t%s\n", $1, "Kb", $2)
}'

View file

@ -2,14 +2,14 @@
rfork ne rfork ne
server=irc.oftc.net server=irc.libera.chat
port=6667 port=6697
realname='<nil>' realname='<nil>'
target='' target=''
netdir=() netdir=()
nick=$user nick=$user
pass=() pass=()
tls=0 tls=1
serverpass=() serverpass=()
fn sighup { fn sighup {
@ -212,7 +212,7 @@ while (~ $1 -*) {
target=$2 target=$2
shift shift
case -T case -T
tls=1 tls=0
case -* case -*
echo 'usage: ircrc [-p port] [-P server password] [-r realname] [-t target] [-n nick] [-T] [server]' >[1=2] echo 'usage: ircrc [-p port] [-P server password] [-r realname] [-t target] [-n nick] [-T] [server]' >[1=2]
exit 'usage' exit 'usage'
@ -222,22 +222,23 @@ while (~ $1 -*) {
if (~ $#* 1) { if (~ $#* 1) {
switch ($1) { switch ($1) {
case bitlbee case libera
server=im.bitlbee.org server=irc.libera.chat
case oftc case vulp
server=irc.oftc.net server=va.furryplace.eu
case * case *
server=$1 server=$1
} }
} }
title title
userpass=`{auth/userpasswd 'server='^$server^' service=irc user='^$nick >[2]/dev/null} #userpass=`{auth/userpasswd 'server='^$server^' service=irc user='^$nick >[2]/dev/null}
if(~ $#userpass 2) { if(~ $#userpass 2) {
nick=$userpass(1) nick=$userpass(1)
pass=$userpass(2) pass=$userpass(2)
} }
p='/n/ircrc' p='/n/ircrc'
bind '#|' $p bind '#|' $p
echo connecting to tcp!$server!$port... echo connecting to tcp!$server!$port...

View file

@ -20,6 +20,7 @@ cat > lib/profile <<!
bind -qa $x^home/bin/rc /bin bind -qa $x^home/bin/rc /bin
bind -qa $x^home/bin/$x^cputype /bin bind -qa $x^home/bin/$x^cputype /bin
font=/lib/font/bit/vga/unicode.font font=/lib/font/bit/vga/unicode.font
fn mothra { /bin/mothra -a $* }
switch($x^service){ switch($x^service){
case terminal case terminal
webcookies webcookies
@ -29,7 +30,7 @@ case terminal
echo -n 'res 3' > '#m/mousectl' echo -n 'res 3' > '#m/mousectl'
prompt=('term% ' ' ') prompt=('term% ' ' ')
fn term%{ $x^* } fn term%{ $x^* }
rio rio -sb -i riostart
case cpu case cpu
bind /mnt/term/dev/cons /dev/cons bind /mnt/term/dev/cons /dev/cons
bind -q /mnt/term/dev/consctl /dev/consctl bind -q /mnt/term/dev/consctl /dev/consctl
@ -46,6 +47,8 @@ case cpu
prompt=('cpu% ' ' ') prompt=('cpu% ' ' ')
fn cpu%{ $x^* } fn cpu%{ $x^* }
if(! test -e /mnt/term/dev/wsys){ if(! test -e /mnt/term/dev/wsys){
prompt=('draw% ' ' ')
fn draw%{ $x^* }
# call from drawterm # call from drawterm
if(test -e /mnt/term/dev/secstore){ if(test -e /mnt/term/dev/secstore){
auth/factotum -n auth/factotum -n
@ -57,12 +60,20 @@ case cpu
webcookies webcookies
webfs webfs
plumber plumber
rio rio -sb -i riostart
} }
case con case con
prompt=('cpu% ' ' ') prompt=('cpu% ' ' ')
} }
! !
cat > bin/rc/riostart <<!
#!/bin/rc
window 0,0,161,61 stats -lm
window -miny 130
# run a system shell on the serial console
~ $#console 0 || window -scroll console
!
cat > lib/plumbing <<! cat > lib/plumbing <<!
# to update: cp /usr/$user/lib/plumbing /mnt/plumb/rules # to update: cp /usr/$user/lib/plumbing /mnt/plumb/rules

View file

@ -45,7 +45,7 @@ struct Ptprpc
uchar type[2]; uchar type[2];
uchar code[2]; uchar code[2];
uchar transid[4]; uchar transid[4];
uchar d[500]; uchar d[1012];
}; };
struct Node struct Node

9
sys/src/cmd/rio/col.h Normal file
View file

@ -0,0 +1,9 @@
enum {
Colrioback,
Numcolors,
};
extern Image *col[Numcolors];
void themeload(char *s, int n);
char *themestring(int *n);

View file

@ -14,6 +14,7 @@ enum
Qscreen, Qscreen,
Qsnarf, Qsnarf,
Qtext, Qtext,
Qtheme,
Qwctl, Qwctl,
Qwindow, Qwindow,
Qwsys, /* directory of window directories */ Qwsys, /* directory of window directories */
@ -295,7 +296,6 @@ Cursor whitearrow;
Cursor query; Cursor query;
Cursor *corners[9]; Cursor *corners[9];
Image *background;
Image *cols[NCOL]; Image *cols[NCOL];
Image *titlecol; Image *titlecol;
Image *lighttitlecol; Image *lighttitlecol;
@ -334,3 +334,5 @@ int snarfversion; /* updated each time it is written */
int messagesize; /* negotiated in 9P version setup */ int messagesize; /* negotiated in 9P version setup */
int shiftdown; int shiftdown;
int debug; int debug;
#include "col.h"

View file

@ -172,10 +172,61 @@ Cursor *corners[9] = {
&bl, &b, &br, &bl, &b, &br,
}; };
enum {
Noredraw = 1,
Rgbcol = 2,
Imagecol = 3,
};
typedef struct Color Color;
struct Color {
char *id;
int type;
union {
u32int rgb;
char *path;
};
int flags;
};
static Color theme[Numcolors] = {
[Colrioback] = {"rioback", Rgbcol, {0x777777}, 0},
};
Image *col[Numcolors];
static char *
readall(int f, int *osz)
{
int bufsz, sz, n;
char *s;
bufsz = 1023;
s = nil;
for(sz = 0;; sz += n){
if(bufsz-sz < 1024){
bufsz *= 2;
s = realloc(s, bufsz);
}
if((n = readn(f, s+sz, bufsz-sz-1)) < 1)
break;
}
if(n < 0 || sz < 1){
free(s);
return nil;
}
s[sz] = 0;
*osz = sz;
return s;
}
void void
iconinit(void) iconinit(void)
{ {
background = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x777777FF); int i, f, sz;
char *s;
/* greys are multiples of 0x11111100+0xFF, 14* being palest */ /* greys are multiples of 0x11111100+0xFF, 14* being palest */
cols[BACK] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xFFFFFFFF^reverse); cols[BACK] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xFFFFFFFF^reverse);
@ -201,4 +252,97 @@ iconinit(void)
holdcol = dholdcol; holdcol = dholdcol;
else else
holdcol = paleholdcol; holdcol = paleholdcol;
if((f = open("/dev/theme", OREAD|OCEXEC)) >= 0){
if((s = readall(f, &sz)) != nil)
themeload(s, sz);
free(s);
close(f);
}
for (i = 0; i < nelem(col); i++){
if(col[i] == nil)
col[i] = allocimage(display, Rect(0,0,1,1), RGB24, 1, theme[i].rgb<8|0x777777ff);
}
}
void redraw(void);
void
themeload(char *s, int n)
{
int i, fd;
char *t, *a[2], *e, *newp;
Image *newc, *repl;
u32int rgb;
if((t = malloc(n+1)) == nil)
return;
memmove(t, s, n);
t[n] = 0;
for(s = t; s != nil && *s; s = e){
if((e = strchr(s, '\n')) != nil)
*e++ = 0;
if(tokenize(s, a, 2) == 2){
for(i = 0; i < nelem(theme); i++) {
if(strcmp(theme[i].id, a[0]) == 0) {
newc = nil;
if(a[1][0] == '/'){
if((fd = open(a[1], OREAD)) >= 0){
if ((newc = readimage(display, fd, 0)) == nil)
goto End;
close(fd);
if ((repl = allocimage(display, Rect(0, 0, Dx(newc->r), Dy(newc->r)), RGB24, 1, 0x000000ff)) == nil)
goto End;
if (theme[i].type == Imagecol)
free(theme[i].path);
if ((newp = strdup(a[1])) == nil)
goto End;
theme[i].type = Imagecol;
theme[i].path = newp;
draw(repl, repl->r, newc, 0, newc->r.min);
freeimage(newc);
newc = repl;
}
}else{
rgb = strtoul(a[1], nil, 16);
if((newc = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, rgb<<8 | 0xff)) != nil) {
if (theme[i].type == Imagecol)
free(theme[i].path);
theme[i].type = Rgbcol;
theme[i].rgb = rgb;
}
}
if(new != nil){
freeimage(col[i]);
col[i] = newc;
}
break;
}
}
}
}
End:
free(t);
redraw();
}
char *
themestring(int *n)
{
char *s, *t, *e;
int i;
if((t = malloc(512)) != nil){
s = t;
e = s+512;
for(i = 0; i < nelem(theme); i++)
if (theme[i].type == Rgbcol)
s = seprint(s, e, "%s\t%06ux\n", theme[i].id, theme[i].rgb);
else if (theme[i].type == Imagecol)
s = seprint(s, e, "%s\t%s\n", theme[i].id, theme[i].path);
*n = s - t;
}
return t;
} }

View file

@ -33,6 +33,7 @@ Dirtab dirtab[]=
{ "screen", QTFILE, Qscreen, 0400 }, { "screen", QTFILE, Qscreen, 0400 },
{ "snarf", QTFILE, Qsnarf, 0600 }, { "snarf", QTFILE, Qsnarf, 0600 },
{ "text", QTFILE, Qtext, 0600 }, { "text", QTFILE, Qtext, 0600 },
{ "theme", QTFILE, Qtheme, 0600 },
{ "wdir", QTFILE, Qwdir, 0600 }, { "wdir", QTFILE, Qwdir, 0600 },
{ "wctl", QTFILE, Qwctl, 0600 }, { "wctl", QTFILE, Qwctl, 0600 },
{ "window", QTFILE, Qwindow, 0400 }, { "window", QTFILE, Qwindow, 0400 },

View file

@ -39,6 +39,7 @@ int threadrforkflag = 0; /* should be RFENVG but that hides rio from plumber */
void mousethread(void*); void mousethread(void*);
void keyboardthread(void*); void keyboardthread(void*);
void winclosethread(void*); void winclosethread(void*);
void themethread(void*);
void initcmd(void*); void initcmd(void*);
Channel* initkbd(void); Channel* initkbd(void);
@ -199,10 +200,10 @@ threadmain(int argc, char *argv[])
kbdchan = initkbd(); kbdchan = initkbd();
if(kbdchan == nil) if(kbdchan == nil)
error("can't find keyboard"); error("can't find keyboard");
wscreen = allocscreen(screen, background, 0); wscreen = allocscreen(screen, col[Colrioback], 0);
if(wscreen == nil) if(wscreen == nil)
error("can't allocate screen"); error("can't allocate screen");
draw(view, viewr, background, nil, ZP); draw(view, viewr, col[Colrioback], nil, ZP);
flushimage(display, 1); flushimage(display, 1);
timerinit(); timerinit();
@ -580,6 +581,48 @@ wtopcmp(void *a, void *b)
return (*(Window**)a)->topped - (*(Window**)b)->topped; return (*(Window**)a)->topped - (*(Window**)b)->topped;
} }
void
redraw(void)
{
Image *im;
int i, j;
Rectangle r;
Point o, n;
Window *w;
view = screen;
draw(view, view->r, col[Colrioback], nil, ZP);
o = subpt(viewr.max, viewr.min);
n = subpt(view->clipr.max, view->clipr.min);
qsort(window, nwindow, sizeof(window[0]), wtopcmp);
for(i=0; i<nwindow; i++){
w = window[i];
r = rectsubpt(w->i->r, viewr.min);
r.min.x = (r.min.x*n.x)/o.x;
r.min.y = (r.min.y*n.y)/o.y;
r.max.x = (r.max.x*n.x)/o.x;
r.max.y = (r.max.y*n.y)/o.y;
r = rectaddpt(r, view->clipr.min);
if(!goodrect(r))
r = rectsubpt(w->i->r, subpt(w->i->r.min, r.min));
for(j=0; j<nhidden; j++)
if(w == hidden[j])
break;
frinittick(w);
incref(w);
if(j < nhidden){
im = allocimage(display, r, screen->chan, 0, DNofill);
r = ZR;
} else
im = allocwindow(wscreen, r, Refbackup, DNofill);
if(im)
wsendctlmesg(w, Reshaped, r, im);
wclose(w);
}
viewr = view->r;
flushimage(display, 1);
}
void void
resized(void) resized(void)
{ {
@ -594,10 +637,10 @@ resized(void)
freescrtemps(); freescrtemps();
view = screen; view = screen;
freescreen(wscreen); freescreen(wscreen);
wscreen = allocscreen(screen, background, 0); wscreen = allocscreen(screen, col[Colrioback], 0);
if(wscreen == nil) if(wscreen == nil)
error("can't re-allocate screen"); error("can't re-allocate screen");
draw(view, view->r, background, nil, ZP); draw(view, view->r, col[Colrioback], nil, ZP);
o = subpt(viewr.max, viewr.min); o = subpt(viewr.max, viewr.min);
n = subpt(view->clipr.max, view->clipr.min); n = subpt(view->clipr.max, view->clipr.min);
qsort(window, nwindow, sizeof(window[0]), wtopcmp); qsort(window, nwindow, sizeof(window[0]), wtopcmp);

View file

@ -502,6 +502,10 @@ xfidwrite(Xfid *x)
memmove(w->label, x->data, cnt); memmove(w->label, x->data, cnt);
break; break;
case Qtheme:
themeload(x->data, cnt);
break;
case Qmouse: case Qmouse:
if(w!=input || Dx(w->screenr)<=0) if(w!=input || Dx(w->screenr)<=0)
break; break;
@ -746,6 +750,10 @@ xfidread(Xfid *x)
t = wcontents(w, &n); t = wcontents(w, &n);
goto Text; goto Text;
case Qtheme:
t = themestring(&n);
goto Text;
Text: Text:
if(off > n){ if(off > n){
off = n; off = n;

View file

@ -29,17 +29,17 @@ flstart(Rectangle r)
lDrect = r; lDrect = r;
/* Main text is yellowish */ /* Main text is yellowish */
maincols[BACK] = allocimagemix(display, DPaleyellow, DWhite); maincols[BACK] = display->black;
maincols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow); maincols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x555555FF);
maincols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DYellowgreen); maincols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, 0x222222FF);
maincols[TEXT] = display->black; maincols[TEXT] = display->white;
maincols[HTEXT] = display->black; maincols[HTEXT] = display->black;
/* Command text is blueish */ /* Command text is blueish */
cmdcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite); cmdcols[BACK] = display->black;
cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen); cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x555555FF);
cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DPurpleblue); cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, 0x222222FF);
cmdcols[TEXT] = display->black; cmdcols[TEXT] = display->white;
cmdcols[HTEXT] = display->black; cmdcols[HTEXT] = display->black;
} }
@ -83,18 +83,19 @@ flinit(Flayer *l, Rectangle r, Font *ft, Image **cols)
draw(screen, l->entire, l->f.cols[BACK], nil, ZP); draw(screen, l->entire, l->f.cols[BACK], nil, ZP);
scrdraw(l, 0L); scrdraw(l, 0L);
flborder(l, 0); flborder(l, 0);
flrefresh(l, l->entire, 0);
} }
void void
flclose(Flayer *l) flclose(Flayer *l)
{ {
if(l->visible == All) if(l->visible == All)
draw(screen, l->entire, display->white, nil, ZP); draw(screen, l->entire, display->black, nil, ZP);
else if(l->visible == Some){ else if(l->visible == Some){
if(l->f.b == 0) if(l->f.b == 0)
l->f.b = allocimage(display, l->entire, screen->chan, 0, DNofill); l->f.b = allocimage(display, l->entire, screen->chan, 0, DNofill);
if(l->f.b){ if(l->f.b){
draw(l->f.b, l->entire, display->white, nil, ZP); draw(l->f.b, l->entire, display->black, nil, ZP);
flrefresh(l, l->entire, 0); flrefresh(l, l->entire, 0);
} }
} }
@ -362,7 +363,7 @@ flresize(Rectangle dr)
if(0 && Dx(dr)==Dx(olDrect) && Dy(dr)==Dy(olDrect)) if(0 && Dx(dr)==Dx(olDrect) && Dy(dr)==Dy(olDrect))
move = 1; move = 1;
else else
draw(screen, lDrect, display->white, nil, ZP); draw(screen, lDrect, display->black, nil, ZP);
for(i=0; i<nllist; i++){ for(i=0; i<nllist; i++){
l = llist[i]; l = llist[i];
l->lastsr = ZR; l->lastsr = ZR;

View file

@ -246,22 +246,12 @@ getr(Rectangle *rp)
Point p; Point p;
Rectangle r; Rectangle r;
*rp = getrect(3, mousectl); *rp = screen->r;
if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
p = rp->min; p = rp->min;
r = cmd.l[cmd.front].entire; r = cmd.l[cmd.front].entire;
*rp = screen->r; if(cmd.nwin==1)
if(cmd.nwin==1){
if (p.y <= r.min.y)
rp->max.y = r.min.y;
else if (p.y >= r.max.y)
rp->min.y = r.max.y; rp->min.y = r.max.y;
if (p.x <= r.min.x)
rp->max.x = r.min.x;
else if (p.x >= r.max.x)
rp->min.x = r.max.x;
}
}
return rectclip(rp, screen->r) && return rectclip(rp, screen->r) &&
rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40; rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
} }

View file

@ -207,7 +207,7 @@ sweeptext(int new, int tag)
if(getr(&r) && (t = malloc(sizeof(Text)))){ if(getr(&r) && (t = malloc(sizeof(Text)))){
memset((void*)t, 0, sizeof(Text)); memset((void*)t, 0, sizeof(Text));
current((Flayer *)0); //current((Flayer *)0);
flnew(&t->l[0], gettext, 0, (char *)t); flnew(&t->l[0], gettext, 0, (char *)t);
flinit(&t->l[0], r, font, maincols); /*bnl*/ flinit(&t->l[0], r, font, maincols); /*bnl*/
t->nwin = 1; t->nwin = 1;

View file

@ -46,7 +46,7 @@ getscreen(int argc, char **argv)
if(t != nil) if(t != nil)
maxtab = strtoul(t, nil, 0); maxtab = strtoul(t, nil, 0);
free(t); free(t);
draw(screen, screen->clipr, display->white, nil, ZP); draw(screen, screen->clipr, display->black, nil, ZP);
} }
int int

View file

@ -0,0 +1,7 @@
</$objtype/mkfile
BIN=/$objtype/bin
TARG=stats.mono
OFILES=stats.$O
</sys/src/cmd/mkone

1390
sys/src/cmd/stats.mono/stats.c Executable file

File diff suppressed because it is too large Load diff

View file

@ -387,7 +387,7 @@ lower(char *s)
} }
int int
spfquery(Squery *x, char *d, int include) spfquery(Squery *x, char *d, int include, int depth)
{ {
char *s, **t, *r, *p, *q, buf[10]; char *s, **t, *r, *p, *q, buf[10];
int i, n, c; int i, n, c;
@ -398,6 +398,10 @@ spfquery(Squery *x, char *d, int include)
fprint(2, "spf: include loop: %s (%s)\n", d, inc->s); fprint(2, "spf: include loop: %s (%s)\n", d, inc->s);
return -1; return -1;
} }
if(depth >= 10){
fprint(2, "spf: too much recursion %s\n", d);
return -1;
}
s = spffetch(x, d); s = spffetch(x, d);
if(!s) if(!s)
return -1; return -1;
@ -457,7 +461,7 @@ spfquery(Squery *x, char *d, int include)
if(rflag) if(rflag)
fprint(2, "I> %s\n", q); fprint(2, "I> %s\n", q);
addbegin(mod, r, q); addbegin(mod, r, q);
if(spfquery(x, q, 1) == -1){ if(spfquery(x, q, 1, depth+1) == -1){
ditch(); ditch();
addfail(); addfail();
}else }else
@ -704,7 +708,7 @@ main(int argc, char **argv)
goto loop; goto loop;
spfinit(&q, d, argc, argv); /* or s? */ spfinit(&q, d, argc, argv); /* or s? */
addbegin('+', ".", s); addbegin('+', ".", s);
if(spfquery(&q, s, 0) != -1) if(spfquery(&q, s, 0, 0) != -1)
break; break;
} }
if(eflag && nspf) if(eflag && nspf)

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
%}