#!/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 "\"Image:"; 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(); }