The source is spread over three files and it comes with a conf file (KUDOS!). This is the 0.1 version of awkiawki.
Don't be scared! The source looks better on the edit page :)
#!/usr/bin/mawk -f
################################################################################
# awkiawki - wikiwiki clone written in (n|g|m)awk
# $Id: awki.cgi,v 1.45 2004/07/13 16:34:45 olt Exp $
################################################################################
# Copyright (c) 2002 Oliver Tonnhofer (olt@bogosoft.com)
# See the file `COPYING' for copyright notice.
################################################################################
BEGIN {
# --- default options ---
# --- use awki.conf to override default settings ---
#
# img_tag: HTML img tag for picture in page header.
localconf["img_tag"] = "
"
# datadir: Directory for raw pagedata (must be writeable for the script).
localconf["datadir"] = "./data/"
# parser: Parsing script.
localconf["parser"] = "./parser.awk"
# special_parser: Parser for special_* functions.
localconf["special_parser"] = "./special_parser.awk"
# default_page: Name of the default_page.
localconf["default_page"] = "FrontPage"
# show_changes: Number of changes listed by RecentChanges
localconf["show_changes"] = 10
# max_post: Bytes accept by POST requests (to avoid DOS).
localconf["max_post"] = 100000
# write_protection: Regex for write protected files
# e.g.: "*", "PageOne|PageTwo|^.*NonEditable"
# HINT: to edit these protected pages, upload a .htaccess
# protected awki.cgi script with write_protection = ""
localconf["write_protection"] = ""
# css: HTTP URL for external CSS file.
localconf["css"] = ""
# always_convert_spaces: If true, convert runs of 8 spaces to tab automatical.
localconf["always_convert_spaces"] = 0
# date_cmd: Command for current date.
localconf["date_cmd"] = "date '+%e %b. %G %R:%S %Z'"
# rcs: If true, rcs is used for revisioning.
localconf["rcs"] = 0
# path: add path to PATH environment
localconf["path"] = ""
# --- default options ---
scriptname = ENVIRON["SCRIPT_NAME"]
if (localconf["path"])
ENVIRON["PATH"] = localconf["path"] ":" ENVIRON["PATH"]
#load external configfile
load_config(scriptname)
# PATH_INFO contains page name
if (ENVIRON["PATH_INFO"]) {
query["page"] = ENVIRON["PATH_INFO"]
}
if (ENVIRON["REQUEST_METHOD"] == "POST") {
if (ENVIRON["CONTENT_TYPE"] == "application/x-www-form-urlencoded") {
if (ENVIRON["CONTENT_LENGTH"] < localconf["max_post"])
bytes = ENVIRON["CONTENT_LENGTH"]
else
bytes = localconf["max_post"]
cmd = "dd ibs=1 count=" bytes " 2>/dev/null"
cmd | getline query_str
close (cmd)
}
if (ENVIRON["QUERY_STRING"])
query_str = query_str "&" ENVIRON["QUERY_STRING"]
} else {
if (ENVIRON["QUERY_STRING"])
query_str = ENVIRON["QUERY_STRING"]
}
n = split(query_str, querys, "&")
for (i=1; i<=n; i++) {
split(querys[i], data, "=")
query[data[1]] = data[2]
}
# (IMPORTANT for security!)
query["page"] = clear_pagename(query["page"])
query["revision"] = clear_revision(query["revision"])
query["revision2"] = clear_revision(query["revision2"])
query["string"] = clear_str(decode(query["string"]))
if (!localconf["rcs"])
query["revision"] = 0
if (query["page"] == "")
query["page"] = localconf["default_page"]
query["filename"] = localconf["datadir"] query["page"]
#check if page is editable
special_pages = "FullSearch|PageList|RecentChanges"
if (query["page"] ~ "("special_pages")") {
special_page = 1
} else if (!localconf["write_protection"] || query["page"] !~ "("localconf["write_protection"]")") {
page_editable = 1
}
header(query["page"])
if (query["edit"] && page_editable)
edit(query["page"], query["filename"], query["revision"])
else if (query["save"] && query["text"] && page_editable)
save(query["page"], query["text"], query["string"], query["filename"])
else if (query["page"] ~ "PageList")
special_index(localconf["datadir"])
else if (query["page"] ~ "RecentChanges")
special_changes(localconf["datadir"])
else if (query["page"] ~ "FullSearch")
special_search(query["string"],localconf["datadir"])
else if (query["page"] && query["history"])
special_history(query["page"], query["filename"])
else if (query["page"] && query["diff"] && query["revision"])
special_diff(query["page"], query["filename"], query["revision"], query["revision2"])
else
parse(query["page"], query["filename"], query["revision"])
footer(query["page"])
}
# print header
function header(page) {
print "Content-type: text/html\n"
print "\n\n" page " "
if (localconf["css"])
print ""
if (query["save"])
print ""
print "\n"
print ""localconf["img_tag"]
print "FullSearch?string="page"\">"page"
"
}
# print footer
function footer(page) {
print "
"
if (page_editable)
print "Edit "page""
print ""localconf["default_page"]""
print "PageList\">PageList"
print "RecentChanges\">RecentChanges"
if (localconf["rcs"] && !special_page)
print "PageHistory"
print "\n\n"
}
# send page to parser script
function parse(name, filename, revision) {
if (system("[ -f "filename" ]") == 0 ) {
if (revision) {
print "Displaying old version ("revision") of "name"."
system("co -q -p'"revision"' " filename " | "localconf["parser"] " -v datadir='"localconf["datadir"] "'")
} else
system(localconf["parser"] " -v datadir='"localconf["datadir"] "' " filename)
}
}
function special_diff(page, filename, revision, revision2, revisions) {
if (system("[ -f "filename" ]") == 0) {
print "Displaying diff between "revision
if (revision2)
print " and "revision2
else
print " and current version"
print " of "page"."
if (revision2)
revisions = "-r" revision " -r" revision2
else
revisions = "-r" revision
system("rcsdiff "revisions" -u "filename" | " localconf["special_parser"] " -v special_diff='"page"'") }
}
# print edit form
function edit(page, filename, revision, cmd) {
if (revision)
print "If you save previous versions, you'll overwrite the current page."
print "
"
print "Emphases: italic; bold; bold italic; mixed 'bold'and italic
Horizontal Rule: ----
Paragraph: blank line
Headings: -Title 1 ; --Title 2 ; ---Title 3
Preformatted Text: *space*Text
Lists: tab(s) and one of * bullets; 1 numbered items
Links: JoinCapitalizedWords; url (http, https, ftp, gopher, mailto, news)"
}
# save page
function save(page, text, comment, filename, dtext, date) {
dtext = decode(text);
if ( localconf["always_convert_spaces"] || query["convertspaces"] == "on")
gsub(/ /, "\t", dtext);
print dtext > filename
if (localconf["rcs"]) {
localconf["date_cmd"] | getline date
system("ci -q -t-"page" -l -m'"ENVIRON["REMOTE_ADDR"] ";;" date ";;"comment"' " filename)
}
print "saved "page""
}
# list all pages
function special_index(datadir) {
system("ls -1 " datadir " | " localconf["special_parser"] " -v special_index=yes")
}
# list pages with last modified time (sorted by date)
function special_changes(datadir, date) {
localconf["date_cmd"] | getline date
print "current date:", date "
"
system("ls -tlL "datadir" | " localconf["special_parser"] " -v special_changes=" localconf["show_changes"])
}
function special_search(name,datadir) {
system("grep -il '"name"' "datadir"* | " localconf["special_parser"] " -v special_search=yes")
}
function special_history(name, filename) {
print "last changes on "name"
"
system("rlog " filename " | " localconf["special_parser"] " -v special_history="name)
print "Show diff between:"
print "
"
}
# remove '"` characters from string
# *** !Important for Security! ***
function clear_str(str) {
gsub(/['`"]/, "", str)
if (length(str) > 80)
return substr(str, 1, 80)
else
return str
}
# retrun the pagename
# *** !Important for Security! ***
function clear_pagename(str) {
if (match(str, /[A-Z][a-z]+[A-Z][A-Za-z]*/))
return substr(str, RSTART, RLENGTH)
else
return ""
}
# return revision numbers
# *** !Important for Security! ***
function clear_revision(str) {
if (match(str, /[1-9]\.[0-9]+/))
return substr(str, RSTART, RLENGTH)
else
return ""
}
# decode urlencoded string
function decode(text, hex, i, hextab, decoded, len, c, c1, c2, code) {
split("0 1 2 3 4 5 6 7 8 9 a b c d e f", hex, " ")
for (i=0; i<16; i++) hextab[hex[i+1]] = i
# urldecode function from Heiner Steven
# http://www.shelldorado.com/scripts/cmds/urldecode
# decode %xx to ASCII char
decoded = ""
i = 1
len = length(text)
while ( i <= len ) {
c = substr (text, i, 1)
if ( c == "%" ) {
if ( i+2 <= len ) {
c1 = tolower(substr(text, i+1, 1))
c2 = tolower(substr(text, i+2, 1))
if ( hextab [c1] != "" || hextab [c2] != "" ) {
if ( (c1 >= 2 && c1 != 7) || (c1 == 7 && c2 != "f") || (c1 == 0 && c2 ~ "[9acd]") ){
code = 0 + hextab [c1] * 16 + hextab [c2] + 0
c = sprintf ("%c", code)
} else {
c = " "
}
i = i + 2
}
}
} else if ( c == "+" ) { # special handling: "+" means " "
c = " "
}
decoded = decoded c
++i
}
# change linebreaks to \n
gsub(/\r\n/, "\n", decoded)
# remove last linebreak
sub(/[\n\r]*$/,"",decoded)
return decoded
}
#load configfile
function load_config(script, configfile,key,value) {
configfile = script
#remove trailing / ('/awki/awki.cgi/' -> '/awki/awki.cgi')
sub(/\/$/, "", configfile)
#remove path ('/awki/awki.cgi' -> 'awki.cgi')
sub(/^.*\//, "", configfile)
#remove suffix ('awki.cgi' -> 'awki')
sub(/\.[^.]*$/,"", configfile)
# append .conf suffix
configfile = configfile ".conf"
#read configfile
while((getline < configfile) >0) {
if ($0 ~ /^#/) continue #ignore comments
if (match($0,/[ \t]*=[ \t]*/)) {
key = substr($0, 1, RSTART-1)
sub(/^[ \t]*/, "", key)
value = substr($0, RSTART+RLENGTH)
sub(/[ \t]*$/, "", value)
if (sub(/^"/, "", value))
sub(/"$/, "", value)
#set localconf variables
localconf[key] = value
}
}
}
################################################################################
# awki.conf - example configfile for awkiawki
# $Id: awki.conf,v 1.2 2003/06/19 10:32:49 olt Exp $
################################################################################
# Copyright (c) 2002 Oliver Tonnhofer (olt@bogosoft.com)
# See the file `COPYING' for copyright notice.
################################################################################
## The first option contains the default value.
## Surrounding doubble-quotes are optional.
## default_page:
## Name of the default_page.
# default_page = "FrontPage"
# default_page = "AwkiAwki"
## img_tag:
## HTML img tag for picture in page header.
img_tag = 
## datadir:
## Directory for raw pagedata (must be writeable for the script).
datadir = "./data/"
## css:
## HTTP URL for external CSS file
css = "/main.css"
# css = "http://mysite.domain/mystylesheet.css"
## rcs:
## If true, RCS is used for revisioning.
## This will enable PageHistory!
## HINT: Add a RCS directory in your datadir to save the
## RCS Files in a separate directory.
# rcs = 0
rcs = "true"
## path:
## add path to PATH environment
## (path is added to the left side of $PATH)
#path = ""
#path = "/sw/bin:/usr/local"
## show_changes:
## Number of changes listed by RecentChanges.
# show_changes = 10
show_changes = 25
## write_protection:
## Regex for write protected files.
## e.g.: "*", "PageOne|PageTwo|^.*NonEditable"
## HINT: To edit these protected pages, upload a .htaccess
## protected awki.cgi script with write_protection = ""
# write_protection = ""
# write_protection = ^AwkiAwki$|ReadOnly$
## always_convert_spaces:
## If true, convert runs of 8 spaces to tab automatical.
# always_convert_spaces = 0
## date_cmd:
## Command for current date.
date_cmd = "date '+%e %b. %G %R:%S %Z'"
## parser:
## Parsing script.
parser = "./parser.awk"
## special_parser:
## Parser for special_* functions.
special_parser = "./special_parser.awk"
## max_post:
## Bytes accepted by POST requests (to avoid DOS).
max_post = 500000
#!/usr/bin/awk -f
################################################################################
# parser.awk - parsing script for awkiawki
# $Id: parser.awk,v 1.6 2002/12/07 13:46:45 olt Exp $
################################################################################
# Copyright (c) 2002 Oliver Tonnhofer (olt@bogosoft.com)
# See the file `COPYING' for copyright notice.
################################################################################
BEGIN {
list["ol"] = 0
list["ul"] = 0
scriptname = ENVIRON["SCRIPT_NAME"]
FS = "[ ]"
cmd = "ls " datadir
while ((cmd | getline ls_out) >0)
if (match(ls_out, /[A-Z][a-z]+[A-Z][A-Za-z]*/) && substr(ls_out, RSTART + RLENGTH) !~ /,v/) {
page = substr(ls_out, RSTART, RLENGTH)
pages[page] = 1
}
close(cmd)
}
# register blanklines
/^$/ { blankline = 1; next; }
# HTML entities for <, > and &
/[&<>]/ { gsub(/&/, "\\&"); gsub(/, "\\<"); gsub(/>/, "\\>"); }
# generate links
/[A-Z][a-z]+[A-Z][A-Za-z]*/ ||
/(https?|ftp|gopher|mailto|news):/ {
tmpline = ""
for(i=1;i<=NF;i++) {
field = $i
# generate HTML img tag for .jpg,.jpeg,.gif,png URLs
if (field ~ /https?:\/\/[^\t]*\.(jpg|jpeg|gif|png)/ && field !~ /https?:\/\/[^\t]*\.(jpg|jpeg|gif|png)/) {
sub(/https?:\/\/[^\t]*\.(jpg|jpeg|gif|png)/, "
",field)
# links for mailto, news and http, ftp and gopher URLs
}else if (field ~ /((https?|ftp|gopher):\/\/|(mailto|news):)[^\t]*/) {
sub(/((https?|ftp|gopher):\/\/|(mailto|news):)[^\t]*[^.,?;:'")\t]/, "&",field)
# remove mailto: in link description
sub(/>mailto:/, ">",field)
# links for awkipages
}else if (field ~ /(^|[[,.?;:'"\(\t])[A-Z][a-z]+[A-Z][A-Za-z]*/ && field !~ //) {
match(field, /[A-Z][a-z]+[A-Z][A-Za-z]*/)
tmp_pagename = substr(field, RSTART, RLENGTH)
if (pages[tmp_pagename])
sub(/[A-Z][a-z]+[A-Z][A-Za-z]*/, "&",field)
else
sub(/[A-Z][a-z]+[A-Z][A-Za-z]*/, "&?",field)
}
tmpline = tmpline field OFS
}
# return tmpline to $0 and remove last OFS (whitespace)
$0 = substr(tmpline, 1, length(tmpline)-1)
}
# remove six single quotes (WikiLinks)
{ gsub(//,""); }
# emphasize text in single-quotes
// { gsub(/('?'?[^'])*/, "&"); gsub(//,""); }
// { gsub(/('?[^'])*/, "&"); gsub(//,""); }
#headings
/^-[^-]/ { $0 = "" substr($0, 2) "
"; close_tags(); print; next; }
/^--[^-]/ { $0 = "" substr($0, 3) "
"; close_tags(); print; next; }
/^---[^-]/ { $0 = "" substr($0, 4) "
"; close_tags(); print; next; }
# horizontal line
/^----/ { sub(/^----+/, "
"); blankline = 1; close_tags(); print; next; }
/^\t+[*]/ { close_tags("list"); parse_list("ul", "ol"); print; next;}
/^\t+[1]/ { close_tags("list"); parse_list("ol", "ul"); print; next;}
/^ / {
close_tags("pre");
if (pre != 1) {
print "\n" $0; pre = 1
blankline = 0
} else {
if (blankline==1) {
print ""; blankline = 0
}
print;
}
next;
}
NR == 1 { print ""; }
{
close_tags();
# print paragraph when blankline registered
if (blankline==1) {
print "";
blankline=0;
}
#print line
print;
}
END {
$0 = ""
close_tags();
}
function close_tags(not) {
# if list is parsed this line print it
if (not !~ "list") {
if (list["ol"] > 0) {
parse_list("ol", "ul")
} else if (list["ul"] > 0) {
parse_list("ul", "ol")
}
}
# close monospace
if (not !~ "pre") {
if (pre == 1) {
print ""
pre = 0
}
}
}
function parse_list(this, other) {
thislist = list[this]
otherlist = list[other]
tabcount = 0
while(/^\t+[1*]/) {
sub(/^\t/,"")
tabcount++
}
# close foreign tags
if (otherlist > 0) {
while(otherlist-- > 0) {
print "" other ">"
}
}
# if we are needing more tags we open new
if (thislist < tabcount) {
while(thislist++ < tabcount) {
print "<" this ">"
}
# if we are needing less tags we close some
} else if (thislist > tabcount) {
while(thislist-- != tabcount) {
print "" this ">"
}
}
if (tabcount) {
sub(/^[1*]/,"")
$0 = "\t" $0
}
list[other] = 0
list[this] = tabcount
}
#!/usr/bin/mawk -f
################################################################################
# special_parser.awk - parsing script for awkiawki
# $Id: special_parser.awk,v 1.5 2003/07/07 11:22:55 olt Exp $
################################################################################
# Copyright (c) 2002 Oliver Tonnhofer (olt@bogosoft.com)
# See the file `COPYING' for copyright notice.
################################################################################
BEGIN {
scriptname = ENVIRON["SCRIPT_NAME"]
if (special_changes)
print ""
if (special_history) {
# use the power of awk to split the information of rlog
RS = "----------------------------\n"
FS = "\n"
print "\nversion date ip comment "
print "view version edit version diff to current "
}
if (special_diff)
print ""
}
special_changes && $9 ~ /^[A-Z][a-z]+[A-Z][A-Za-z]*$/ {
filenr++
print "" generate_link($9) " "$7" "$6" "$8" "
if (filenr == special_changes)
exit
}
special_index && /^[A-Z][a-z]+[A-Z][A-Za-z]*$/ {
print generate_link($0) "
"
}
special_search && /[A-Z][a-z]+[A-Z][A-Za-z]*$/ {
sub(/^.*[^\/]\//, "")
print generate_link($0) "
"
}
special_history && NR > 1 {
#pick revision number
match($1, /[1-9]\.[0-9]+/)
revision = substr($1, RSTART, RLENGTH)
#split the double-semicolon separated information line
split($3, sp_array, ";;")
ip_address = sp_array[1]
date = sp_array[2]
comment = sp_array[3]
if (NR == 2)
revision = "current"
print ""revision" "date" "ip_address" "comment" "
print "
if (NR > 2)
print "?revision="revision
print "\">view "
print "
if (NR >2)
print "revision="revision"&"
print "page="special_history"\">edit "
if (NR >2)
print "diff "
print " "
}
special_diff && (NR >2) {
if (/^\+/)
print ""$0""
else if (/^\-/)
print ""$0""
else
print $0
}
END {
if (special_changes || special_history)
print "
"
if (special_diff)
print ""
}
function generate_link(string) {
sub(/[A-Z][a-z]+[A-Z][A-Za-z]*/, "&", string)
return(string)
}