#!/bin/awk -f
BEGIN {
srand();
if ((statuscode = ENVIRON["statuscode"]) && statuscode ~ "^[345][0-9][0-9]") {
errorpage(statuscode);
exit 0;
}
indexpath = "./index.txt";
if (system("test -f " indexpath)) {
listingpage();
exit 0;
}
ARGC = 2;
ARGV[1] = indexpath;
}
function errorpage(code) {
if (code == "302") {
skipend = 1;
return;
}
if (code == "400") title = "Bad request";
else if (code == "404") title = "Not found";
else if (code == "405") title = "Method not allowed";
else if (code == "414") title = "URI too long";
else title = "Error";
onbegin();
onparagraph("Your destination could not be found.");
onend();
}
function listingpage() {
"pwd | xargs basename" | getline cwdname; # This may leak the root directory name.
title = capitalized(cwdname);
onbegin();
onlink("..");
ls = "ls -pQ";
while ((ls | getline direntry) > 0) {
entryuri = system("test -d " direntry) == 0 ? sprintf("./%s/", direntry) : sprintf("./%s", direntry);
onlink(entryuri);
}
onend();
}
function sanitized(s) {
gsub("&", "\\&", s);
gsub("<", "\\<", s);
gsub(">", "\\>", s);
return s;
}
function normalized(url) {
if (url ~ /^(\.\/|\.\.\/)/) {
sub(/\/index\.txt$/, "/", url);
sub(/^\.\//, "", url);
}
return url;
}
function capitalized(s) {
if (!s) return s;
first = substr(s, 1, 1);
return toupper(first) substr(s, 2);
}
# function djb2(s) {
# for (i = 0; i < 256; i++) ord[sprintf("%c", i)] = i;
# n = split(s, chars, "");
# hash = 5381;
# for (i = 1; i <= n; i++) hash = (hash*33 + ord[chars[i]]) % 2147483648;
# return hash;
# }
function flower() {
flowers[0] = "M6 24 c-4 -1 -6 -4 -6 -8 l0 -3 2 -1 2 0 -1 0 c-2 -2 -3 -4 -3 -9 l0 -3 4 0 c5 0 7 1 8 4 l0 1 1 -1 c3 -3 4 -4 8 -4 l3 0 0 4 0 4 -1 2 c-2 2 -2 2 -4 2 0 0 1 0 2 0 l3 0 0 4 c0 5 -2 7 -6 8 -1 0 -10 0 -12 0z m7 -9 c1 -1 3 -2 4 -2 1 0 1 -1 0 -2 -2 -4 -6 -4 -9 0 -1 1 -1 1 0 2 2 1 2 2 3 3 l1 1 0 0 c0 -1 0 -1 1 -2z";
flowers[1] = "M6 24 c-5 -1 -6 -3 -6 -8 l0 -4 1 0 c0 0 1 0 2 0 l1 0 -2 -2 c-2 -2 -2 -2 -2 -6 l0 -4 4 0 c5 0 6 1 8 3 l0 1 1 -1 c1 -2 3 -3 7 -3 l4 0 0 4 c0 5 -1 6 -3 7 -2 1 -1 1 1 1 l2 0 0 3 c0 5 -1 6 -4 8 -1 1 -11 1 -14 1z m9 -10 c0 0 1 -1 1 -1 3 -1 0 -5 -3 -5 -4 0 -8 4 -5 4 1 0 3 2 4 4 l1 1 0 -1 c0 0 1 -1 2 -2z";
flowers[2] = "M4 24 c-1 -1 -2 -2 -3 -3 l-1 -2 0 -3 0 -2 2 -1 c2 0 3 0 2 -1 -3 -1 -4 -3 -4 -8 l0 -4 4 0 c7 0 8 1 8 3 0 1 1 1 2 -1 2 -2 1 -2 6 -2 l4 0 0 4 c0 5 -1 6 -4 8 l-1 0 3 0 2 1 0 2 c0 4 -2 7 -4 8 -1 1 -15 1 -16 1z m8 -9 c1 -1 2 -2 4 -2 2 0 2 -1 1 -3 -2 -3 -6 -3 -8 -1 -2 3 -2 4 0 5 2 1 3 1 3 2 0 1 0 0 0 -1z";
flowers[3] = "M4 24 c-2 -1 -4 -4 -4 -9 l0 -3 2 0 2 0 -1 -1 c-3 -1 -3 -2 -3 -7 l0 -4 3 0 c6 0 8 1 9 3 0 1 0 1 1 0 1 -2 2 -3 7 -3 l4 0 0 3 c0 5 -1 7 -4 8 l-1 1 3 0 2 0 0 3 c0 5 -1 7 -4 8 -1 1 -15 1 -16 1z m11 -10 c0 0 1 -1 2 -1 2 -2 -1 -6 -5 -6 -1 0 -5 3 -5 4 0 0 0 1 0 1 0 0 0 0 1 0 1 1 2 1 3 3 l2 2 0 -1 c0 0 1 -1 2 -2z";
flowers[4] = "M8 24 c-2 0 -4 -1 -6 -3 -1 -2 -2 -3 -2 -6 l0 -3 2 0 2 0 -1 -1 c-2 -2 -3 -4 -3 -8 l0 -3 4 0 c5 0 6 1 7 3 l1 1 0 -1 c1 -3 2 -3 8 -3 l4 0 0 2 c0 4 -1 6 -3 8 l-1 2 1 0 c0 0 1 0 2 0 l1 0 0 4 c0 4 0 5 -2 7 l-1 1 -6 0 c-3 0 -6 0 -7 0z m6 -10 c1 -1 2 -2 3 -2 1 0 1 -1 0 -2 -3 -4 -9 -3 -10 1 l0 1 1 0 c2 0 4 3 4 4 0 1 0 1 0 0 0 0 1 -1 2 -2z";
# "pwd" | getline pwd;
# return flowers[djb2(pwd) % length(flowers)];
return flowers[int(rand()*length(flowers))];
}
function endlinklist() { if (linklist) { printf "
"; linklist = 0; } }
function endlist() { if (list) { printf ""; list = 0; } }
function endblockquoteparagraph() { if (blockquoteparagraph) { printf ""; blockquoteparagraph = 0; } }
function endblockquotebody() { if (blockquotebody) { endblockquoteparagraph(); printf ""; blockquotebody = 0; } }
function endquote() { if (quote) { endblockquotebody(); printf ""; quote = 0; } }
function endpre() { if (pre) { printf ""; pre = 0; } }
function endparagraph() { if (paragraph) { printf ""; paragraph = 0; } }
function onbegin() {
printf "";
printf "";
printf "
";
printf "";
printf "";
printf "";
printf "";
# printf "";
if (title) printf "%s", title;
printf "";
# printf "";
printf "";
printf "";
printf "";
printf "";
printf "";
}
function onimage(url, alt, width, height, isicon, caption, sourceurl) { endlinklist(); endquote(); endparagraph(); endpre(); endlist();
a = width/height;
if (width >= height && width > 800) {
width = 800;
height = 800/a;
} else if (height >= width && height > 800) {
width = 800*a;
height = 800;
}
isfigure = !(isicon || caption == nil);
if (isfigure) {
printf "";
figure = 1;
}
printf "";
printf "
";
printf "";
if (caption) {
printf "";
printf "";
printf "%s", sanitized(caption);
if (sourceurl) printf " Source: %s", normalized(sourceurl), sanitized(normalized(sourceurl));
printf "
";
printf "";
}
if (isfigure) printf "";
}
function onvideo(url, alt, width, height, previewurl) { endlinklist(); endquote(); endparagraph(); endpre(); endlist();
a = width/height;
if (width >= height && width > 800) {
width = 800;
height = 800/a;
} else if (height >= width && height > 800) {
width = 800*a;
height = 800;
}
printf "";
}
function onaudio(url) { endlinklist(); endquote(); endparagraph(); endpre(); endlist();
printf "
";
}
function onlink(url, alt, prefix, suffix, xurl, xalt) { endquote(); endparagraph(); endpre(); endlist();
if (linklist) printf "
";
if (!linklist) {
printf "";
# printf "";
printf "";
printf "
";
skipend = 1;
}
function isurl(s) { return s ~ /^\.\/|^\.\.\/|^http:\/\/|^https:\/\/|^gemini:\/\// }
function matchingparenindex(s) {
n = 1;
p = 0;
while ((i = match(s, /[()]/))) {
c = substr(s, i, 1);
if (c == "(") { s = substr(s, i + 1); p += i + 1; ++n; }
else { s = substr(s, i + 1); p += i + 1; --n; if (n == 0) return p - 1; }
}
return p + length(s);
}
!title { title = $0; next; }
!began { onbegin(); began = 1; next; }
# Image: [xpx] () Source:
/^(Image|Banner|Icon): [^ ]+ \[[0-9]+x[0-9]+px\]/ && isurl((url = $2)) {
isicon = substr($1, 1, length($1) - 1) == "Icon";
split(substr($3, 2, length($3) - 4), dimensions, "x");
width = dimensions[1]+0; height = dimensions[2]+0;
rest = substr($0, length($1) + 1 + length(url) + 1 + length($3) + 1 + 1);
if (rest ~ /^\(/) {
rest = substr(rest, 2);
i = matchingparenindex(rest);
alt = substr(rest, 1, i - 1);
caption = substr(rest, i + 2);
} else {
alt = nil;
caption = rest;
}
if (caption) {
if ($(NF - 1) == "Source:" && isurl($NF)) {
sourceurl = $NF;
n = 9 + length(sourceurl);
caption = substr(caption, 1, length(caption) - n);
onimage(url, alt, width, height, isicon, caption, sourceurl);
next;
}
onimage(url, alt, width, height, isicon, caption);
next;
}
onimage(url, alt, width, height, isicon);
next;
}
# Video: [xpx] ()
/^Video: [^ ]+ \[[0-9]+x[0-9]+px\] \(Preview: [^ )]+\)/ && isurl((url = $2)) && isurl((previewurl = substr($5, 1, length($5) - 1))) {
split(substr($3, 2, length($3) - 4), dimensions, "x");
width = dimensions[1]+0; height = dimensions[2]+0;
rest = substr($0, length($1) + 1 + length(url) + 1 + length($3) + 1 + length($4) + 1 + length($5) + 1 + 1);
if (rest ~ /^\(/) {
rest = substr(rest, 2);
i = matchingparenindex(rest);
alt = substr(rest, 1, i - 1);
} else
alt = nil;
onvideo(url, alt, width, height, previewurl);
next;
}
# Audio:
/^Audio: [^ ]+$/ && isurl((url = $2)) {
onaudio(url);
next;
}
# : (: )
match($0, /: [^ ]+ \(/) && (altend = RSTART) && (xaltstart = RSTART + RLENGTH) && isurl((url = substr($0, RSTART + 2, RLENGTH - 4))) && match($0, /: [^ ]+\)$/) && (xaltend = RSTART) && isurl((xurl = substr($0, RSTART + 2, RLENGTH - 3))) {
alt = substr($0, 1, altend - 1);
xalt = substr($0, xaltstart, xaltend - xaltstart);
onlink(url, alt, nil, nil, xurl, xalt);
next;
}
# : ()
match($0, /: [^ ]+ \(/) && isurl((url = substr($0, RSTART + 2, RLENGTH - 4))) && /\)$/ {
prefix = substr($0, 1, RSTART);
s = substr($0, RSTART + RLENGTH);
alt = substr(s, 1, length(s) - 1);
onlink(url, alt, prefix);
next;
}
# (: )
match($0, /^[^ ]+ \(/) && (xaltstart = RLENGTH + 1) && match($0, /: [^ ]+\)$/) && (xaltend = RSTART) && isurl((url = $1)) && isurl((xurl = substr($NF, 1, length($NF) - 1))) {
xalt = substr($0, xaltstart, xaltend - xaltstart);
onlink(url, nil, nil, nil, xurl, xalt);
next;
}
# () —
/^[^ ]+ \(/ && match($0, /\) — /) && isurl((url = $1)) {
alt = substr($0, length(url) + 3, RSTART - length(url) - 3);
suffix = substr($0, RSTART + 2);
onlink(url, alt, nil, suffix);
next;
}
# ()
/^[^ ]+ \(/ && /\)$/ && isurl((url = $1)) {
s = substr($0, length(url) + 1);
alt = substr(s, 3, length(s) - 2 - 1);
onlink(url, alt);
next;
}
#
isurl((url = $0)) { onlink(url); next; }
# See also:
# Further reading:
# For more details, see:
/(See also|Further reading|For more details, see): [^ ]+$/ && isurl((url = $NF)) {
prefix = substr($0, 1, length($0) - length(url));
onlink(url, nil, prefix);
next;
}
# - :
# .* forces last occurrence.
match($0, /- .*: /) && isurl((url = substr($0, RLENGTH + 1))) {
alt = substr($0, 3, length($0) - length(url) - 4);
onlistlink(url, alt);
next;
}
# -
/^- / && isurl((url = substr($0, 3))) { onlistlink(url); next; }
# :
# .* forces last occurrence.
match($0, /.*: /) && isurl((url = substr($0, RLENGTH + 1))) {
alt = substr($0, 1, length($0) - length(url) - 2);
onlink(url, alt);
next;
}
/^[0-9]+\. / { onsection($0, substr($1, 1, length($1) - 1)); next; }
/^[0-9]+\.[0-9]+\. / { onsubsection($0, substr($1, 1, length($1) - 1)); next; }
/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/ { onsection($0, $0); next; }
/^- / { onlist(substr($0, 3)); next; }
/^> / { onblockquote(substr($0, 3)); next; }
/^“/ && /”$/ { onquote(substr($0, 2, length($0) - 2)); next; }
# — ,
/^— / && /, [^ ]+$/ && isurl((url = $NF)) {
author = substr($0, 3, length($0) - length($NF) - 4);
oncite(author, url);
next;
}
# —
/^— / { oncite(substr($0, 3)); next; }
/^ \(.*\)$/ && !pre { precaption = substr($0, 3, length($0) - 3); next; }
/^ / { onpre(substr($0, 2)); precaption = nil; next; }
/^TODO/ { onparagraph(nil, $0); next; }
/^Update [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]: / { onparagraph(substr($0, 18), substr($0, 1, 17)); next; }
/^Warning: / { onparagraph(substr($0, 10), substr($0, 1, 9)); next; }
/^$/ { onparagraph(nil); next; }
!headline { headline = 1; onheadline($0); next; }
{ onparagraph($0); next; }
END { onend(); }