Skip to main content

My first Arc project: a simple Wiki

The only way to learn a programming language is to write something in it. So, I decided it was time to dig into Arc and my first project is a very simple Wiki.

Here's the source (wiki.arc):

; A wiki written in Arc (arc0)
;
; Copyright (c) 2008 John Graham-Cumming
;
; (load "wiki.arc")
; (wsv)
;
; Then go to http://localhost:8080/show

(load "web.arc")
(load "util.arc")

(= pagedir* "wiki/")

(def histfiles (page)
(sort > (map [coerce _ 'int] (rem [is "current" _] (dir (pagepath page))))))

(def nexthist (page)
(let h (histfiles page)
(if h (++ (car h)) 0)))

(def pagepath (page)
(string pagedir* (page 0) "/" (page 0) (page 1) "/" page ))

(def pagefile (page (o file))
(string (pagepath page) "/" (or file "current")))

(def slurp (page (o file))
(if
(let p (pagefile page file)
(if (file-exists p) (readfile p)))))

(def upperlen (word)
(len (keep upper word)))

(def is-wikilink (word)
(if (alphas word)
(if (~is (word 0) (downcase (word 0)))
(>= (upperlen word) 2))))

(mac url-show (page)
`(string "show?p=" ,page))

(mac url-edit (page)
`(string "edit?p=" ,page))

(mac link-show (page text)
`(link ,text (url-show ,page)))

(mac link-edit (page text)
`(link ,text (url-edit ,page)))

(def wikify (word)
(if (is-wikilink word)
(if (file-exists (pagefile word))
(link-show word word)
(pr word)(link-edit word "?"))
(pr word))
(ws))

(mac spew-raw (page)
`(spew ,page [pr _ " "]))

(mac spew-wiki (page (o file))
`(spew ,page [wikify (string _)] ,file))

(def spew (page f (o file))
(let p (pagepath page)
(if (dir-exists p)
(map f (flat (map tokens (slurp page file))))
(pr "This page does not yet exist."))))

(def squash (file body)
(writefile1 body file))

(def save-page (req)
(w/$ p
(w/$ t
(squash (pagefile p) t)
(squash (string (pagepath p) "/" (nexthist p)) t))
(url-show p)))

(mac mtime (f)
`(datetime (file-mtime ,f)))

(def last-modified (page)
(let f (pagefile page)
(if (file-exists f)
(pr "Last modified: " (mtime f)))))

(mac show-page (page)
`(whitepage
(tag h1 (link-show ,page ,page))
(spew-wiki ,page)
(br 2)
(hr)
(last-modified ,page)
(br)
(link-edit ,page "[edit]")
(ws)
(link "[history]" (string "history?p=" ,page))))

(mac edit-page (page)
`(whitepage
(tag h1 (pr (string "Editing " ,page)))
(arform save-page
(textarea "t" 25 80 (spew-raw ,page))
(hidden "p" ,page)
(br)
(submit "Save"))
(link-show ,page "[cancel]")
(br 2)))

(def revision (page rev)
(tag li
(pr "Revision: " )
(link rev (string "revision?p=" page "&r=" rev))
(pr " modified " (mtime (string (pagepath page) "/" rev)))))

(mac history-page (page)
`(whitepage
(tag h1 (pr (string "History of " ,page)))
(tag ul (map [revision ,page _] (histfiles ,page)))
(hr)
(link-show ,page (string "Back to " ,page))))

(mac revision-page (page rev)
`(whitepage
(tag h1 (pr "Showing revision " ,rev " of " ,page))
(spew-wiki ,page ,rev)
(br 2)
(hr)
(last-modified ,page)
(br)
(link-show ,page (string "Back to " ,page))))

(defop show req
(w/$ p
(if p
(show-page ($ "p"))
(show-page "HomePage"))))

(defop edit req
(w/$ p
(ensure-dir (pagepath p))
(edit-page p)))

(defop history req
(history-page ($ "p")))

(defop revision req
(revision-page ($ "p") ($ "r")))

(def wsv ()
(ensure-dir pagedir*)
(asv))

It loads two helpers. The first contains common utilities that aren't really Wiki-related (util.arc):

(def alpha (c)
(or (<= #\a c #\z) (<= #\A c #\Z)))

(def alphas (str)
(is (keep alpha str) str))

(def upper (c)
(is (upcase c) c))

(def datetime ((o time (seconds)))
(let val (tostring
(system (string "date -u -r " time " \"+%Y-%m-%d %H:%M\"")))
(subseq val 0 (- (len val) 1))))


And the second contains enhancement to Arc's web/HTML handling (web.arc):

(mac hidden (name val)
`(gentag input type 'hidden name ,name value ,val))

(mac hr ()
`(gentag hr))

(mac ws ()
`(pr " "))

(mac $ (r)
`(arg req ,r))

(mac w/$ (r . body)
`(with (,r ($ (string ',r))) ,@body))

In web.arc there are a couple of bits of syntax to make accessing form/URL arguments easier: ($ "p") (which gets the value of the argument p) and (w/$ p ...) which sets a variable called p to the value of the argument p and then evaluates the rest of the expression.

All this is released under the same license as Arc. (Since I have never programmed in Arc before, and it's been almost 20 years since I stopped coding in LISP or ML, I'd appreciate constructive comments).

Comments

PS said…
Is it live somewhere? ;)
Not in the Internet, but only because I need to set up mod_proxy on my box so that I can expose it.

Popular posts from this blog

Your last name contains invalid characters

My last name is "Graham-Cumming". But here's a typical form response when I enter it:


Does the web site have any idea how rude it is to claim that my last name contains invalid characters? Clearly not. What they actually meant is: our web site will not accept that hyphen in your last name. But do they say that? No, of course not. They decide to shove in my face the claim that there's something wrong with my name.

There's nothing wrong with my name, just as there's nothing wrong with someone whose first name is Jean-Marie, or someone whose last name is O'Reilly.

What is wrong is that way this is being handled. If the system can't cope with non-letters and spaces it needs to say that. How about the following error message:

Our system is unable to process last names that contain non-letters, please replace them with spaces.

Don't blame me for having a last name that your system doesn't like, whose fault is that? Saying "Your last name …

Importing an existing SSL key/certificate pair into a Java keystore

I'm writing this blog post in case anyone else has to Google that. In Java 6 keytool has been improved so that it now becomes possible to import an existing key and certificate (say one you generated outside of the Java world) into a keystore.

You need: Java 6 and openssl.

1. Suppose you have a certificate and key in PEM format. The key is named host.key and the certificate host.crt.

2. The first step is to convert them into a single PKCS12 file using the command: openssl pkcs12 -export -in host.crt -inkey host.key > host.p12. You will be asked for various passwords (the password to access the key (if set) and then the password for the PKCS12 file being created).

3. Then import the PKCS12 file into a keystore using the command: keytool -importkeystore -srckeystore host.p12 -destkeystore host.jks -srcstoretype pkcs12. You now have a keystore named host.jks containing the certificate/key you need.

For the sake of completeness here's the output of a full session I performe…

More fun with toys: the Ikea LILLABO Train Set

As further proof of my unsuitability to be a child minder (see previous post) I found myself playing with an Ikea LILLABO 20-piece basic set train.


The train set has 16 pieces of track (12 curves, two straight pieces and a two part bridge) and 4 pieces of train. What I wondered was... how many possible looping train tracks can be made using all 16 pieces?

The answer is... 9. Here's a picture of the 9 different layouts.


The picture was generated using a little program written in Processing. The bridge is red, the straight pieces are green and the curves are blue or magenta depending on whether they are oriented clockwise or anticlockwise. The curved pieces can be oriented in either way.

To generate those layouts I wrote a small program which runs through all the possible layouts and determines which form a loop. The program eliminates duplicate layouts (such as those that are mirror images of each other).

It outputs a list of instructions for building loops. These instructions con…