Import Hunchentoot wholesale
authorLucian Mogosanu <lucian@mogosanu.ro>
Thu, 15 Aug 2019 11:14:01 +0000 (14:14 +0300)
committerLucian Mogosanu <lucian@mogosanu.ro>
Thu, 15 Aug 2019 11:14:16 +0000 (14:14 +0300)
Yes, the whole orchestra is fucking huge. Not my fault.

308 files changed:
blog.lisp
deps/alexandria/.boring [new file with mode: 0644]
deps/alexandria/.gitignore [new file with mode: 0644]
deps/alexandria/AUTHORS [new file with mode: 0644]
deps/alexandria/LICENCE [new file with mode: 0644]
deps/alexandria/README [new file with mode: 0644]
deps/alexandria/alexandria-tests.asd [new file with mode: 0644]
deps/alexandria/alexandria.asd [new file with mode: 0644]
deps/alexandria/arrays.lisp [new file with mode: 0644]
deps/alexandria/binding.lisp [new file with mode: 0644]
deps/alexandria/conditions.lisp [new file with mode: 0644]
deps/alexandria/control-flow.lisp [new file with mode: 0644]
deps/alexandria/definitions.lisp [new file with mode: 0644]
deps/alexandria/doc/.gitignore [new file with mode: 0644]
deps/alexandria/doc/Makefile [new file with mode: 0644]
deps/alexandria/doc/alexandria.texinfo [new file with mode: 0644]
deps/alexandria/doc/docstrings.lisp [new file with mode: 0644]
deps/alexandria/features.lisp [new file with mode: 0644]
deps/alexandria/functions.lisp [new file with mode: 0644]
deps/alexandria/hash-tables.lisp [new file with mode: 0644]
deps/alexandria/io.lisp [new file with mode: 0644]
deps/alexandria/lists.lisp [new file with mode: 0644]
deps/alexandria/macros.lisp [new file with mode: 0644]
deps/alexandria/numbers.lisp [new file with mode: 0644]
deps/alexandria/package.lisp [new file with mode: 0644]
deps/alexandria/sequences.lisp [new file with mode: 0644]
deps/alexandria/strings.lisp [new file with mode: 0644]
deps/alexandria/symbols.lisp [new file with mode: 0644]
deps/alexandria/tests.lisp [new file with mode: 0644]
deps/alexandria/types.lisp [new file with mode: 0644]
deps/bordeaux-threads/CONTRIBUTORS [new file with mode: 0644]
deps/bordeaux-threads/LICENSE [new file with mode: 0644]
deps/bordeaux-threads/README [new file with mode: 0644]
deps/bordeaux-threads/bordeaux-threads-test.asd [new file with mode: 0644]
deps/bordeaux-threads/bordeaux-threads.asd [new file with mode: 0644]
deps/bordeaux-threads/src/bordeaux-threads.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/condition-variables.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/default-implementations.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-abcl.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-allegro.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-clisp.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-clozure.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-cmucl.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-corman.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-ecl.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-lispworks-condition-variables.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-lispworks.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-mcl.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-mkcl.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-null.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-sbcl.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/impl-scl.lisp [new file with mode: 0644]
deps/bordeaux-threads/src/pkgdcl.lisp [new file with mode: 0644]
deps/bordeaux-threads/test/bordeaux-threads-test.lisp [new file with mode: 0644]
deps/bordeaux-threads/version.lisp-expr [new file with mode: 0644]
deps/chunga/CHANGELOG [new file with mode: 0644]
deps/chunga/CHANGELOG.txt [new file with mode: 0644]
deps/chunga/chunga.asd [new file with mode: 0644]
deps/chunga/conditions.lisp [new file with mode: 0644]
deps/chunga/docs/index.html [new file with mode: 0644]
deps/chunga/input.lisp [new file with mode: 0644]
deps/chunga/known-words.lisp [new file with mode: 0644]
deps/chunga/output.lisp [new file with mode: 0644]
deps/chunga/packages.lisp [new file with mode: 0644]
deps/chunga/read.lisp [new file with mode: 0644]
deps/chunga/specials.lisp [new file with mode: 0644]
deps/chunga/streams.lisp [new file with mode: 0644]
deps/chunga/util.lisp [new file with mode: 0644]
deps/cl-base64/.gitignore [new file with mode: 0644]
deps/cl-base64/COPYING [new file with mode: 0644]
deps/cl-base64/cl-base64.asd [new file with mode: 0644]
deps/cl-base64/debian/changelog [new file with mode: 0644]
deps/cl-base64/debian/compat [new file with mode: 0644]
deps/cl-base64/debian/control [new file with mode: 0644]
deps/cl-base64/debian/copyright [new file with mode: 0644]
deps/cl-base64/debian/rules [new file with mode: 0755]
deps/cl-base64/debian/upload.sh [new file with mode: 0755]
deps/cl-base64/debian/watch [new file with mode: 0644]
deps/cl-base64/decode.lisp [new file with mode: 0644]
deps/cl-base64/encode.lisp [new file with mode: 0644]
deps/cl-base64/package.lisp [new file with mode: 0644]
deps/cl-base64/tests.lisp [new file with mode: 0644]
deps/cl-fad/CHANGELOG [new file with mode: 0644]
deps/cl-fad/LICENSE [new file with mode: 0644]
deps/cl-fad/README [new file with mode: 0644]
deps/cl-fad/cl-fad.asd [new file with mode: 0644]
deps/cl-fad/cl-fad.system [new file with mode: 0644]
deps/cl-fad/corman.lisp [new file with mode: 0644]
deps/cl-fad/doc/index.html [new file with mode: 0644]
deps/cl-fad/fad.lisp [new file with mode: 0644]
deps/cl-fad/fad.test.lisp [new file with mode: 0644]
deps/cl-fad/load.lisp [new file with mode: 0644]
deps/cl-fad/openmcl.lisp [new file with mode: 0644]
deps/cl-fad/packages.lisp [new file with mode: 0644]
deps/cl-fad/packages.test.lisp [new file with mode: 0644]
deps/cl-fad/path.lisp [new file with mode: 0644]
deps/cl-fad/temporary-files.lisp [new file with mode: 0644]
deps/cl-fad/temporary-files.test.lisp [new file with mode: 0644]
deps/flexi-streams/CHANGELOG [new file with mode: 0644]
deps/flexi-streams/ascii.lisp [new file with mode: 0644]
deps/flexi-streams/code-pages.lisp [new file with mode: 0644]
deps/flexi-streams/conditions.lisp [new file with mode: 0644]
deps/flexi-streams/decode.lisp [new file with mode: 0644]
deps/flexi-streams/doc/foo.txt [new file with mode: 0644]
deps/flexi-streams/doc/index.html [new file with mode: 0644]
deps/flexi-streams/encode.lisp [new file with mode: 0644]
deps/flexi-streams/external-format.lisp [new file with mode: 0644]
deps/flexi-streams/flexi-streams.asd [new file with mode: 0644]
deps/flexi-streams/in-memory.lisp [new file with mode: 0644]
deps/flexi-streams/input.lisp [new file with mode: 0644]
deps/flexi-streams/io.lisp [new file with mode: 0644]
deps/flexi-streams/iso-8859.lisp [new file with mode: 0644]
deps/flexi-streams/koi8-r.lisp [new file with mode: 0644]
deps/flexi-streams/length.lisp [new file with mode: 0644]
deps/flexi-streams/lw-char-stream.lisp [new file with mode: 0644]
deps/flexi-streams/mapping.lisp [new file with mode: 0644]
deps/flexi-streams/output.lisp [new file with mode: 0644]
deps/flexi-streams/packages.lisp [new file with mode: 0644]
deps/flexi-streams/specials.lisp [new file with mode: 0644]
deps/flexi-streams/stream.lisp [new file with mode: 0644]
deps/flexi-streams/strings.lisp [new file with mode: 0644]
deps/flexi-streams/test/README [new file with mode: 0644]
deps/flexi-streams/test/hebrew_latin8_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/hebrew_latin8_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/hebrew_latin8_lf.txt [new file with mode: 0644]
deps/flexi-streams/test/hebrew_utf8_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/hebrew_utf8_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/hebrew_utf8_lf.txt [new file with mode: 0644]
deps/flexi-streams/test/kafka_cp1252_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/kafka_cp1252_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/kafka_cp1252_lf.txt [new file with mode: 0644]
deps/flexi-streams/test/kafka_latin1_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/kafka_latin1_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/kafka_latin1_lf.txt [new file with mode: 0644]
deps/flexi-streams/test/kafka_utf8_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/kafka_utf8_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/kafka_utf8_lf.txt [new file with mode: 0644]
deps/flexi-streams/test/packages.lisp [new file with mode: 0644]
deps/flexi-streams/test/russian_koi8r_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/russian_koi8r_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/russian_koi8r_lf.txt [new file with mode: 0644]
deps/flexi-streams/test/russian_utf8_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/russian_utf8_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/russian_utf8_lf.txt [new file with mode: 0644]
deps/flexi-streams/test/test.lisp [new file with mode: 0644]
deps/flexi-streams/test/tilton_ascii_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/tilton_ascii_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/tilton_ascii_lf.txt [new file with mode: 0644]
deps/flexi-streams/test/tilton_utf8_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/tilton_utf8_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/tilton_utf8_lf.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs2_cr_be.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs2_cr_le.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs2_crlf_be.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs2_crlf_le.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs2_lf_be.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs2_lf_le.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs4_cr_be.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs4_cr_le.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs4_crlf_be.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs4_crlf_le.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs4_lf_be.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_ucs4_lf_le.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_utf8_cr.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_utf8_crlf.txt [new file with mode: 0644]
deps/flexi-streams/test/unicode_demo_utf8_lf.txt [new file with mode: 0644]
deps/flexi-streams/util.lisp [new file with mode: 0644]
deps/hunchentoot/.gitignore [new file with mode: 0644]
deps/hunchentoot/.pre-release.sh [new file with mode: 0755]
deps/hunchentoot/CHANGELOG [new file with mode: 0644]
deps/hunchentoot/CHANGELOG_TBNL [new file with mode: 0644]
deps/hunchentoot/README [new file with mode: 0644]
deps/hunchentoot/acceptor.lisp [new file with mode: 0644]
deps/hunchentoot/compat.lisp [new file with mode: 0644]
deps/hunchentoot/conditions.lisp [new file with mode: 0644]
deps/hunchentoot/cookie.lisp [new file with mode: 0644]
deps/hunchentoot/doc/LICENSE.txt [new file with mode: 0644]
deps/hunchentoot/doc/Makefile [new file with mode: 0644]
deps/hunchentoot/doc/clixdoc.xsl [new file with mode: 0644]
deps/hunchentoot/doc/hunchentoot.gif [new file with mode: 0644]
deps/hunchentoot/doc/index.xml [new file with mode: 0644]
deps/hunchentoot/easy-handlers.lisp [new file with mode: 0644]
deps/hunchentoot/headers.lisp [new file with mode: 0644]
deps/hunchentoot/hunchentoot.asd [new file with mode: 0644]
deps/hunchentoot/lispworks.lisp [new file with mode: 0755]
deps/hunchentoot/log.lisp [new file with mode: 0644]
deps/hunchentoot/make-docstrings.lisp [new file with mode: 0644]
deps/hunchentoot/mime-types.lisp [new file with mode: 0644]
deps/hunchentoot/misc.lisp [new file with mode: 0644]
deps/hunchentoot/packages.lisp [new file with mode: 0644]
deps/hunchentoot/release-checklist.txt [new file with mode: 0644]
deps/hunchentoot/reply.lisp [new file with mode: 0644]
deps/hunchentoot/request.lisp [new file with mode: 0644]
deps/hunchentoot/run-test.lisp [new file with mode: 0644]
deps/hunchentoot/session.lisp [new file with mode: 0644]
deps/hunchentoot/set-timeouts.lisp [new file with mode: 0644]
deps/hunchentoot/specials.lisp [new file with mode: 0644]
deps/hunchentoot/ssl.lisp [new file with mode: 0644]
deps/hunchentoot/taskmaster.lisp [new file with mode: 0644]
deps/hunchentoot/test/UTF-8-demo.html [new file with mode: 0644]
deps/hunchentoot/test/favicon.ico [new file with mode: 0755]
deps/hunchentoot/test/fz.jpg [new file with mode: 0644]
deps/hunchentoot/test/packages.lisp [new file with mode: 0755]
deps/hunchentoot/test/script-engine.lisp [new file with mode: 0644]
deps/hunchentoot/test/script.lisp [new file with mode: 0644]
deps/hunchentoot/test/test-certificate.crt [new file with mode: 0644]
deps/hunchentoot/test/test-handlers.lisp [new file with mode: 0644]
deps/hunchentoot/test/test-key-no-password.key [new file with mode: 0644]
deps/hunchentoot/url-rewrite/packages.lisp [new file with mode: 0644]
deps/hunchentoot/url-rewrite/primitives.lisp [new file with mode: 0644]
deps/hunchentoot/url-rewrite/specials.lisp [new file with mode: 0644]
deps/hunchentoot/url-rewrite/url-rewrite.lisp [new file with mode: 0644]
deps/hunchentoot/url-rewrite/util.lisp [new file with mode: 0644]
deps/hunchentoot/util.lisp [new file with mode: 0644]
deps/hunchentoot/www/errors/404.html [new file with mode: 0644]
deps/hunchentoot/www/errors/500.html [new file with mode: 0644]
deps/hunchentoot/www/favicon.ico [new file with mode: 0644]
deps/hunchentoot/www/hunchentoot-doc.html [new file with mode: 0644]
deps/hunchentoot/www/hunchentoot.gif [new file with mode: 0644]
deps/hunchentoot/www/img/made-with-lisp-logo.jpg [new file with mode: 0644]
deps/hunchentoot/www/index.html [new file with mode: 0644]
deps/md5/.gitattributes [new file with mode: 0644]
deps/md5/.gitignore [new file with mode: 0644]
deps/md5/COPYING [new file with mode: 0644]
deps/md5/NEWS [new file with mode: 0755]
deps/md5/README [new file with mode: 0644]
deps/md5/md5.asd [new file with mode: 0755]
deps/md5/md5.lisp [new file with mode: 0755]
deps/rfc2388/packages.lisp [new file with mode: 0644]
deps/rfc2388/rfc2388.asd [new file with mode: 0644]
deps/rfc2388/rfc2388.lisp [new file with mode: 0644]
deps/rfc2388/test.lisp [new file with mode: 0644]
deps/trivial-backtrace/.gitignore [new file with mode: 0644]
deps/trivial-backtrace/COPYING [new file with mode: 0644]
deps/trivial-backtrace/dev/backtrace.lisp [new file with mode: 0644]
deps/trivial-backtrace/dev/fallback.lisp [new file with mode: 0644]
deps/trivial-backtrace/dev/map-backtrace.lisp [new file with mode: 0644]
deps/trivial-backtrace/dev/mucking.lisp [new file with mode: 0644]
deps/trivial-backtrace/dev/packages.lisp [new file with mode: 0644]
deps/trivial-backtrace/dev/utilities.lisp [new file with mode: 0644]
deps/trivial-backtrace/lift-standard.config [new file with mode: 0644]
deps/trivial-backtrace/test/packages.lisp [new file with mode: 0644]
deps/trivial-backtrace/test/test-setup.lisp [new file with mode: 0644]
deps/trivial-backtrace/test/tests.lisp [new file with mode: 0644]
deps/trivial-backtrace/trivial-backtrace-test.asd [new file with mode: 0644]
deps/trivial-backtrace/trivial-backtrace.asd [new file with mode: 0644]
deps/trivial-backtrace/website/source/index.md [new file with mode: 0644]
deps/trivial-backtrace/website/source/resources/footer.md [new file with mode: 0644]
deps/trivial-backtrace/website/source/resources/header.md [new file with mode: 0644]
deps/trivial-backtrace/website/source/resources/navigation.md [new file with mode: 0644]
deps/trivial-backtrace/website/website.tmproj [new file with mode: 0644]
deps/trivial-gray-streams/COPYING [new file with mode: 0644]
deps/trivial-gray-streams/Makefile [new file with mode: 0644]
deps/trivial-gray-streams/README [new file with mode: 0644]
deps/trivial-gray-streams/build.xcvb [new file with mode: 0644]
deps/trivial-gray-streams/package.lisp [new file with mode: 0644]
deps/trivial-gray-streams/streams.lisp [new file with mode: 0644]
deps/trivial-gray-streams/test/package.lisp [new file with mode: 0644]
deps/trivial-gray-streams/test/run-on-many-lisps.lisp [new file with mode: 0644]
deps/trivial-gray-streams/test/test-framework.lisp [new file with mode: 0644]
deps/trivial-gray-streams/test/test.lisp [new file with mode: 0644]
deps/trivial-gray-streams/trivial-gray-streams-test.asd [new file with mode: 0644]
deps/trivial-gray-streams/trivial-gray-streams.asd [new file with mode: 0644]
deps/usocket/CHANGES [new file with mode: 0644]
deps/usocket/LICENSE [new file with mode: 0644]
deps/usocket/README.md [new file with mode: 0644]
deps/usocket/TODO [new file with mode: 0644]
deps/usocket/backend/abcl.lisp [new file with mode: 0644]
deps/usocket/backend/allegro.lisp [new file with mode: 0644]
deps/usocket/backend/clisp.lisp [new file with mode: 0644]
deps/usocket/backend/clozure.lisp [new file with mode: 0644]
deps/usocket/backend/cmucl.lisp [new file with mode: 0644]
deps/usocket/backend/ecl.lisp [new file with mode: 0644]
deps/usocket/backend/lispworks.lisp [new file with mode: 0644]
deps/usocket/backend/mcl.lisp [new file with mode: 0644]
deps/usocket/backend/mocl.lisp [new file with mode: 0644]
deps/usocket/backend/openmcl.lisp [new file with mode: 0644]
deps/usocket/backend/sbcl.lisp [new file with mode: 0644]
deps/usocket/backend/scl.lisp [new file with mode: 0644]
deps/usocket/condition.lisp [new file with mode: 0644]
deps/usocket/doc/backends.txt [new file with mode: 0644]
deps/usocket/doc/design.txt [new file with mode: 0644]
deps/usocket/notes/abcl-socket.txt [new file with mode: 0644]
deps/usocket/notes/active-sockets-apis.txt [new file with mode: 0644]
deps/usocket/notes/address-apis.txt [new file with mode: 0644]
deps/usocket/notes/allegro-socket.txt [new file with mode: 0644]
deps/usocket/notes/clisp-sockets.txt [new file with mode: 0644]
deps/usocket/notes/cmucl-sockets.txt [new file with mode: 0644]
deps/usocket/notes/errors.txt [new file with mode: 0644]
deps/usocket/notes/lw-sockets.txt [new file with mode: 0644]
deps/usocket/notes/openmcl-sockets.txt [new file with mode: 0644]
deps/usocket/notes/sb-bsd-sockets.txt [new file with mode: 0644]
deps/usocket/notes/usock-sockets.txt [new file with mode: 0644]
deps/usocket/option.lisp [new file with mode: 0644]
deps/usocket/package.lisp [new file with mode: 0644]
deps/usocket/server.lisp [new file with mode: 0644]
deps/usocket/test/package.lisp [new file with mode: 0644]
deps/usocket/test/test-condition.lisp [new file with mode: 0644]
deps/usocket/test/test-datagram.lisp [new file with mode: 0644]
deps/usocket/test/test-usocket.lisp [new file with mode: 0644]
deps/usocket/test/wait-for-input.lisp [new file with mode: 0644]
deps/usocket/usocket-test.asd [new file with mode: 0644]
deps/usocket/usocket.asd [new file with mode: 0644]
deps/usocket/usocket.lisp [new file with mode: 0644]
deps/usocket/vendor/OpenTransportUDP.lisp [new file with mode: 0644]
deps/usocket/vendor/kqueue.lisp [new file with mode: 0644]
deps/usocket/vendor/spawn-thread.lisp [new file with mode: 0644]
deps/usocket/vendor/split-sequence.lisp [new file with mode: 0644]

index c283c11..32acf0e 100644 (file)
--- a/blog.lisp
+++ b/blog.lisp
 
 (eval-when (:compile-toplevel :execute :load-toplevel)
   ;; These are the current "external" requirements
-  (defvar *deplist* '("cl-who/" "cl-ppcre/" "lbs-utils/"))
+  (defvar *deplist* '("cl-who/" "cl-ppcre/" "lbs-utils/"
+                     ;; Hunchentoot deps: cl-ppcre is already
+                     ;; included here.
+                     "chunga/" "trivial-gray-streams/" "cl-base64/" "cl-fad/"
+                     "bordeaux-threads/" "alexandria/" "flexi-streams/" "md5/"
+                     "rfc2388/" "trivial-backtrace/" "usocket/"
+                     ;; Hunchentoot itself
+                     "hunchentoot/"))
 
   ;; Set paths and change dir (SBCL-specific)
   (dolist (dep *deplist*)
   (sb-posix:chdir *lbs-base*)
   (setq *default-pathname-defaults* (pathname *lbs-base*))
 
+  ;; Configure WWWisms: for now SSL is a part of the stuff we're
+  ;; loading, so disable it explicitly.
+  (pushnew :drakma-no-ssl *features*)
+  (pushnew :hunchentoot-no-ssl *features*)
+
   ;; Load libraries
   (require 'asdf)
   (asdf:load-system :cl-who)
   (asdf:load-system :cl-ppcre)
+  (asdf:load-system :hunchentoot)
   (asdf:load-system :lbs-utils))
 
 ; TLBS global variables
diff --git a/deps/alexandria/.boring b/deps/alexandria/.boring
new file mode 100644 (file)
index 0000000..dfa9e6d
--- /dev/null
@@ -0,0 +1,13 @@
+# Boring file regexps:
+~$
+^_darcs
+^\{arch\}
+^.arch-ids
+\#
+\.dfsl$
+\.ppcf$
+\.fasl$
+\.x86f$
+\.fas$
+\.lib$
+^public_html
diff --git a/deps/alexandria/.gitignore b/deps/alexandria/.gitignore
new file mode 100644 (file)
index 0000000..e832e94
--- /dev/null
@@ -0,0 +1,4 @@
+*.fasl
+*~
+\#*
+*.patch
diff --git a/deps/alexandria/AUTHORS b/deps/alexandria/AUTHORS
new file mode 100644 (file)
index 0000000..b550ea5
--- /dev/null
@@ -0,0 +1,9 @@
+
+ACTA EST FABULA PLAUDITE
+
+Nikodemus Siivola 
+Attila Lendvai
+Marco Baringer
+Robert Strandh
+Luis Oliveira
+Tobias C. Rittweiler
\ No newline at end of file
diff --git a/deps/alexandria/LICENCE b/deps/alexandria/LICENCE
new file mode 100644 (file)
index 0000000..b5140fb
--- /dev/null
@@ -0,0 +1,37 @@
+Alexandria software and associated documentation are in the public
+domain:
+
+  Authors dedicate this work to public domain, for the benefit of the
+  public at large and to the detriment of the authors' heirs and
+  successors. Authors intends this dedication to be an overt act of
+  relinquishment in perpetuity of all present and future rights under
+  copyright law, whether vested or contingent, in the work. Authors
+  understands that such relinquishment of all rights includes the
+  relinquishment of all rights to enforce (by lawsuit or otherwise)
+  those copyrights in the work.
+
+  Authors recognize that, once placed in the public domain, the work
+  may be freely reproduced, distributed, transmitted, used, modified,
+  built upon, or otherwise exploited by anyone for any purpose,
+  commercial or non-commercial, and in any way, including by methods
+  that have not yet been invented or conceived.
+
+In those legislations where public domain dedications are not
+recognized or possible, Alexandria is distributed under the following
+terms and conditions:
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/deps/alexandria/README b/deps/alexandria/README
new file mode 100644 (file)
index 0000000..59e49ab
--- /dev/null
@@ -0,0 +1,52 @@
+Alexandria is a collection of portable public domain utilities that
+meet the following constraints:
+
+ * Utilities, not extensions: Alexandria will not contain conceptual
+   extensions to Common Lisp, instead limiting itself to tools and
+   utilities that fit well within the framework of standard ANSI
+   Common Lisp. Test-frameworks, system definitions, logging
+   facilities, serialization layers, etc. are all outside the scope of
+   Alexandria as a library, though well within the scope of Alexandria
+   as a project.
+
+ * Conservative: Alexandria limits itself to what project members
+   consider conservative utilities. Alexandria does not and will not
+   include anaphoric constructs, loop-like binding macros, etc.
+
+ * Portable: Alexandria limits itself to portable parts of Common
+   Lisp. Even apparently conservative and useful functions remain
+   outside the scope of Alexandria if they cannot be implemented
+   portably. Portability is here defined as portable within a
+   conforming implementation: implementation bugs are not considered
+   portability issues.
+
+Homepage:
+
+  http://common-lisp.net/project/alexandria/
+
+Mailing lists:
+
+  http://lists.common-lisp.net/mailman/listinfo/alexandria-devel
+  http://lists.common-lisp.net/mailman/listinfo/alexandria-cvs
+
+Repository:
+
+  git://common-lisp.net/projects/alexandria/alexandria.git
+
+Documentation:
+
+  http://common-lisp.net/project/alexandria/draft/alexandria.html
+
+  (To build docs locally: cd doc && make html pdf info)
+
+Patches:
+
+  Patches are always welcome! Please send them to the mailing list as
+  attachments, generated by "git format-patch -1".
+
+  Patches should include a commit message that explains what's being
+  done and /why/, and when fixing a bug or adding a feature you should
+  also include a test-case.
+
+  Be advised though that right now new features are unlikely to be
+  accepted until 1.0 is officially out of the door.
diff --git a/deps/alexandria/alexandria-tests.asd b/deps/alexandria/alexandria-tests.asd
new file mode 100644 (file)
index 0000000..8631769
--- /dev/null
@@ -0,0 +1,12 @@
+(defsystem alexandria-tests
+  :licence "Public Domain / 0-clause MIT"
+  :description "Tests for Alexandria, which is a collection of portable public domain utilities."
+  :author "Nikodemus Siivola <nikodemus@sb-studio.net>, and others."
+  :depends-on (:alexandria #+sbcl :sb-rt #-sbcl :rt)
+  :components ((:file "tests")))
+
+(defmethod perform ((o test-op) (c (eql (find-system :alexandria-tests))))
+  (flet ((run-tests (&rest args)
+           (apply (intern (string '#:run-tests) '#:alexandria-tests) args)))
+    (run-tests :compiled nil)
+    (run-tests :compiled t)))
diff --git a/deps/alexandria/alexandria.asd b/deps/alexandria/alexandria.asd
new file mode 100644 (file)
index 0000000..045c360
--- /dev/null
@@ -0,0 +1,68 @@
+(defsystem :alexandria
+  :version "0.0.0"
+  :licence "Public Domain / 0-clause MIT"
+  :description "Alexandria is a collection of portable public domain utilities."
+  :author "Nikodemus Siivola <nikodemus@sb-studio.net>, and others."
+  :long-description
+  "Alexandria is a project and a library.
+
+As a project Alexandria's goal is to reduce duplication of effort and improve
+portability of Common Lisp code according to its own idiosyncratic and rather
+conservative aesthetic. What this actually means is open to debate, but each
+project member has a veto on all project activities, so a degree of
+conservativism is inevitable.
+
+As a library Alexandria is one of the means by which the project strives for
+its goals.
+
+Alexandria is a collection of portable public domain utilities that meet
+the following constraints:
+
+ * Utilities, not extensions: Alexandria will not contain conceptual
+   extensions to Common Lisp, instead limiting itself to tools and utilities
+   that fit well within the framework of standard ANSI Common Lisp.
+   Test-frameworks, system definitions, logging facilities, serialization
+   layers, etc. are all outside the scope of Alexandria as a library, though
+   well within the scope of Alexandria as a project.
+
+ * Conservative: Alexandria limits itself to what project members consider
+   conservative utilities. Alexandria does not and will not include anaphoric
+   constructs, loop-like binding macros, etc.
+
+ * Portable: Alexandria limits itself to portable parts of Common Lisp. Even
+   apparently conservative and useful functions remain outside the scope of
+   Alexandria if they cannot be implemented portably. Portability is here
+   defined as portable within a conforming implementation: implementation bugs
+   are not considered portability issues.
+
+ * Team player: Alexandria will not (initially, at least) subsume or provide
+   functionality for which good-quality special-purpose packages exist, like
+   split-sequence. Instead, third party packages such as that may be
+   \"blessed\"."
+  :components
+  ((:static-file "LICENCE")
+   (:static-file "tests.lisp")
+   (:file "package")
+   (:file "definitions" :depends-on ("package"))
+   (:file "binding" :depends-on ("package"))
+   (:file "strings" :depends-on ("package"))
+   (:file "conditions" :depends-on ("package"))
+   (:file "io" :depends-on ("package" "macros" "lists" "types"))
+   (:file "macros" :depends-on ("package" "strings" "symbols"))
+   (:file "hash-tables" :depends-on ("package" "macros"))
+   (:file "control-flow" :depends-on ("package" "definitions" "macros"))
+   (:file "symbols" :depends-on ("package"))
+   (:file "functions" :depends-on ("package" "symbols" "macros"))
+   (:file "lists" :depends-on ("package" "functions"))
+   (:file "types" :depends-on ("package" "symbols" "lists"))
+   (:file "arrays" :depends-on ("package" "types"))
+   (:file "sequences" :depends-on ("package" "lists" "types"))
+   (:file "numbers" :depends-on ("package" "sequences"))
+   (:file "features" :depends-on ("package" "control-flow"))))
+
+(defmethod operation-done-p ((o test-op) (c (eql (find-system :alexandria))))
+  nil)
+
+(defmethod perform ((o test-op) (c (eql (find-system :alexandria))))
+  (operate 'load-op :alexandria-tests)
+  (operate 'test-op :alexandria-tests))
diff --git a/deps/alexandria/arrays.lisp b/deps/alexandria/arrays.lisp
new file mode 100644 (file)
index 0000000..76c1879
--- /dev/null
@@ -0,0 +1,18 @@
+(in-package :alexandria)
+
+(defun copy-array (array &key (element-type (array-element-type array))
+                              (fill-pointer (and (array-has-fill-pointer-p array)
+                                                 (fill-pointer array)))
+                              (adjustable (adjustable-array-p array)))
+  "Returns an undisplaced copy of ARRAY, with same fill-pointer and
+adjustability (if any) as the original, unless overridden by the keyword
+arguments."
+ (let* ((dimensions (array-dimensions array))
+        (new-array (make-array dimensions
+                               :element-type element-type
+                               :adjustable adjustable
+                               :fill-pointer fill-pointer)))
+   (dotimes (i (array-total-size array))
+     (setf (row-major-aref new-array i)
+           (row-major-aref array i)))
+   new-array))
diff --git a/deps/alexandria/binding.lisp b/deps/alexandria/binding.lisp
new file mode 100644 (file)
index 0000000..36d92bc
--- /dev/null
@@ -0,0 +1,93 @@
+(in-package :alexandria)
+
+(defmacro if-let (bindings &body (then-form &optional else-form))
+    "Creates new variable bindings, and conditionally executes either
+THEN-FORM or ELSE-FORM. ELSE-FORM defaults to NIL.
+
+BINDINGS must be either single binding of the form:
+
+ (variable initial-form)
+
+or a list of bindings of the form:
+
+ ((variable-1 initial-form-1)
+  (variable-2 initial-form-2)
+  ...
+  (variable-n initial-form-n))
+
+All initial-forms are executed sequentially in the specified order. Then all
+the variables are bound to the corresponding values.
+
+If all variables were bound to true values, the THEN-FORM is executed with the
+bindings in effect, otherwise the ELSE-FORM is executed with the bindings in
+effect."
+    (let* ((binding-list (if (and (consp bindings) (symbolp (car bindings)))
+                             (list bindings)
+                             bindings))
+         (variables (mapcar #'car binding-list)))
+    `(let ,binding-list
+       (if (and ,@variables)
+           ,then-form
+           ,else-form))))
+
+(defmacro when-let (bindings &body forms)
+    "Creates new variable bindings, and conditionally executes FORMS.
+
+BINDINGS must be either single binding of the form:
+
+ (variable initial-form)
+
+or a list of bindings of the form:
+
+ ((variable-1 initial-form-1)
+  (variable-2 initial-form-2)
+  ...
+  (variable-n initial-form-n))
+
+All initial-forms are executed sequentially in the specified order. Then all
+the variables are bound to the corresponding values.
+
+If all variables were bound to true values, then FORMS are executed as an
+implicit PROGN."
+  (let* ((binding-list (if (and (consp bindings) (symbolp (car bindings)))
+                           (list bindings)
+                           bindings))
+         (variables (mapcar #'car binding-list)))
+    `(let ,binding-list
+       (when (and ,@variables)
+         ,@forms))))
+
+(defmacro when-let* (bindings &body forms)
+  "Creates new variable bindings, and conditionally executes FORMS.
+
+BINDINGS must be either single binding of the form:
+
+ (variable initial-form)
+
+or a list of bindings of the form:
+
+ ((variable-1 initial-form-1)
+  (variable-2 initial-form-2)
+  ...
+  (variable-n initial-form-n))
+
+Each initial-form is executed in turn, and the variable bound to the
+corresponding value. Initial-form expressions can refer to variables
+previously bound by the WHEN-LET*.
+
+Execution of WHEN-LET* stops immediately if any initial-form evaluates to NIL.
+If all initial-forms evaluate to true, then FORMS are executed as an implicit
+PROGN."
+  (let ((binding-list (if (and (consp bindings) (symbolp (car bindings)))
+                          (list bindings)
+                          bindings)))
+    (labels ((bind (bindings forms)
+               (if bindings
+                   `((let (,(car bindings))
+                       (when ,(caar bindings)
+                         ,@(bind (cdr bindings) forms))))
+                   forms)))
+      `(let (,(car binding-list))
+         (when ,(caar binding-list)
+           ,@(bind (cdr binding-list) forms))))))
+
diff --git a/deps/alexandria/conditions.lisp b/deps/alexandria/conditions.lisp
new file mode 100644 (file)
index 0000000..ac471cc
--- /dev/null
@@ -0,0 +1,91 @@
+(in-package :alexandria)
+
+(defun required-argument (&optional name)
+  "Signals an error for a missing argument of NAME. Intended for
+use as an initialization form for structure and class-slots, and
+a default value for required keyword arguments."
+  (error "Required argument ~@[~S ~]missing." name))
+
+(define-condition simple-style-warning (simple-warning style-warning)
+  ())
+
+(defun simple-style-warning (message &rest args)
+  (warn 'simple-style-warning :format-control message :format-arguments args))
+
+;; We don't specify a :report for simple-reader-error to let the
+;; underlying implementation report the line and column position for
+;; us. Unfortunately this way the message from simple-error is not
+;; displayed, unless there's special support for that in the
+;; implementation. But even then it's still inspectable from the
+;; debugger...
+(define-condition simple-reader-error
+    #-sbcl(simple-error reader-error)
+    #+sbcl(sb-int:simple-reader-error)
+  ())
+
+(defun simple-reader-error (stream message &rest args)
+  (error 'simple-reader-error
+         :stream stream
+         :format-control message
+         :format-arguments args))
+
+(define-condition simple-parse-error (simple-error parse-error)
+  ())
+
+(defun simple-parse-error (message &rest args)
+  (error 'simple-parse-error
+         :format-control message
+         :format-arguments args))
+
+(define-condition simple-program-error (simple-error program-error)
+  ())
+
+(defun simple-program-error (message &rest args)
+  (error 'simple-program-error
+         :format-control message
+         :format-arguments args))
+
+(defmacro ignore-some-conditions ((&rest conditions) &body body)
+  "Similar to CL:IGNORE-ERRORS but the (unevaluated) CONDITIONS
+list determines which specific conditions are to be ignored."
+  `(handler-case
+       (progn ,@body)
+     ,@(loop for condition in conditions collect
+             `(,condition (c) (values nil c)))))
+
+(defmacro unwind-protect-case ((&optional abort-flag) protected-form &body clauses)
+  "Like CL:UNWIND-PROTECT, but you can specify the circumstances that
+the cleanup CLAUSES are run.
+
+  clauses ::= (:NORMAL form*)* | (:ABORT form*)* | (:ALWAYS form*)*
+
+Clauses can be given in any order, and more than one clause can be
+given for each circumstance. The clauses whose denoted circumstance
+occured, are executed in the order the clauses appear.
+
+ABORT-FLAG is the name of a variable that will be bound to T in
+CLAUSES if the PROTECTED-FORM aborted preemptively, and to NIL
+otherwise.
+
+Examples:
+
+  (unwind-protect-case ()
+       (protected-form)
+     (:normal (format t \"This is only evaluated if PROTECTED-FORM executed normally.~%\"))
+     (:abort  (format t \"This is only evaluated if PROTECTED-FORM aborted preemptively.~%\"))
+     (:always (format t \"This is evaluated in either case.~%\")))
+
+  (unwind-protect-case (aborted-p)
+       (protected-form)
+     (:always (perform-cleanup-if aborted-p)))
+"
+  (check-type abort-flag (or null symbol))
+  (let ((gflag (gensym "FLAG+")))
+    `(let ((,gflag t))
+       (unwind-protect (multiple-value-prog1 ,protected-form (setf ,gflag nil))
+        (let ,(and abort-flag `((,abort-flag ,gflag)))
+          ,@(loop for (cleanup-kind . forms) in clauses
+                  collect (ecase cleanup-kind
+                            (:normal `(when (not ,gflag) ,@forms))
+                            (:abort  `(when ,gflag ,@forms))
+                            (:always `(progn ,@forms)))))))))
\ No newline at end of file
diff --git a/deps/alexandria/control-flow.lisp b/deps/alexandria/control-flow.lisp
new file mode 100644 (file)
index 0000000..27a2915
--- /dev/null
@@ -0,0 +1,106 @@
+(in-package :alexandria)
+
+(defun extract-function-name (spec)
+  "Useful for macros that want to mimic the functional interface for functions
+like #'eq and 'eq."
+  (if (and (consp spec)
+           (member (first spec) '(quote function)))
+      (second spec)
+      spec))
+
+(defun generate-switch-body (whole object clauses test key &optional default)
+  (with-gensyms (value)
+    (setf test (extract-function-name test))
+    (setf key (extract-function-name key))
+    (when (and (consp default)
+               (member (first default) '(error cerror)))
+      (setf default `(,@default "No keys match in SWITCH. Testing against ~S with ~S."
+                      ,value ',test)))
+    `(let ((,value (,key ,object)))
+      (cond ,@(mapcar (lambda (clause)
+                        (if (member (first clause) '(t otherwise))
+                            (progn
+                              (when default
+                                (error "Multiple default clauses or illegal use of a default clause in ~S."
+                                       whole))
+                              (setf default `(progn ,@(rest clause)))
+                              '(()))
+                            (destructuring-bind (key-form &body forms) clause
+                              `((,test ,value ,key-form)
+                                ,@forms))))
+                      clauses)
+            (t ,default)))))
+
+(defmacro switch (&whole whole (object &key (test 'eql) (key 'identity))
+                         &body clauses)
+  "Evaluates first matching clause, returning its values, or evaluates and
+returns the values of DEFAULT if no keys match."
+  (generate-switch-body whole object clauses test key))
+
+(defmacro eswitch (&whole whole (object &key (test 'eql) (key 'identity))
+                          &body clauses)
+  "Like SWITCH, but signals an error if no key matches."
+  (generate-switch-body whole object clauses test key '(error)))
+
+(defmacro cswitch (&whole whole (object &key (test 'eql) (key 'identity))
+                          &body clauses)
+  "Like SWITCH, but signals a continuable error if no key matches."
+  (generate-switch-body whole object clauses test key '(cerror "Return NIL from CSWITCH.")))
+
+(defmacro whichever (&rest possibilities &environment env)
+  "Evaluates exactly one of POSSIBILITIES, chosen at random."
+  (setf possibilities (mapcar (lambda (p) (macroexpand p env)) possibilities))
+  (if (every (lambda (p) (constantp p)) possibilities)
+      `(svref (load-time-value (vector ,@possibilities)) (random ,(length possibilities)))
+      (labels ((expand (possibilities position random-number)
+                 (if (null (cdr possibilities))
+                     (car possibilities)
+                     (let* ((length (length possibilities))
+                            (half (truncate length 2))
+                            (second-half (nthcdr half possibilities))
+                            (first-half (butlast possibilities (- length half))))
+                       `(if (< ,random-number ,(+ position half))
+                            ,(expand first-half position random-number)
+                            ,(expand second-half (+ position half) random-number))))))
+        (with-gensyms (random-number)
+          (let ((length (length possibilities)))
+            `(let ((,random-number (random ,length)))
+               ,(expand possibilities 0 random-number)))))))
+
+(defmacro xor (&rest datums)
+  "Evaluates its arguments one at a time, from left to right. If more than one
+argument evaluates to a true value no further DATUMS are evaluated, and NIL is
+returned as both primary and secondary value. If exactly one argument
+evaluates to true, its value is returned as the primary value after all the
+arguments have been evaluated, and T is returned as the secondary value. If no
+arguments evaluate to true NIL is retuned as primary, and T as secondary
+value."
+  (with-gensyms (xor tmp true)
+    `(let (,tmp ,true)
+       (block ,xor
+         ,@(mapcar (lambda (datum)
+                     `(if (setf ,tmp ,datum)
+                          (if ,true
+                              (return-from ,xor (values nil nil))
+                              (setf ,true ,tmp))))
+                   datums)
+         (return-from ,xor (values ,true t))))))
+
+(defmacro nth-value-or (nth-value &body forms)
+  "Evaluates FORM arguments one at a time, until the NTH-VALUE returned by one
+of the forms is true. It then returns all the values returned by evaluating
+that form. If none of the forms return a true nth value, this form returns
+NIL."
+  (once-only (nth-value)
+    (with-gensyms (values)
+      `(let ((,values (multiple-value-list ,(first forms))))
+         (if (nth ,nth-value ,values)
+             (values-list ,values)
+             ,(if (rest forms)
+                  `(nth-value-or ,nth-value ,@(rest forms))
+                  nil))))))
+
+(defmacro multiple-value-prog2 (first-form second-form &body forms)
+  "Evaluates FIRST-FORM, then SECOND-FORM, and then FORMS. Yields as its value
+all the value returned by SECOND-FORM."
+  `(progn ,first-form (multiple-value-prog1 ,second-form ,@forms)))
diff --git a/deps/alexandria/definitions.lisp b/deps/alexandria/definitions.lisp
new file mode 100644 (file)
index 0000000..863e1f6
--- /dev/null
@@ -0,0 +1,37 @@
+(in-package :alexandria)
+
+(defun %reevaluate-constant (name value test)
+  (if (not (boundp name))
+      value
+      (let ((old (symbol-value name))
+            (new value))
+        (if (not (constantp name))
+            (prog1 new
+              (cerror "Try to redefine the variable as a constant."
+                      "~@<~S is an already bound non-constant variable ~
+                       whose value is ~S.~:@>" name old))
+            (if (funcall test old new)
+                old
+                (restart-case
+                    (error "~@<~S is an already defined constant whose value ~
+                              ~S is not equal to the provided initial value ~S ~
+                              under ~S.~:@>" name old new test)
+                  (ignore ()
+                    :report "Retain the current value."
+                    old)
+                  (continue ()
+                    :report "Try to redefine the constant."
+                    new)))))))
+
+(defmacro define-constant (name initial-value &key (test ''eql) documentation)
+  "Ensures that the global variable named by NAME is a constant with a value
+that is equal under TEST to the result of evaluating INITIAL-VALUE. TEST is a
+/function designator/ that defaults to EQL. If DOCUMENTATION is given, it
+becomes the documentation string of the constant.
+
+Signals an error if NAME is already a bound non-constant variable.
+
+Signals an error if NAME is already a constant variable whose value is not
+equal under TEST to result of evaluating INITIAL-VALUE."
+  `(defconstant ,name (%reevaluate-constant ',name ,initial-value ,test)
+     ,@(when documentation `(,documentation))))
diff --git a/deps/alexandria/doc/.gitignore b/deps/alexandria/doc/.gitignore
new file mode 100644 (file)
index 0000000..f22577b
--- /dev/null
@@ -0,0 +1,3 @@
+alexandria
+include
+
diff --git a/deps/alexandria/doc/Makefile b/deps/alexandria/doc/Makefile
new file mode 100644 (file)
index 0000000..85eb818
--- /dev/null
@@ -0,0 +1,28 @@
+.PHONY: clean html pdf include clean-include clean-crap info doc
+
+doc: pdf html info clean-crap
+
+clean-include:
+       rm -rf include
+
+clean-crap:
+       rm -f *.aux *.cp *.fn *.fns *.ky *.log *.pg *.toc *.tp *.tps *.vr
+
+clean: clean-include
+       rm -f  *.pdf *.html *.info
+
+include:
+       sbcl --no-userinit --eval '(require :asdf)' \
+       --eval '(let ((asdf:*central-registry* (list "../"))) (require :alexandria))' \
+       --load docstrings.lisp \
+       --eval '(sb-texinfo:generate-includes "include/" (list :alexandria) :base-package :alexandria)' \
+       --eval '(quit)'
+
+pdf: include
+       texi2pdf alexandria.texinfo
+
+html: include
+       makeinfo --html --no-split alexandria.texinfo
+
+info: include
+       makeinfo alexandria.texinfo
diff --git a/deps/alexandria/doc/alexandria.texinfo b/deps/alexandria/doc/alexandria.texinfo
new file mode 100644 (file)
index 0000000..89b03ac
--- /dev/null
@@ -0,0 +1,277 @@
+\input texinfo   @c -*-texinfo-*-
+@c %**start of header
+@setfilename alexandria.info
+@settitle Alexandria Manual
+@c %**end of header
+
+@settitle Alexandria Manual -- draft version
+
+@c for install-info
+@dircategory Software development
+@direntry
+* alexandria:           Common Lisp utilities.
+@end direntry
+
+@copying
+Alexandria software and associated documentation are in the public
+domain:
+
+@quotation
+  Authors dedicate this work to public domain, for the benefit of the
+  public at large and to the detriment of the authors' heirs and
+  successors. Authors intends this dedication to be an overt act of
+  relinquishment in perpetuity of all present and future rights under
+  copyright law, whether vested or contingent, in the work. Authors
+  understands that such relinquishment of all rights includes the
+  relinquishment of all rights to enforce (by lawsuit or otherwise)
+  those copyrights in the work.
+
+  Authors recognize that, once placed in the public domain, the work
+  may be freely reproduced, distributed, transmitted, used, modified,
+  built upon, or otherwise exploited by anyone for any purpose,
+  commercial or non-commercial, and in any way, including by methods
+  that have not yet been invented or conceived.
+@end quotation
+
+In those legislations where public domain dedications are not
+recognized or possible, Alexandria is distributed under the following
+terms and conditions:
+
+@quotation
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+@end quotation
+@end copying
+
+@titlepage
+
+@title Alexandria Manual
+@subtitle draft version
+
+@c The following two commands start the copyright page.
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+
+@end titlepage
+
+@contents
+
+@ifnottex
+
+@include include/ifnottex.texinfo
+
+@node Top
+@comment  node-name,  next,  previous,  up
+@top Alexandria
+
+@insertcopying
+
+@menu
+* Hash Tables::
+* Data and Control Flow::
+* Conses::
+* Sequences::
+* IO::
+* Macro Writing::
+* Symbols::
+* Arrays::
+* Types::
+* Numbers::
+@end menu
+
+@end ifnottex
+
+@node Hash Tables
+@comment  node-name,  next,  previous,  up
+@chapter Hash Tables
+
+@include include/macro-alexandria-ensure-gethash.texinfo
+@include include/fun-alexandria-copy-hash-table.texinfo
+@include include/fun-alexandria-maphash-keys.texinfo
+@include include/fun-alexandria-maphash-values.texinfo
+@include include/fun-alexandria-hash-table-keys.texinfo
+@include include/fun-alexandria-hash-table-values.texinfo
+@include include/fun-alexandria-hash-table-alist.texinfo
+@include include/fun-alexandria-hash-table-plist.texinfo
+@include include/fun-alexandria-alist-hash-table.texinfo
+@include include/fun-alexandria-plist-hash-table.texinfo
+
+@node Data and Control Flow
+@comment  node-name,  next,  previous,  up
+@chapter Data and Control Flow
+
+@include include/macro-alexandria-define-constant.texinfo
+@include include/macro-alexandria-destructuring-case.texinfo
+@include include/macro-alexandria-ensure-functionf.texinfo
+@include include/macro-alexandria-multiple-value-prog2.texinfo
+@include include/macro-alexandria-named-lambda.texinfo
+@include include/macro-alexandria-nth-value-or.texinfo
+@include include/macro-alexandria-if-let.texinfo
+@include include/macro-alexandria-when-let.texinfo
+@include include/macro-alexandria-when-let-star.texinfo
+@include include/macro-alexandria-switch.texinfo
+@include include/macro-alexandria-cswitch.texinfo
+@include include/macro-alexandria-eswitch.texinfo
+@include include/macro-alexandria-whichever.texinfo
+@include include/macro-alexandria-xor.texinfo
+
+@include include/fun-alexandria-disjoin.texinfo
+@include include/fun-alexandria-conjoin.texinfo
+@include include/fun-alexandria-compose.texinfo
+@include include/fun-alexandria-ensure-function.texinfo
+@include include/fun-alexandria-multiple-value-compose.texinfo
+@include include/fun-alexandria-curry.texinfo
+@include include/fun-alexandria-rcurry.texinfo
+
+@node Conses
+@comment  node-name,  next,  previous,  up
+@chapter Conses
+
+@include include/type-alexandria-proper-list.texinfo
+@include include/type-alexandria-circular-list.texinfo
+
+@include include/macro-alexandria-appendf.texinfo
+@include include/macro-alexandria-nconcf.texinfo
+@include include/macro-alexandria-remove-from-plistf.texinfo
+@include include/macro-alexandria-delete-from-plistf.texinfo
+@include include/macro-alexandria-reversef.texinfo
+@include include/macro-alexandria-nreversef.texinfo
+@include include/macro-alexandria-unionf.texinfo
+@include include/macro-alexandria-nunionf.texinfo
+
+@include include/macro-alexandria-doplist.texinfo
+
+@include include/fun-alexandria-circular-list-p.texinfo
+@include include/fun-alexandria-circular-tree-p.texinfo
+@include include/fun-alexandria-proper-list-p.texinfo
+
+@include include/fun-alexandria-alist-plist.texinfo
+@include include/fun-alexandria-plist-alist.texinfo
+@include include/fun-alexandria-circular-list.texinfo
+@include include/fun-alexandria-make-circular-list.texinfo
+@include include/fun-alexandria-ensure-car.texinfo
+@include include/fun-alexandria-ensure-cons.texinfo
+@include include/fun-alexandria-ensure-list.texinfo
+@include include/fun-alexandria-flatten.texinfo
+@include include/fun-alexandria-lastcar.texinfo
+@include include/fun-alexandria-setf-lastcar.texinfo
+@include include/fun-alexandria-proper-list-length.texinfo
+@include include/fun-alexandria-mappend.texinfo
+@include include/fun-alexandria-map-product.texinfo
+@include include/fun-alexandria-remove-from-plist.texinfo
+@include include/fun-alexandria-delete-from-plist.texinfo
+@include include/fun-alexandria-set-equal.texinfo
+@include include/fun-alexandria-setp.texinfo
+
+@node Sequences
+@comment  node-name,  next,  previous,  up
+@chapter Sequences
+
+@include include/type-alexandria-proper-sequence.texinfo
+
+@include include/macro-alexandria-deletef.texinfo
+@include include/macro-alexandria-removef.texinfo
+
+@include include/fun-alexandria-rotate.texinfo
+@include include/fun-alexandria-shuffle.texinfo
+@include include/fun-alexandria-random-elt.texinfo
+@include include/fun-alexandria-emptyp.texinfo
+@include include/fun-alexandria-sequence-of-length-p.texinfo
+@include include/fun-alexandria-length-equals.texinfo
+@include include/fun-alexandria-copy-sequence.texinfo
+@include include/fun-alexandria-first-elt.texinfo
+@include include/fun-alexandria-setf-first-elt.texinfo
+@include include/fun-alexandria-last-elt.texinfo
+@include include/fun-alexandria-setf-last-elt.texinfo
+@include include/fun-alexandria-starts-with.texinfo
+@include include/fun-alexandria-starts-with-subseq.texinfo
+@include include/fun-alexandria-ends-with.texinfo
+@include include/fun-alexandria-ends-with-subseq.texinfo
+@include include/fun-alexandria-map-combinations.texinfo
+@include include/fun-alexandria-map-derangements.texinfo
+@include include/fun-alexandria-map-permutations.texinfo
+
+@node IO
+@comment  node-name,   next,  previous,  up
+@chapter IO
+
+@include include/fun-alexandria-read-stream-content-into-string.texinfo
+@include include/fun-alexandria-read-file-into-string.texinfo
+@include include/fun-alexandria-read-stream-content-into-byte-vector.texinfo
+@include include/fun-alexandria-read-file-into-byte-vector.texinfo
+
+@node Macro Writing
+@comment  node-name,  next,  previous,  up
+@chapter Macro Writing
+
+@include include/macro-alexandria-once-only.texinfo
+@include include/macro-alexandria-with-gensyms.texinfo
+@include include/macro-alexandria-with-unique-names.texinfo
+@include include/fun-alexandria-featurep.texinfo
+@include include/fun-alexandria-parse-body.texinfo
+@include include/fun-alexandria-parse-ordinary-lambda-list.texinfo
+
+@node Symbols
+@comment  node-name,  next,  previous,  up
+@chapter Symbols
+
+@include include/fun-alexandria-ensure-symbol.texinfo
+@include include/fun-alexandria-format-symbol.texinfo
+@include include/fun-alexandria-make-keyword.texinfo
+@include include/fun-alexandria-make-gensym.texinfo
+@include include/fun-alexandria-make-gensym-list.texinfo
+@include include/fun-alexandria-symbolicate.texinfo
+
+@node Arrays
+@comment  node-name,  next,  previous,  up
+@chapter Arrays
+
+@include include/type-alexandria-array-index.texinfo
+@include include/type-alexandria-array-length.texinfo
+@include include/fun-alexandria-copy-array.texinfo
+
+@node Types
+@comment  node-name,  next,  previous,  up
+@chapter Types
+
+@include include/type-alexandria-string-designator.texinfo
+@include include/macro-alexandria-coercef.texinfo
+@include include/fun-alexandria-of-type.texinfo
+@include include/fun-alexandria-type-equals.texinfo
+
+@node Numbers
+@comment  node-name,  next,  previous,  up
+@chapter Numbers
+
+@include include/macro-alexandria-maxf.texinfo
+@include include/macro-alexandria-minf.texinfo
+
+@include include/fun-alexandria-binomial-coefficient.texinfo
+@include include/fun-alexandria-count-permutations.texinfo
+@include include/fun-alexandria-clamp.texinfo
+@include include/fun-alexandria-lerp.texinfo
+@include include/fun-alexandria-factorial.texinfo
+@include include/fun-alexandria-subfactorial.texinfo
+@include include/fun-alexandria-gaussian-random.texinfo
+@include include/fun-alexandria-iota.texinfo
+@include include/fun-alexandria-map-iota.texinfo
+@include include/fun-alexandria-mean.texinfo
+@include include/fun-alexandria-median.texinfo
+@include include/fun-alexandria-variance.texinfo
+@include include/fun-alexandria-standard-deviation.texinfo
+
+@bye
diff --git a/deps/alexandria/doc/docstrings.lisp b/deps/alexandria/doc/docstrings.lisp
new file mode 100644 (file)
index 0000000..51dda07
--- /dev/null
@@ -0,0 +1,881 @@
+;;; -*- lisp -*-
+
+;;;; A docstring extractor for the sbcl manual.  Creates
+;;;; @include-ready documentation from the docstrings of exported
+;;;; symbols of specified packages.
+
+;;;; This software is part of the SBCL software system. SBCL is in the
+;;;; public domain and is provided with absolutely no warranty. See
+;;;; the COPYING file for more information.
+;;;;
+;;;; Written by Rudi Schlatte <rudi@constantly.at>, mangled
+;;;; by Nikodemus Siivola.
+
+;;;; TODO
+;;;; * Verbatim text
+;;;; * Quotations
+;;;; * Method documentation untested
+;;;; * Method sorting, somehow
+;;;; * Index for macros & constants?
+;;;; * This is getting complicated enough that tests would be good
+;;;; * Nesting (currently only nested itemizations work)
+;;;; * doc -> internal form -> texinfo (so that non-texinfo format are also
+;;;;   easily generated)
+
+;;;; FIXME: The description below is no longer complete. This
+;;;; should possibly be turned into a contrib with proper documentation.
+
+;;;; Formatting heuristics (tweaked to format SAVE-LISP-AND-DIE sanely):
+;;;;
+;;;; Formats SYMBOL as @code{symbol}, or @var{symbol} if symbol is in
+;;;; the argument list of the defun / defmacro.
+;;;;
+;;;; Lines starting with * or - that are followed by intented lines
+;;;; are marked up with @itemize.
+;;;;
+;;;; Lines containing only a SYMBOL that are followed by indented
+;;;; lines are marked up as @table @code, with the SYMBOL as the item.
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (require 'sb-introspect))
+
+(defpackage :sb-texinfo
+  (:use :cl :sb-mop)
+  (:shadow #:documentation)
+  (:export #:generate-includes #:document-package)
+  (:documentation
+   "Tools to generate TexInfo documentation from docstrings."))
+
+(in-package :sb-texinfo)
+
+;;;; various specials and parameters
+
+(defvar *texinfo-output*)
+(defvar *texinfo-variables*)
+(defvar *documentation-package*)
+(defvar *base-package*)
+
+(defparameter *undocumented-packages* '(sb-pcl sb-int sb-kernel sb-sys sb-c))
+
+(defparameter *documentation-types*
+  '(compiler-macro
+    function
+    method-combination
+    setf
+    ;;structure  ; also handled by `type'
+    type
+    variable)
+  "A list of symbols accepted as second argument of `documentation'")
+
+(defparameter *character-replacements*
+  '((#\* . "star") (#\/ . "slash") (#\+ . "plus")
+    (#\< . "lt") (#\> . "gt")
+    (#\= . "equals"))
+  "Characters and their replacement names that `alphanumize' uses. If
+the replacements contain any of the chars they're supposed to replace,
+you deserve to lose.")
+
+(defparameter *characters-to-drop* '(#\\ #\` #\')
+  "Characters that should be removed by `alphanumize'.")
+
+(defparameter *texinfo-escaped-chars* "@{}"
+  "Characters that must be escaped with #\@ for Texinfo.")
+
+(defparameter *itemize-start-characters* '(#\* #\-)
+  "Characters that might start an itemization in docstrings when
+  at the start of a line.")
+
+(defparameter *symbol-characters* "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*:-+&#'"
+  "List of characters that make up symbols in a docstring.")
+
+(defparameter *symbol-delimiters* " ,.!?;")
+
+(defparameter *ordered-documentation-kinds*
+  '(package type structure condition class macro))
+
+;;;; utilities
+
+(defun flatten (list)
+  (cond ((null list)
+         nil)
+        ((consp (car list))
+         (nconc (flatten (car list)) (flatten (cdr list))))
+        ((null (cdr list))
+         (cons (car list) nil))
+        (t
+         (cons (car list) (flatten (cdr list))))))
+
+(defun whitespacep (char)
+  (find char #(#\tab #\space #\page)))
+
+(defun setf-name-p (name)
+  (or (symbolp name)
+      (and (listp name) (= 2 (length name)) (eq (car name) 'setf))))
+
+(defgeneric specializer-name (specializer))
+
+(defmethod specializer-name ((specializer eql-specializer))
+  (list 'eql (eql-specializer-object specializer)))
+
+(defmethod specializer-name ((specializer class))
+  (class-name specializer))
+
+(defun ensure-class-precedence-list (class)
+  (unless (class-finalized-p class)
+    (finalize-inheritance class))
+  (class-precedence-list class))
+
+(defun specialized-lambda-list (method)
+  ;; courtecy of AMOP p. 61
+  (let* ((specializers (method-specializers method))
+         (lambda-list (method-lambda-list method))
+         (n-required (length specializers)))
+    (append (mapcar (lambda (arg specializer)
+                      (if  (eq specializer (find-class 't))
+                           arg
+                           `(,arg ,(specializer-name specializer))))
+                    (subseq lambda-list 0 n-required)
+                    specializers)
+           (subseq lambda-list n-required))))
+
+(defun string-lines (string)
+  "Lines in STRING as a vector."
+  (coerce (with-input-from-string (s string)
+            (loop for line = (read-line s nil nil)
+               while line collect line))
+          'vector))
+
+(defun indentation (line)
+  "Position of first non-SPACE character in LINE."
+  (position-if-not (lambda (c) (char= c #\Space)) line))
+
+(defun docstring (x doc-type)
+  (cl:documentation x doc-type))
+
+(defun flatten-to-string (list)
+  (format nil "~{~A~^-~}" (flatten list)))
+
+(defun alphanumize (original)
+  "Construct a string without characters like *`' that will f-star-ck
+up filename handling. See `*character-replacements*' and
+`*characters-to-drop*' for customization."
+  (let ((name (remove-if (lambda (x) (member x *characters-to-drop*))
+                         (if (listp original)
+                             (flatten-to-string original)
+                             (string original))))
+        (chars-to-replace (mapcar #'car *character-replacements*)))
+    (flet ((replacement-delimiter (index)
+             (cond ((or (< index 0) (>= index (length name))) "")
+                   ((alphanumericp (char name index)) "-")
+                   (t ""))))
+      (loop for index = (position-if #'(lambda (x) (member x chars-to-replace))
+                                     name)
+         while index
+         do (setf name (concatenate 'string (subseq name 0 index)
+                                    (replacement-delimiter (1- index))
+                                    (cdr (assoc (aref name index)
+                                                *character-replacements*))
+                                    (replacement-delimiter (1+ index))
+                                    (subseq name (1+ index))))))
+    name))
+
+;;;; generating various names
+
+(defgeneric name (thing)
+  (:documentation "Name for a documented thing. Names are either
+symbols or lists of symbols."))
+
+(defmethod name ((symbol symbol))
+  symbol)
+
+(defmethod name ((cons cons))
+  cons)
+
+(defmethod name ((package package))
+  (short-package-name package))
+
+(defmethod name ((method method))
+  (list
+   (generic-function-name (method-generic-function method))
+   (method-qualifiers method)
+   (specialized-lambda-list method)))
+
+;;; Node names for DOCUMENTATION instances
+
+(defgeneric name-using-kind/name (kind name doc))
+
+(defmethod name-using-kind/name (kind (name string) doc)
+  (declare (ignore kind doc))
+  name)
+
+(defmethod name-using-kind/name (kind (name symbol) doc)
+  (declare (ignore kind))
+  (format nil "~@[~A:~]~A" (short-package-name (get-package doc)) name))
+
+(defmethod name-using-kind/name (kind (name list) doc)
+  (declare (ignore kind))
+  (assert (setf-name-p name))
+  (format nil "(setf ~@[~A:~]~A)" (short-package-name (get-package doc)) (second name)))
+
+(defmethod name-using-kind/name ((kind (eql 'method)) name doc)
+  (format nil "~A~{ ~A~} ~A"
+          (name-using-kind/name nil (first name) doc)
+          (second name)
+          (third name)))
+
+(defun node-name (doc)
+  "Returns TexInfo node name as a string for a DOCUMENTATION instance."
+  (let ((kind (get-kind doc)))
+    (format nil "~:(~A~) ~(~A~)" kind (name-using-kind/name kind (get-name doc) doc))))
+
+(defun short-package-name (package)
+  (unless (eq package *base-package*)
+    (car (sort (copy-list (cons (package-name package) (package-nicknames package)))
+               #'< :key #'length))))
+
+;;; Definition titles for DOCUMENTATION instances
+
+(defgeneric title-using-kind/name (kind name doc))
+
+(defmethod title-using-kind/name (kind (name string) doc)
+  (declare (ignore kind doc))
+  name)
+
+(defmethod title-using-kind/name (kind (name symbol) doc)
+  (declare (ignore kind))
+  (format nil "~@[~A:~]~A" (short-package-name (get-package doc)) name))
+
+(defmethod title-using-kind/name (kind (name list) doc)
+  (declare (ignore kind))
+  (assert (setf-name-p name))
+  (format nil "(setf ~@[~A:~]~A)" (short-package-name (get-package doc)) (second name)))
+
+(defmethod title-using-kind/name ((kind (eql 'method)) name doc)
+  (format nil "~{~A ~}~A"
+          (second name)
+          (title-using-kind/name nil (first name) doc)))
+
+(defun title-name (doc)
+  "Returns a string to be used as name of the definition."
+  (string-downcase (title-using-kind/name (get-kind doc) (get-name doc) doc)))
+
+(defun include-pathname (doc)
+  (let* ((kind (get-kind doc))
+         (name (nstring-downcase
+                (if (eq 'package kind)
+                    (format nil "package-~A" (alphanumize (get-name doc)))
+                    (format nil "~A-~A-~A"
+                            (case (get-kind doc)
+                              ((function generic-function) "fun")
+                              (structure "struct")
+                              (variable "var")
+                              (otherwise (symbol-name (get-kind doc))))
+                            (alphanumize (let ((*base-package* nil))
+                                           (short-package-name (get-package doc))))
+                            (alphanumize (get-name doc)))))))
+    (make-pathname :name name  :type "texinfo")))
+
+;;;; documentation class and related methods
+
+(defclass documentation ()
+  ((name :initarg :name :reader get-name)
+   (kind :initarg :kind :reader get-kind)
+   (string :initarg :string :reader get-string)
+   (children :initarg :children :initform nil :reader get-children)
+   (package :initform *documentation-package* :reader get-package)))
+
+(defmethod print-object ((documentation documentation) stream)
+  (print-unreadable-object (documentation stream :type t)
+    (princ (list (get-kind documentation) (get-name documentation)) stream)))
+
+(defgeneric make-documentation (x doc-type string))
+
+(defmethod make-documentation ((x package) doc-type string)
+  (declare (ignore doc-type))
+  (make-instance 'documentation
+                 :name (name x)
+                 :kind 'package
+                 :string string))
+
+(defmethod make-documentation (x (doc-type (eql 'function)) string)
+  (declare (ignore doc-type))
+  (let* ((fdef (and (fboundp x) (fdefinition x)))
+         (name x)
+         (kind (cond ((and (symbolp x) (special-operator-p x))
+                      'special-operator)
+                     ((and (symbolp x) (macro-function x))
+                      'macro)
+                     ((typep fdef 'generic-function)
+                      (assert (or (symbolp name) (setf-name-p name)))
+                      'generic-function)
+                     (fdef
+                      (assert (or (symbolp name) (setf-name-p name)))
+                      'function)))
+         (children (when (eq kind 'generic-function)
+                     (collect-gf-documentation fdef))))
+    (make-instance 'documentation
+                   :name (name x)
+                   :string string
+                   :kind kind
+                   :children children)))
+
+(defmethod make-documentation ((x method) doc-type string)
+  (declare (ignore doc-type))
+  (make-instance 'documentation
+                 :name (name x)
+                 :kind 'method
+                 :string string))
+
+(defmethod make-documentation (x (doc-type (eql 'type)) string)
+  (make-instance 'documentation
+                 :name (name x)
+                 :string string
+                 :kind (etypecase (find-class x nil)
+                         (structure-class 'structure)
+                         (standard-class 'class)
+                         (sb-pcl::condition-class 'condition)
+                         ((or built-in-class null) 'type))))
+
+(defmethod make-documentation (x (doc-type (eql 'variable)) string)
+  (make-instance 'documentation
+                 :name (name x)
+                 :string string
+                 :kind (if (constantp x)
+                           'constant
+                           'variable)))
+
+(defmethod make-documentation (x (doc-type (eql 'setf)) string)
+  (declare (ignore doc-type))
+  (make-instance 'documentation
+                 :name (name x)
+                 :kind 'setf-expander
+                 :string string))
+
+(defmethod make-documentation (x doc-type string)
+  (make-instance 'documentation
+                 :name (name x)
+                 :kind doc-type
+                 :string string))
+
+(defun maybe-documentation (x doc-type)
+  "Returns a DOCUMENTATION instance for X and DOC-TYPE, or NIL if
+there is no corresponding docstring."
+  (let ((docstring (docstring x doc-type)))
+    (when docstring
+      (make-documentation x doc-type docstring))))
+
+(defun lambda-list (doc)
+  (case (get-kind doc)
+    ((package constant variable type structure class condition nil)
+     nil)
+    (method
+     (third (get-name doc)))
+    (t
+     ;; KLUDGE: Eugh.
+     ;;
+     ;; believe it or not, the above comment was written before CSR
+     ;; came along and obfuscated this.  (2005-07-04)
+     (when (symbolp (get-name doc))
+       (labels ((clean (x &key optional key)
+                  (typecase x
+                    (atom x)
+                    ((cons (member &optional))
+                     (cons (car x) (clean (cdr x) :optional t)))
+                    ((cons (member &key))
+                     (cons (car x) (clean (cdr x) :key t)))
+                    ((cons (member &whole &environment))
+                     ;; Skip these
+                     (clean (cdr x) :optional optional :key key))
+                    ((cons cons)
+                     (cons
+                      (cond (key (if (consp (caar x))
+                                     (caaar x)
+                                     (caar x)))
+                            (optional (caar x))
+                            (t (clean (car x))))
+                      (clean (cdr x) :key key :optional optional)))
+                    (cons
+                     (cons
+                      (cond ((or key optional) (car x))
+                            (t (clean (car x))))
+                      (clean (cdr x) :key key :optional optional))))))
+         (clean (sb-introspect:function-lambda-list (get-name doc))))))))
+
+(defun get-string-name (x)
+  (let ((name (get-name x)))
+    (cond ((symbolp name)
+           (symbol-name name))
+          ((and (consp name) (eq 'setf (car name)))
+           (symbol-name (second name)))
+          ((stringp name)
+           name)
+          (t
+           (error "Don't know which symbol to use for name ~S" name)))))
+
+(defun documentation< (x y)
+  (let ((p1 (position (get-kind x) *ordered-documentation-kinds*))
+        (p2 (position (get-kind y) *ordered-documentation-kinds*)))
+    (if (or (not (and p1 p2)) (= p1 p2))
+        (string< (get-string-name x) (get-string-name y))
+        (< p1 p2))))
+
+;;;; turning text into texinfo
+
+(defun escape-for-texinfo (string &optional downcasep)
+  "Return STRING with characters in *TEXINFO-ESCAPED-CHARS* escaped
+with #\@. Optionally downcase the result."
+  (let ((result (with-output-to-string (s)
+                  (loop for char across string
+                        when (find char *texinfo-escaped-chars*)
+                        do (write-char #\@ s)
+                        do (write-char char s)))))
+    (if downcasep (nstring-downcase result) result)))
+
+(defun empty-p (line-number lines)
+  (and (< -1 line-number (length lines))
+       (not (indentation (svref lines line-number)))))
+
+;;; line markups
+
+(defvar *not-symbols* '("ANSI" "CLHS"))
+
+(defun locate-symbols (line)
+  "Return a list of index pairs of symbol-like parts of LINE."
+  ;; This would be a good application for a regex ...
+  (let (result)
+    (flet ((grab (start end)
+             (unless (member (subseq line start end) '("ANSI" "CLHS"))
+               (push (list start end) result))))
+      (do ((begin nil)
+           (maybe-begin t)
+           (i 0 (1+ i)))
+          ((= i (length line))
+           ;; symbol at end of line
+           (when (and begin (or (> i (1+ begin))
+                                (not (member (char line begin) '(#\A #\I)))))
+             (grab begin i))
+           (nreverse result))
+        (cond
+          ((and begin (find (char line i) *symbol-delimiters*))
+           ;; symbol end; remember it if it's not "A" or "I"
+           (when (or (> i (1+ begin)) (not (member (char line begin) '(#\A #\I))))
+             (grab begin i))
+           (setf begin nil
+                 maybe-begin t))
+          ((and begin (not (find (char line i) *symbol-characters*)))
+           ;; Not a symbol: abort
+           (setf begin nil))
+          ((and maybe-begin (not begin) (find (char line i) *symbol-characters*))
+           ;; potential symbol begin at this position
+           (setf begin i
+                 maybe-begin nil))
+          ((find (char line i) *symbol-delimiters*)
+           ;; potential symbol begin after this position
+           (setf maybe-begin t))
+          (t
+           ;; Not reading a symbol, not at potential start of symbol
+           (setf maybe-begin nil)))))))
+
+(defun texinfo-line (line)
+  "Format symbols in LINE texinfo-style: either as code or as
+variables if the symbol in question is contained in symbols
+*TEXINFO-VARIABLES*."
+  (with-output-to-string (result)
+    (let ((last 0))
+      (dolist (symbol/index (locate-symbols line))
+        (write-string (subseq line last (first symbol/index)) result)
+        (let ((symbol-name (apply #'subseq line symbol/index)))
+          (format result (if (member symbol-name *texinfo-variables*
+                                     :test #'string=)
+                             "@var{~A}"
+                             "@code{~A}")
+                  (string-downcase symbol-name)))
+        (setf last (second symbol/index)))
+      (write-string (subseq line last) result))))
+
+;;; lisp sections
+
+(defun lisp-section-p (line line-number lines)
+  "Returns T if the given LINE looks like start of lisp code --
+ie. if it starts with whitespace followed by a paren or
+semicolon, and the previous line is empty"
+  (let ((offset (indentation line)))
+    (and offset
+         (plusp offset)
+         (find (find-if-not #'whitespacep line) "(;")
+         (empty-p (1- line-number) lines))))
+
+(defun collect-lisp-section (lines line-number)
+  (let ((lisp (loop for index = line-number then (1+ index)
+                    for line = (and (< index (length lines)) (svref lines index))
+                    while (indentation line)
+                    collect line)))
+    (values (length lisp) `("@lisp" ,@lisp "@end lisp"))))
+
+;;; itemized sections
+
+(defun maybe-itemize-offset (line)
+  "Return NIL or the indentation offset if LINE looks like it starts
+an item in an itemization."
+  (let* ((offset (indentation line))
+         (char (when offset (char line offset))))
+    (and offset
+         (member char *itemize-start-characters* :test #'char=)
+         (char= #\Space (find-if-not (lambda (c) (char= c char))
+                                     line :start offset))
+         offset)))
+
+(defun collect-maybe-itemized-section (lines starting-line)
+  ;; Return index of next line to be processed outside
+  (let ((this-offset (maybe-itemize-offset (svref lines starting-line)))
+        (result nil)
+        (lines-consumed 0))
+    (loop for line-number from starting-line below (length lines)
+       for line = (svref lines line-number)
+       for indentation = (indentation line)
+       for offset = (maybe-itemize-offset line)
+       do (cond
+            ((not indentation)
+             ;; empty line -- inserts paragraph.
+             (push "" result)
+             (incf lines-consumed))
+            ((and offset (> indentation this-offset))
+             ;; nested itemization -- handle recursively
+             ;; FIXME: tables in itemizations go wrong
+             (multiple-value-bind (sub-lines-consumed sub-itemization)
+                 (collect-maybe-itemized-section lines line-number)
+               (when sub-lines-consumed
+                 (incf line-number (1- sub-lines-consumed)) ; +1 on next loop
+                 (incf lines-consumed sub-lines-consumed)
+                 (setf result (nconc (nreverse sub-itemization) result)))))
+            ((and offset (= indentation this-offset))
+             ;; start of new item
+             (push (format nil "@item ~A"
+                           (texinfo-line (subseq line (1+ offset))))
+                   result)
+             (incf lines-consumed))
+            ((and (not offset) (> indentation this-offset))
+             ;; continued item from previous line
+             (push (texinfo-line line) result)
+             (incf lines-consumed))
+            (t
+             ;; end of itemization
+             (loop-finish))))
+    ;; a single-line itemization isn't.
+    (if (> (count-if (lambda (line) (> (length line) 0)) result) 1)
+        (values lines-consumed `("@itemize" ,@(reverse result) "@end itemize"))
+        nil)))
+
+;;; table sections
+
+(defun tabulation-body-p (offset line-number lines)
+  (when (< line-number (length lines))
+    (let ((offset2 (indentation (svref lines line-number))))
+      (and offset2 (< offset offset2)))))
+
+(defun tabulation-p (offset line-number lines direction)
+  (let ((step  (ecase direction
+                 (:backwards (1- line-number))
+                 (:forwards (1+ line-number)))))
+    (when (and (plusp line-number) (< line-number (length lines)))
+      (and (eql offset (indentation (svref lines line-number)))
+           (or (when (eq direction :backwards)
+                 (empty-p step lines))
+               (tabulation-p offset step lines direction)
+               (tabulation-body-p offset step lines))))))
+
+(defun maybe-table-offset (line-number lines)
+  "Return NIL or the indentation offset if LINE looks like it starts
+an item in a tabulation. Ie, if it is (1) indented, (2) preceded by an
+empty line, another tabulation label, or a tabulation body, (3) and
+followed another tabulation label or a tabulation body."
+  (let* ((line (svref lines line-number))
+         (offset (indentation line))
+         (prev (1- line-number))
+         (next (1+ line-number)))
+    (when (and offset (plusp offset))
+      (and (or (empty-p prev lines)
+               (tabulation-body-p offset prev lines)
+               (tabulation-p offset prev lines :backwards))
+           (or (tabulation-body-p offset next lines)
+               (tabulation-p offset next lines :forwards))
+           offset))))
+
+;;; FIXME: This and itemization are very similar: could they share
+;;; some code, mayhap?
+
+(defun collect-maybe-table-section (lines starting-line)
+  ;; Return index of next line to be processed outside
+  (let ((this-offset (maybe-table-offset starting-line lines))
+        (result nil)
+        (lines-consumed 0))
+    (loop for line-number from starting-line below (length lines)
+          for line = (svref lines line-number)
+          for indentation = (indentation line)
+          for offset = (maybe-table-offset line-number lines)
+          do (cond
+               ((not indentation)
+                ;; empty line -- inserts paragraph.
+                (push "" result)
+                (incf lines-consumed))
+               ((and offset (= indentation this-offset))
+                ;; start of new item, or continuation of previous item
+                (if (and result (search "@item" (car result) :test #'char=))
+                    (push (format nil "@itemx ~A" (texinfo-line line))
+                          result)
+                    (progn
+                      (push "" result)
+                      (push (format nil "@item ~A" (texinfo-line line))
+                            result)))
+                (incf lines-consumed))
+               ((> indentation this-offset)
+                ;; continued item from previous line
+                (push (texinfo-line line) result)
+                (incf lines-consumed))
+               (t
+                ;; end of itemization
+                (loop-finish))))
+     ;; a single-line table isn't.
+    (if (> (count-if (lambda (line) (> (length line) 0)) result) 1)
+        (values lines-consumed
+                `("" "@table @emph" ,@(reverse result) "@end table" ""))
+        nil)))
+
+;;; section markup
+
+(defmacro with-maybe-section (index &rest forms)
+  `(multiple-value-bind (count collected) (progn ,@forms)
+    (when count
+      (dolist (line collected)
+        (write-line line *texinfo-output*))
+      (incf ,index (1- count)))))
+
+(defun write-texinfo-string (string &optional lambda-list)
+  "Try to guess as much formatting for a raw docstring as possible."
+  (let ((*texinfo-variables* (flatten lambda-list))
+        (lines (string-lines (escape-for-texinfo string nil))))
+      (loop for line-number from 0 below (length lines)
+            for line = (svref lines line-number)
+            do (cond
+                 ((with-maybe-section line-number
+                    (and (lisp-section-p line line-number lines)
+                         (collect-lisp-section lines line-number))))
+                 ((with-maybe-section line-number
+                    (and (maybe-itemize-offset line)
+                         (collect-maybe-itemized-section lines line-number))))
+                 ((with-maybe-section line-number
+                    (and (maybe-table-offset line-number lines)
+                         (collect-maybe-table-section lines line-number))))
+                 (t
+                  (write-line (texinfo-line line) *texinfo-output*))))))
+
+;;;; texinfo formatting tools
+
+(defun hide-superclass-p (class-name super-name)
+  (let ((super-package (symbol-package super-name)))
+    (or
+     ;; KLUDGE: We assume that we don't want to advertise internal
+     ;; classes in CP-lists, unless the symbol we're documenting is
+     ;; internal as well.
+     (and (member super-package #.'(mapcar #'find-package *undocumented-packages*))
+          (not (eq super-package (symbol-package class-name))))
+     ;; KLUDGE: We don't generally want to advertise SIMPLE-ERROR or
+     ;; SIMPLE-CONDITION in the CPLs of conditions that inherit them
+     ;; simply as a matter of convenience. The assumption here is that
+     ;; the inheritance is incidental unless the name of the condition
+     ;; begins with SIMPLE-.
+     (and (member super-name '(simple-error simple-condition))
+          (let ((prefix "SIMPLE-"))
+            (mismatch prefix (string class-name) :end2 (length prefix)))
+          t ; don't return number from MISMATCH
+          ))))
+
+(defun hide-slot-p (symbol slot)
+  ;; FIXME: There is no pricipal reason to avoid the slot docs fo
+  ;; structures and conditions, but their DOCUMENTATION T doesn't
+  ;; currently work with them the way we'd like.
+  (not (and (typep (find-class symbol nil) 'standard-class)
+            (docstring slot t))))
+
+(defun texinfo-anchor (doc)
+  (format *texinfo-output* "@anchor{~A}~%" (node-name doc)))
+
+;;; KLUDGE: &AUX *PRINT-PRETTY* here means "no linebreaks please"
+(defun texinfo-begin (doc &aux *print-pretty*)
+  (let ((kind (get-kind doc)))
+    (format *texinfo-output* "@~A {~:(~A~)} ~({~A}~@[ ~{~A~^ ~}~]~)~%"
+            (case kind
+              ((package constant variable)
+               "defvr")
+              ((structure class condition type)
+               "deftp")
+              (t
+               "deffn"))
+            (map 'string (lambda (char) (if (eql char #\-) #\Space char)) (string kind))
+            (title-name doc)
+            ;; &foo would be amusingly bold in the pdf thanks to TeX/Texinfo
+            ;; interactions,so we escape the ampersand -- amusingly for TeX.
+            ;; sbcl.texinfo defines macros that expand @&key and friends to &key.
+            (mapcar (lambda (name)
+                      (if (member name lambda-list-keywords)
+                          (format nil "@~A" name)
+                          name))
+                    (lambda-list doc)))))
+
+(defun texinfo-index (doc)
+  (let ((title (title-name doc)))
+    (case (get-kind doc)
+      ((structure type class condition)
+       (format *texinfo-output* "@tindex ~A~%" title))
+      ((variable constant)
+       (format *texinfo-output* "@vindex ~A~%" title))
+      ((compiler-macro function method-combination macro generic-function)
+       (format *texinfo-output* "@findex ~A~%" title)))))
+
+(defun texinfo-inferred-body (doc)
+  (when (member (get-kind doc) '(class structure condition))
+    (let ((name (get-name doc)))
+      ;; class precedence list
+      (format *texinfo-output* "Class precedence list: @code{~(~{@lw{~A}~^, ~}~)}~%~%"
+              (remove-if (lambda (class)  (hide-superclass-p name class))
+                         (mapcar #'class-name (ensure-class-precedence-list (find-class name)))))
+      ;; slots
+      (let ((slots (remove-if (lambda (slot) (hide-slot-p name slot))
+                              (class-direct-slots (find-class name)))))
+        (when slots
+          (format *texinfo-output* "Slots:~%@itemize~%")
+          (dolist (slot slots)
+            (format *texinfo-output*
+                    "@item ~(@code{~A}~#[~:; --- ~]~
+                      ~:{~2*~@[~2:*~A~P: ~{@code{@w{~S}}~^, ~}~]~:^; ~}~)~%~%"
+                    (slot-definition-name slot)
+                    (remove
+                     nil
+                     (mapcar
+                      (lambda (name things)
+                        (if things
+                            (list name (length things) things)))
+                      '("initarg" "reader"  "writer")
+                      (list
+                       (slot-definition-initargs slot)
+                       (slot-definition-readers slot)
+                       (slot-definition-writers slot)))))
+            ;; FIXME: Would be neater to handler as children
+            (write-texinfo-string (docstring slot t)))
+          (format *texinfo-output* "@end itemize~%~%"))))))
+
+(defun texinfo-body (doc)
+  (write-texinfo-string (get-string doc)))
+
+(defun texinfo-end (doc)
+  (write-line (case (get-kind doc)
+                ((package variable constant) "@end defvr")
+                ((structure type class condition) "@end deftp")
+                (t "@end deffn"))
+              *texinfo-output*))
+
+(defun write-texinfo (doc)
+  "Writes TexInfo for a DOCUMENTATION instance to *TEXINFO-OUTPUT*."
+  (texinfo-anchor doc)
+  (texinfo-begin doc)
+  (texinfo-index doc)
+  (texinfo-inferred-body doc)
+  (texinfo-body doc)
+  (texinfo-end doc)
+  ;; FIXME: Children should be sorted one way or another
+  (mapc #'write-texinfo (get-children doc)))
+
+;;;; main logic
+
+(defun collect-gf-documentation (gf)
+  "Collects method documentation for the generic function GF"
+  (loop for method in (generic-function-methods gf)
+        for doc = (maybe-documentation method t)
+        when doc
+        collect doc))
+
+(defun collect-name-documentation (name)
+  (loop for type in *documentation-types*
+        for doc = (maybe-documentation name type)
+        when doc
+        collect doc))
+
+(defun collect-symbol-documentation (symbol)
+  "Collects all docs for a SYMBOL and (SETF SYMBOL), returns a list of
+the form DOC instances. See `*documentation-types*' for the possible
+values of doc-type."
+  (nconc (collect-name-documentation symbol)
+         (collect-name-documentation (list 'setf symbol))))
+
+(defun collect-documentation (package)
+  "Collects all documentation for all external symbols of the given
+package, as well as for the package itself."
+  (let* ((*documentation-package* (find-package package))
+         (docs nil))
+    (check-type package package)
+    (do-external-symbols (symbol package)
+      (setf docs (nconc (collect-symbol-documentation symbol) docs)))
+    (let ((doc (maybe-documentation *documentation-package* t)))
+      (when doc
+        (push doc docs)))
+    docs))
+
+(defmacro with-texinfo-file (pathname &body forms)
+  `(with-open-file (*texinfo-output* ,pathname
+                                    :direction :output
+                                    :if-does-not-exist :create
+                                    :if-exists :supersede)
+    ,@forms))
+
+(defun write-ifnottex ()
+  ;; We use @&key, etc to escape & from TeX in lambda lists -- so we need to
+  ;; define them for info as well.
+  (flet ((macro (name)
+                 (let ((string (string-downcase name)))
+                   (format *texinfo-output* "@macro ~A~%~A~%@end macro~%" string string))))
+    (macro '&allow-other-keys)
+    (macro '&optional)
+    (macro '&rest)
+    (macro '&key)
+    (macro '&body)))
+
+(defun generate-includes (directory packages &key (base-package :cl-user))
+  "Create files in `directory' containing Texinfo markup of all
+docstrings of each exported symbol in `packages'. `directory' is
+created if necessary. If you supply a namestring that doesn't end in a
+slash, you lose. The generated files are of the form
+\"<doc-type>_<packagename>_<symbol-name>.texinfo\" and can be included
+via @include statements. Texinfo syntax-significant characters are
+escaped in symbol names, but if a docstring contains invalid Texinfo
+markup, you lose."
+  (handler-bind ((warning #'muffle-warning))
+    (let ((directory (merge-pathnames (pathname directory)))
+          (*base-package* (find-package base-package)))
+      (ensure-directories-exist directory)
+      (dolist (package packages)
+        (dolist (doc (collect-documentation (find-package package)))
+          (with-texinfo-file (merge-pathnames (include-pathname doc) directory)
+            (write-texinfo doc))))
+      (with-texinfo-file (merge-pathnames "ifnottex.texinfo" directory)
+        (write-ifnottex))
+      directory)))
+
+(defun document-package (package &optional filename)
+  "Create a file containing all available documentation for the
+exported symbols of `package' in Texinfo format. If `filename' is not
+supplied, a file \"<packagename>.texinfo\" is generated.
+
+The definitions can be referenced using Texinfo statements like
+@ref{<doc-type>_<packagename>_<symbol-name>.texinfo}. Texinfo
+syntax-significant characters are escaped in symbol names, but if a
+docstring contains invalid Texinfo markup, you lose."
+  (handler-bind ((warning #'muffle-warning))
+    (let* ((package (find-package package))
+           (filename (or filename (make-pathname
+                                   :name (string-downcase (short-package-name package))
+                                   :type "texinfo")))
+           (docs (sort (collect-documentation package) #'documentation<)))
+      (with-texinfo-file filename
+        (dolist (doc docs)
+          (write-texinfo doc)))
+      filename)))
diff --git a/deps/alexandria/features.lisp b/deps/alexandria/features.lisp
new file mode 100644 (file)
index 0000000..67348db
--- /dev/null
@@ -0,0 +1,14 @@
+(in-package :alexandria)
+
+(defun featurep (feature-expression)
+  "Returns T if the argument matches the state of the *FEATURES*
+list and NIL if it does not. FEATURE-EXPRESSION can be any atom
+or list acceptable to the reader macros #+ and #-."
+  (etypecase feature-expression
+    (symbol (not (null (member feature-expression *features*))))
+    (cons (check-type (first feature-expression) symbol)
+          (eswitch ((first feature-expression) :test 'string=)
+            (:and (every #'featurep (rest feature-expression)))
+            (:or  (some #'featurep (rest feature-expression)))
+            (:not (assert (= 2 (length feature-expression)))
+                  (not (featurep (second feature-expression))))))))
diff --git a/deps/alexandria/functions.lisp b/deps/alexandria/functions.lisp
new file mode 100644 (file)
index 0000000..a04b7d0
--- /dev/null
@@ -0,0 +1,161 @@
+(in-package :alexandria)
+
+;;; To propagate return type and allow the compiler to eliminate the IF when
+;;; it is known if the argument is function or not.
+(declaim (inline ensure-function))
+
+(declaim (ftype (function (t) (values function &optional))
+                ensure-function))
+(defun ensure-function (function-designator)
+  "Returns the function designated by FUNCTION-DESIGNATOR:
+if FUNCTION-DESIGNATOR is a function, it is returned, otherwise
+it must be a function name and its FDEFINITION is returned."
+  (if (functionp function-designator)
+      function-designator
+      (fdefinition function-designator)))
+
+(define-modify-macro ensure-functionf/1 () ensure-function)
+
+(defmacro ensure-functionf (&rest places)
+  "Multiple-place modify macro for ENSURE-FUNCTION: ensures that each of
+PLACES contains a function."
+  `(progn ,@(mapcar (lambda (x) `(ensure-functionf/1 ,x)) places)))
+
+(defun disjoin (predicate &rest more-predicates)
+  "Returns a function that applies each of PREDICATE and MORE-PREDICATE
+functions in turn to its arguments, returning the primary value of the first
+predicate that returns true, without calling the remaining predicates.
+If none of the predicates returns true, NIL is returned."
+  (declare (optimize (speed 3) (safety 1) (debug 1)))
+  (let ((predicate (ensure-function predicate))
+       (more-predicates (mapcar #'ensure-function more-predicates)))
+    (lambda (&rest arguments)
+      (or (apply predicate arguments)
+         (some (lambda (p)
+                 (declare (type function p))
+                 (apply p arguments))
+               more-predicates)))))
+
+(defun conjoin (predicate &rest more-predicates)
+  "Returns a function that applies each of PREDICATE and MORE-PREDICATE
+functions in turn to its arguments, returning NIL if any of the predicates
+returns false, without calling the remaining predicates. If none of the
+predicates returns false, returns the primary value of the last predicate."
+  (if (null more-predicates)
+      predicate
+      (lambda (&rest arguments)
+       (and (apply predicate arguments)
+            ;; Cannot simply use CL:EVERY because we want to return the
+            ;; non-NIL value of the last predicate if all succeed.
+            (do ((tail (cdr more-predicates) (cdr tail))
+                 (head (car more-predicates) (car tail)))
+                ((not tail)
+                 (apply head arguments))
+              (unless (apply head arguments)
+                (return nil)))))))
+
+
+(defun compose (function &rest more-functions)
+  "Returns a function composed of FUNCTION and MORE-FUNCTIONS that applies its
+arguments to to each in turn, starting from the rightmost of MORE-FUNCTIONS,
+and then calling the next one with the primary value of the last."
+  (declare (optimize (speed 3) (safety 1) (debug 1)))
+  (reduce (lambda (f g)
+           (let ((f (ensure-function f))
+                 (g (ensure-function g)))
+             (lambda (&rest arguments)
+               (declare (dynamic-extent arguments))
+               (funcall f (apply g arguments)))))
+          more-functions
+          :initial-value function))
+
+(define-compiler-macro compose (function &rest more-functions)
+  (labels ((compose-1 (funs)
+             (if (cdr funs)
+                 `(funcall ,(car funs) ,(compose-1 (cdr funs)))
+                 `(apply ,(car funs) arguments))))
+    (let* ((args (cons function more-functions))
+           (funs (make-gensym-list (length args) "COMPOSE")))
+      `(let ,(loop for f in funs for arg in args
+                  collect `(,f (ensure-function ,arg)))
+         (declare (optimize (speed 3) (safety 1) (debug 1)))
+         (lambda (&rest arguments)
+           (declare (dynamic-extent arguments))
+           ,(compose-1 funs))))))
+
+(defun multiple-value-compose (function &rest more-functions)
+    "Returns a function composed of FUNCTION and MORE-FUNCTIONS that applies
+its arguments to each in turn, starting from the rightmost of
+MORE-FUNCTIONS, and then calling the next one with all the return values of
+the last."
+  (declare (optimize (speed 3) (safety 1) (debug 1)))
+  (reduce (lambda (f g)
+           (let ((f (ensure-function f))
+                 (g (ensure-function g)))
+             (lambda (&rest arguments)
+               (declare (dynamic-extent arguments))
+               (multiple-value-call f (apply g arguments)))))
+          more-functions
+          :initial-value function))
+
+(define-compiler-macro multiple-value-compose (function &rest more-functions)
+  (labels ((compose-1 (funs)
+             (if (cdr funs)
+                 `(multiple-value-call ,(car funs) ,(compose-1 (cdr funs)))
+                 `(apply ,(car funs) arguments))))
+    (let* ((args (cons function more-functions))
+           (funs (make-gensym-list (length args) "MV-COMPOSE")))
+      `(let ,(mapcar #'list funs args)
+         (declare (optimize (speed 3) (safety 1) (debug 1)))
+         (lambda (&rest arguments)
+           (declare (dynamic-extent arguments))
+           ,(compose-1 funs))))))
+
+(declaim (inline curry rcurry))
+
+(defun curry (function &rest arguments)
+  "Returns a function that applies ARGUMENTS and the arguments
+it is called with to FUNCTION."
+  (declare (optimize (speed 3) (safety 1)))
+  (let ((fn (ensure-function function)))
+    (lambda (&rest more)
+      (declare (dynamic-extent more))
+      ;; Using M-V-C we don't need to append the arguments.
+      (multiple-value-call fn (values-list arguments) (values-list more)))))
+
+(define-compiler-macro curry (function &rest arguments)
+  (let ((curries (make-gensym-list (length arguments) "CURRY"))
+        (fun (gensym "FUN")))
+    `(let ((,fun (ensure-function ,function))
+           ,@(mapcar #'list curries arguments))
+       (declare (optimize (speed 3) (safety 1)))
+       (lambda (&rest more)
+         (declare (dynamic-extent more))
+         (apply ,fun ,@curries more)))))
+
+(defun rcurry (function &rest arguments)
+  "Returns a function that applies the arguments it is called
+with and ARGUMENTS to FUNCTION."
+  (declare (optimize (speed 3) (safety 1)))
+  (let ((fn (ensure-function function)))
+    (lambda (&rest more)
+      (declare (dynamic-extent more))
+      (multiple-value-call fn (values-list more) (values-list arguments)))))
+
+(define-compiler-macro rcurry (function &rest arguments)
+  (let ((rcurries (make-gensym-list (length arguments) "RCURRY"))
+        (fun (gensym "FUN")))
+    `(let ((,fun (ensure-function ,function))
+           ,@(mapcar #'list rcurries arguments))
+       (declare (optimize (speed 3) (safety 1)))
+       (lambda (&rest more)
+         (declare (dynamic-extent more))
+         (multiple-value-call ,fun (values-list more) ,@rcurries)))))
+
+(declaim (notinline curry rcurry))
+
+(defmacro named-lambda (name lambda-list &body body)
+  "Expands into a lambda-expression within whose BODY NAME denotes the
+corresponding function."
+  `(labels ((,name ,lambda-list ,@body))
+     #',name))
\ No newline at end of file
diff --git a/deps/alexandria/hash-tables.lisp b/deps/alexandria/hash-tables.lisp
new file mode 100644 (file)
index 0000000..3a6c3eb
--- /dev/null
@@ -0,0 +1,101 @@
+(in-package :alexandria)
+
+(defun copy-hash-table (table &key key test size
+                                   rehash-size rehash-threshold)
+  "Returns a copy of hash table TABLE, with the same keys and values
+as the TABLE. The copy has the same properties as the original, unless
+overridden by the keyword arguments.
+
+Before each of the original values is set into the new hash-table, KEY
+is invoked on the value. As KEY defaults to CL:IDENTITY, a shallow
+copy is returned by default."
+  (setf key (or key 'identity))
+  (setf test (or test (hash-table-test table)))
+  (setf size (or size (hash-table-size table)))
+  (setf rehash-size (or rehash-size (hash-table-rehash-size table)))
+  (setf rehash-threshold (or rehash-threshold (hash-table-rehash-threshold table)))
+  (let ((copy (make-hash-table :test test :size size
+                               :rehash-size rehash-size
+                               :rehash-threshold rehash-threshold)))
+    (maphash (lambda (k v)
+               (setf (gethash k copy) (funcall key v)))
+             table)
+    copy))
+
+(declaim (inline maphash-keys))
+(defun maphash-keys (function table)
+  "Like MAPHASH, but calls FUNCTION with each key in the hash table TABLE."
+  (maphash (lambda (k v)
+             (declare (ignore v))
+             (funcall function k))
+           table))
+
+(declaim (inline maphash-values))
+(defun maphash-values (function table)
+  "Like MAPHASH, but calls FUNCTION with each value in the hash table TABLE."
+  (maphash (lambda (k v)
+             (declare (ignore k))
+             (funcall function v))
+           table))
+
+(defun hash-table-keys (table)
+  "Returns a list containing the keys of hash table TABLE."
+  (let ((keys nil))
+    (maphash-keys (lambda (k)
+                    (push k keys))
+                  table)
+    keys))
+
+(defun hash-table-values (table)
+  "Returns a list containing the values of hash table TABLE."
+  (let ((values nil))
+    (maphash-values (lambda (v)
+                      (push v values))
+                    table)
+    values))
+
+(defun hash-table-alist (table)
+  "Returns an association list containing the keys and values of hash table
+TABLE."
+  (let ((alist nil))
+    (maphash (lambda (k v)
+               (push (cons k v) alist))
+             table)
+    alist))
+
+(defun hash-table-plist (table)
+  "Returns a property list containing the keys and values of hash table
+TABLE."
+  (let ((plist nil))
+    (maphash (lambda (k v)
+               (setf plist (list* k v plist)))
+             table)
+    plist))
+
+(defun alist-hash-table (alist &rest hash-table-initargs)
+  "Returns a hash table containing the keys and values of the association list
+ALIST. Hash table is initialized using the HASH-TABLE-INITARGS."
+  (let ((table (apply #'make-hash-table hash-table-initargs)))
+    (dolist (cons alist)
+      (setf (gethash (car cons) table) (cdr cons)))
+    table))
+
+(defun plist-hash-table (plist &rest hash-table-initargs)
+  "Returns a hash table containing the keys and values of the property list
+PLIST. Hash table is initialized using the HASH-TABLE-INITARGS."
+  (let ((table (apply #'make-hash-table hash-table-initargs)))
+    (do ((tail plist (cddr tail)))
+        ((not tail))
+      (setf (gethash (car tail) table) (cadr tail)))
+    table))
+
+(defmacro ensure-gethash (key hash-table &optional default)
+  "Like GETHASH, but if KEY is not found in the HASH-TABLE saves the DEFAULT
+under key before returning it. Secondary return value is true if key was
+already in the table."
+  (once-only (key hash-table)
+    (with-unique-names (value presentp)
+      `(multiple-value-bind (,value ,presentp) (gethash ,key ,hash-table)
+         (if ,presentp
+             (values ,value ,presentp)
+             (values (setf (gethash ,key ,hash-table) ,default) nil))))))
diff --git a/deps/alexandria/io.lisp b/deps/alexandria/io.lisp
new file mode 100644 (file)
index 0000000..28bf5e6
--- /dev/null
@@ -0,0 +1,172 @@
+;; Copyright (c) 2002-2006, Edward Marco Baringer
+;; All rights reserved.
+
+(in-package :alexandria)
+
+(defmacro with-open-file* ((stream filespec &key direction element-type
+                                   if-exists if-does-not-exist external-format)
+                           &body body)
+  "Just like WITH-OPEN-FILE, but NIL values in the keyword arguments mean to use
+the default value specified for OPEN."
+  (once-only (direction element-type if-exists if-does-not-exist external-format)
+    `(with-open-stream
+         (,stream (apply #'open ,filespec
+                         (append
+                          (when ,direction
+                            (list :direction ,direction))
+                          (when ,element-type
+                            (list :element-type ,element-type))
+                          (when ,if-exists
+                            (list :if-exists ,if-exists))
+                          (when ,if-does-not-exist
+                            (list :if-does-not-exist ,if-does-not-exist))
+                          (when ,external-format
+                            (list :external-format ,external-format)))))
+       ,@body)))
+
+(defmacro with-input-from-file ((stream-name file-name &rest args
+                                             &key (direction nil direction-p)
+                                             &allow-other-keys)
+                                &body body)
+  "Evaluate BODY with STREAM-NAME to an input stream on the file
+FILE-NAME. ARGS is sent as is to the call to OPEN except EXTERNAL-FORMAT,
+which is only sent to WITH-OPEN-FILE when it's not NIL."
+  (declare (ignore direction))
+  (when direction-p
+    (error "Can't specify :DIRECTION for WITH-INPUT-FROM-FILE."))
+  `(with-open-file* (,stream-name ,file-name :direction :input ,@args)
+     ,@body))
+
+(defmacro with-output-to-file ((stream-name file-name &rest args
+                                            &key (direction nil direction-p)
+                                            &allow-other-keys)
+                              &body body)
+  "Evaluate BODY with STREAM-NAME to an output stream on the file
+FILE-NAME. ARGS is sent as is to the call to OPEN except EXTERNAL-FORMAT,
+which is only sent to WITH-OPEN-FILE when it's not NIL."
+  (declare (ignore direction))
+  (when direction-p
+    (error "Can't specify :DIRECTION for WITH-OUTPUT-TO-FILE."))
+  `(with-open-file* (,stream-name ,file-name :direction :output ,@args)
+     ,@body))
+
+(defun read-stream-content-into-string (stream &key (buffer-size 4096))
+  "Return the \"content\" of STREAM as a fresh string."
+  (check-type buffer-size positive-integer)
+  (let ((*print-pretty* nil))
+    (with-output-to-string (datum)
+      (let ((buffer (make-array buffer-size :element-type 'character)))
+        (loop
+          :for bytes-read = (read-sequence buffer stream)
+          :do (write-sequence buffer datum :start 0 :end bytes-read)
+          :while (= bytes-read buffer-size))))))
+
+(defun read-file-into-string (pathname &key (buffer-size 4096) external-format)
+  "Return the contents of the file denoted by PATHNAME as a fresh string.
+
+The EXTERNAL-FORMAT parameter will be passed directly to WITH-OPEN-FILE
+unless it's NIL, which means the system default."
+  (with-input-from-file
+      (file-stream pathname :external-format external-format)
+    (read-stream-content-into-string file-stream :buffer-size buffer-size)))
+
+(defun write-string-into-file (string pathname &key (if-exists :error)
+                                                    if-does-not-exist
+                                                    external-format)
+  "Write STRING to PATHNAME.
+
+The EXTERNAL-FORMAT parameter will be passed directly to WITH-OPEN-FILE
+unless it's NIL, which means the system default."
+  (with-output-to-file (file-stream pathname :if-exists if-exists
+                                    :if-does-not-exist if-does-not-exist
+                                    :external-format external-format)
+    (write-sequence string file-stream)))
+
+(defun read-stream-content-into-byte-vector (stream &key ((%length length))
+                                                         (initial-size 4096))
+  "Return \"content\" of STREAM as freshly allocated (unsigned-byte 8) vector."
+  (check-type length (or null non-negative-integer))
+  (check-type initial-size positive-integer)
+  (do ((buffer (make-array (or length initial-size)
+                           :element-type '(unsigned-byte 8)))
+       (offset 0)
+       (offset-wanted 0))
+      ((or (/= offset-wanted offset)
+           (and length (>= offset length)))
+       (if (= offset (length buffer))
+           buffer
+           (subseq buffer 0 offset)))
+    (unless (zerop offset)
+      (let ((new-buffer (make-array (* 2 (length buffer))
+                                    :element-type '(unsigned-byte 8))))
+        (replace new-buffer buffer)
+        (setf buffer new-buffer)))
+    (setf offset-wanted (length buffer)
+          offset (read-sequence buffer stream :start offset))))
+
+(defun read-file-into-byte-vector (pathname)
+  "Read PATHNAME into a freshly allocated (unsigned-byte 8) vector."
+  (with-input-from-file (stream pathname :element-type '(unsigned-byte 8))
+    (read-stream-content-into-byte-vector stream '%length (file-length stream))))
+
+(defun write-byte-vector-into-file (bytes pathname &key (if-exists :error)
+                                                       if-does-not-exist)
+  "Write BYTES to PATHNAME."
+  (check-type bytes (vector (unsigned-byte 8)))
+  (with-output-to-file (stream pathname :if-exists if-exists
+                               :if-does-not-exist if-does-not-exist
+                               :element-type '(unsigned-byte 8))
+    (write-sequence bytes stream)))
+
+(defun copy-file (from to &key (if-to-exists :supersede)
+                              (element-type '(unsigned-byte 8)) finish-output)
+  (with-input-from-file (input from :element-type element-type)
+    (with-output-to-file (output to :element-type element-type
+                                   :if-exists if-to-exists)
+      (copy-stream input output
+                   :element-type element-type
+                   :finish-output finish-output))))
+
+(defun copy-stream (input output &key (element-type (stream-element-type input))
+                    (buffer-size 4096)
+                    (buffer (make-array buffer-size :element-type element-type))
+                    (start 0) end
+                    finish-output)
+  "Reads data from INPUT and writes it to OUTPUT. Both INPUT and OUTPUT must
+be streams, they will be passed to READ-SEQUENCE and WRITE-SEQUENCE and must have
+compatible element-types."
+  (check-type start non-negative-integer)
+  (check-type end (or null non-negative-integer))
+  (check-type buffer-size positive-integer)
+  (when (and end
+             (< end start))
+    (error "END is smaller than START in ~S" 'copy-stream))
+  (let ((output-position 0)
+        (input-position 0))
+    (unless (zerop start)
+      ;; FIXME add platform specific optimization to skip seekable streams
+      (loop while (< input-position start)
+            do (let ((n (read-sequence buffer input
+                                       :end (min (length buffer)
+                                                 (- start input-position)))))
+                 (when (zerop n)
+                   (error "~@<Could not read enough bytes from the input to fulfill ~
+                           the :START ~S requirement in ~S.~:@>" 'copy-stream start))
+                 (incf input-position n))))
+    (assert (= input-position start))
+    (loop while (or (null end) (< input-position end))
+          do (let ((n (read-sequence buffer input
+                                     :end (when end
+                                            (min (length buffer)
+                                                 (- end input-position))))))
+               (when (zerop n)
+                 (if end
+                     (error "~@<Could not read enough bytes from the input to fulfill ~
+                          the :END ~S requirement in ~S.~:@>" 'copy-stream end)
+                     (return)))
+               (incf input-position n)
+               (write-sequence buffer output :end n)
+               (incf output-position n)))
+    (when finish-output
+      (finish-output output))
+    output-position))
diff --git a/deps/alexandria/lists.lisp b/deps/alexandria/lists.lisp
new file mode 100644 (file)
index 0000000..1dfc836
--- /dev/null
@@ -0,0 +1,367 @@
+(in-package :alexandria)
+
+(declaim (inline safe-endp))
+(defun safe-endp (x)
+  (declare (optimize safety))
+  (endp x))
+
+(defun alist-plist (alist)
+  "Returns a property list containing the same keys and values as the
+association list ALIST in the same order."
+  (let (plist)
+    (dolist (pair alist)
+      (push (car pair) plist)
+      (push (cdr pair) plist))
+    (nreverse plist)))
+
+(defun plist-alist (plist)
+  "Returns an association list containing the same keys and values as the
+property list PLIST in the same order."
+  (let (alist)
+    (do ((tail plist (cddr tail)))
+        ((safe-endp tail) (nreverse alist))
+      (push (cons (car tail) (cadr tail)) alist))))
+
+(declaim (inline racons))
+(defun racons (key value ralist)
+  (acons value key ralist))
+
+(macrolet
+    ((define-alist-get (name get-entry get-value-from-entry add doc)
+       `(progn
+          (declaim (inline ,name))
+          (defun ,name (alist key &key (test 'eql))
+            ,doc
+            (let ((entry (,get-entry key alist :test test)))
+              (values (,get-value-from-entry entry) entry)))
+          (define-setf-expander ,name (place key &key (test ''eql)
+                                       &environment env)
+            (multiple-value-bind
+                  (temporary-variables initforms newvals setter getter)
+                (get-setf-expansion place env)
+              (when (cdr newvals)
+                (error "~A cannot store multiple values in one place" ',name))
+              (with-unique-names (new-value key-val test-val alist entry)
+                (values
+                 (append temporary-variables
+                         (list alist
+                               key-val
+                               test-val
+                               entry))
+                 (append initforms
+                         (list getter
+                               key
+                               test
+                               `(,',get-entry ,key-val ,alist :test ,test-val)))
+                 `(,new-value)
+                 `(cond
+                    (,entry
+                     (setf (,',get-value-from-entry ,entry) ,new-value))
+                    (t
+                     (let ,newvals
+                       (setf ,(first newvals) (,',add ,key ,new-value ,alist))
+                       ,setter
+                       ,new-value)))
+                 `(,',get-value-from-entry ,entry))))))))
+ (define-alist-get assoc-value assoc cdr acons
+"ASSOC-VALUE is an alist accessor very much like ASSOC, but it can
+be used with SETF.")
+ (define-alist-get rassoc-value rassoc car racons
+"RASSOC-VALUE is an alist accessor very much like RASSOC, but it can
+be used with SETF."))
+
+(defun malformed-plist (plist)
+  (error "Malformed plist: ~S" plist))
+
+(defmacro doplist ((key val plist &optional values) &body body)
+  "Iterates over elements of PLIST. BODY can be preceded by
+declarations, and is like a TAGBODY. RETURN may be used to terminate
+the iteration early. If RETURN is not used, returns VALUES."
+  (multiple-value-bind (forms declarations) (parse-body body)
+    (with-gensyms (tail loop results)
+      `(block nil
+         (flet ((,results ()
+                  (let (,key ,val)
+                    (declare (ignorable ,key ,val))
+                    (return ,values))))
+           (let* ((,tail ,plist)
+                  (,key (if ,tail
+                            (pop ,tail)
+                            (,results)))
+                 (,val (if ,tail
+                           (pop ,tail)
+                           (malformed-plist ',plist))))
+            (declare (ignorable ,key ,val))
+            ,@declarations
+            (tagbody
+               ,loop
+               ,@forms
+               (setf ,key (if ,tail
+                              (pop ,tail)
+                              (,results))
+                     ,val (if ,tail
+                              (pop ,tail)
+                              (malformed-plist ',plist)))
+               (go ,loop))))))))
+
+(define-modify-macro appendf (&rest lists) append
+  "Modify-macro for APPEND. Appends LISTS to the place designated by the first
+argument.")
+
+(define-modify-macro nconcf (&rest lists) nconc
+  "Modify-macro for NCONC. Concatenates LISTS to place designated by the first
+argument.")
+
+(define-modify-macro unionf (list &rest args) union
+  "Modify-macro for UNION. Saves the union of LIST and the contents of the
+place designated by the first argument to the designated place.")
+
+(define-modify-macro nunionf (list &rest args) nunion
+  "Modify-macro for NUNION. Saves the union of LIST and the contents of the
+place designated by the first argument to the designated place. May modify
+either argument.")
+
+(define-modify-macro reversef () reverse
+  "Modify-macro for REVERSE. Copies and reverses the list stored in the given
+place and saves back the result into the place.")
+
+(define-modify-macro nreversef () nreverse
+  "Modify-macro for NREVERSE. Reverses the list stored in the given place by
+destructively modifying it and saves back the result into the place.")
+
+(defun circular-list (&rest elements)
+  "Creates a circular list of ELEMENTS."
+  (let ((cycle (copy-list elements)))
+    (nconc cycle cycle)))
+
+(defun circular-list-p (object)
+  "Returns true if OBJECT is a circular list, NIL otherwise."
+  (and (listp object)
+       (do ((fast object (cddr fast))
+            (slow (cons (car object) (cdr object)) (cdr slow)))
+           (nil)
+         (unless (and (consp fast) (listp (cdr fast)))
+           (return nil))
+         (when (eq fast slow)
+           (return t)))))
+
+(defun circular-tree-p (object)
+  "Returns true if OBJECT is a circular tree, NIL otherwise."
+  (labels ((circularp (object seen)
+             (and (consp object)
+                  (do ((fast (cons (car object) (cdr object)) (cddr fast))
+                       (slow object (cdr slow)))
+                      (nil)
+                    (when (or (eq fast slow) (member slow seen))
+                      (return-from circular-tree-p t))
+                    (when (or (not (consp fast)) (not (consp (cdr slow))))
+                      (return
+                        (do ((tail object (cdr tail)))
+                            ((not (consp tail))
+                             nil)
+                          (let ((elt (car tail)))
+                            (circularp elt (cons object seen))))))))))
+    (circularp object nil)))
+
+(defun proper-list-p (object)
+  "Returns true if OBJECT is a proper list."
+  (cond ((not object)
+         t)
+        ((consp object)
+         (do ((fast object (cddr fast))
+              (slow (cons (car object) (cdr object)) (cdr slow)))
+             (nil)
+           (unless (and (listp fast) (consp (cdr fast)))
+             (return (and (listp fast) (not (cdr fast)))))
+           (when (eq fast slow)
+             (return nil))))
+        (t
+         nil)))
+
+(deftype proper-list ()
+  "Type designator for proper lists. Implemented as a SATISFIES type, hence
+not recommended for performance intensive use. Main usefullness as a type
+designator of the expected type in a TYPE-ERROR."
+  `(and list (satisfies proper-list-p)))
+
+(defun circular-list-error (list)
+  (error 'type-error
+         :datum list
+         :expected-type '(and list (not circular-list))))
+
+(macrolet ((def (name lambda-list doc step declare ret1 ret2)
+             (assert (member 'list lambda-list))
+             `(defun ,name ,lambda-list
+                ,doc
+                (do ((last list fast)
+                     (fast list (cddr fast))
+                     (slow (cons (car list) (cdr list)) (cdr slow))
+                     ,@(when step (list step)))
+                    (nil)
+                  (declare (dynamic-extent slow) ,@(when declare (list declare))
+                           (ignorable last))
+                  (when (safe-endp fast)
+                    (return ,ret1))
+                  (when (safe-endp (cdr fast))
+                    (return ,ret2))
+                  (when (eq fast slow)
+                    (circular-list-error list))))))
+  (def proper-list-length (list)
+    "Returns length of LIST, signalling an error if it is not a proper list."
+    (n 1 (+ n 2))
+    ;; KLUDGE: Most implementations don't actually support lists with bignum
+    ;; elements -- and this is WAY faster on most implementations then declaring
+    ;; N to be an UNSIGNED-BYTE.
+    (fixnum n)
+    (1- n)
+    n)
+
+  (def lastcar (list)
+      "Returns the last element of LIST. Signals a type-error if LIST is not a
+proper list."
+    nil
+    nil
+    (cadr last)
+    (car fast))
+
+  (def (setf lastcar) (object list)
+      "Sets the last element of LIST. Signals a type-error if LIST is not a proper
+list."
+    nil
+    nil
+    (setf (cadr last) object)
+    (setf (car fast) object)))
+
+(defun make-circular-list (length &key initial-element)
+  "Creates a circular list of LENGTH with the given INITIAL-ELEMENT."
+  (let ((cycle (make-list length :initial-element initial-element)))
+    (nconc cycle cycle)))
+
+(deftype circular-list ()
+  "Type designator for circular lists. Implemented as a SATISFIES type, so not
+recommended for performance intensive use. Main usefullness as the
+expected-type designator of a TYPE-ERROR."
+  `(satisfies circular-list-p))
+
+(defun ensure-car (thing)
+  "If THING is a CONS, its CAR is returned. Otherwise THING is returned."
+  (if (consp thing)
+      (car thing)
+      thing))
+
+(defun ensure-cons (cons)
+  "If CONS is a cons, it is returned. Otherwise returns a fresh cons with CONS
+  in the car, and NIL in the cdr."
+  (if (consp cons)
+      cons
+      (cons cons nil)))
+
+(defun ensure-list (list)
+  "If LIST is a list, it is returned. Otherwise returns the list designated by LIST."
+  (if (listp list)
+      list
+      (list list)))
+
+(defun remove-from-plist (plist &rest keys)
+  "Returns a propery-list with same keys and values as PLIST, except that keys
+in the list designated by KEYS and values corresponding to them are removed.
+The returned property-list may share structure with the PLIST, but PLIST is
+not destructively modified. Keys are compared using EQ."
+  (declare (optimize (speed 3)))
+  ;; FIXME: possible optimization: (remove-from-plist '(:x 0 :a 1 :b 2) :a)
+  ;; could return the tail without consing up a new list.
+  (loop for (key . rest) on plist by #'cddr
+        do (assert rest () "Expected a proper plist, got ~S" plist)
+        unless (member key keys :test #'eq)
+        collect key and collect (first rest)))
+
+(defun delete-from-plist (plist &rest keys)
+  "Just like REMOVE-FROM-PLIST, but this version may destructively modify the
+provided plist."
+  (declare (optimize speed))
+  (loop with head = plist
+        with tail = nil   ; a nil tail means an empty result so far
+        for (key . rest) on plist by #'cddr
+        do (assert rest () "Expected a proper plist, got ~S" plist)
+           (if (member key keys :test #'eq)
+               ;; skip over this pair
+               (let ((next (cdr rest)))
+                 (if tail
+                     (setf (cdr tail) next)
+                     (setf head next)))
+               ;; keep this pair
+               (setf tail rest))
+        finally (return head)))
+
+(define-modify-macro remove-from-plistf (&rest keys) remove-from-plist
+                     "Modify macro for REMOVE-FROM-PLIST.")
+(define-modify-macro delete-from-plistf (&rest keys) delete-from-plist
+                     "Modify macro for DELETE-FROM-PLIST.")
+
+(declaim (inline sans))
+(defun sans (plist &rest keys)
+  "Alias of REMOVE-FROM-PLIST for backward compatibility."
+  (apply #'remove-from-plist plist keys))
+
+(defun mappend (function &rest lists)
+  "Applies FUNCTION to respective element(s) of each LIST, appending all the
+all the result list to a single list. FUNCTION must return a list."
+  (loop for results in (apply #'mapcar function lists)
+        append results))
+
+(defun setp (object &key (test #'eql) (key #'identity))
+  "Returns true if OBJECT is a list that denotes a set, NIL otherwise. A list
+denotes a set if each element of the list is unique under KEY and TEST."
+  (and (listp object)
+       (let (seen)
+         (dolist (elt object t)
+           (let ((key (funcall key elt)))
+             (if (member key seen :test test)
+                 (return nil)
+                 (push key seen)))))))
+
+(defun set-equal (list1 list2 &key (test #'eql) (key nil keyp))
+  "Returns true if every element of LIST1 matches some element of LIST2 and
+every element of LIST2 matches some element of LIST1. Otherwise returns false."
+  (let ((keylist1 (if keyp (mapcar key list1) list1))
+        (keylist2 (if keyp (mapcar key list2) list2)))
+    (and (dolist (elt keylist1 t)
+           (or (member elt keylist2 :test test)
+               (return nil)))
+         (dolist (elt keylist2 t)
+           (or (member elt keylist1 :test test)
+               (return nil))))))
+
+(defun map-product (function list &rest more-lists)
+  "Returns a list containing the results of calling FUNCTION with one argument
+from LIST, and one from each of MORE-LISTS for each combination of arguments.
+In other words, returns the product of LIST and MORE-LISTS using FUNCTION.
+
+Example:
+
+ (map-product 'list '(1 2) '(3 4) '(5 6))
+  => ((1 3 5) (1 3 6) (1 4 5) (1 4 6)
+      (2 3 5) (2 3 6) (2 4 5) (2 4 6))
+"
+  (labels ((%map-product (f lists)
+             (let ((more (cdr lists))
+                   (one (car lists)))
+               (if (not more)
+                   (mapcar f one)
+                   (mappend (lambda (x)
+                              (%map-product (curry f x) more))
+                            one)))))
+    (%map-product (ensure-function function) (cons list more-lists))))
+
+(defun flatten (tree)
+  "Traverses the tree in order, collecting non-null leaves into a list."
+  (let (list)
+    (labels ((traverse (subtree)
+               (when subtree
+                 (if (consp subtree)
+                     (progn
+                       (traverse (car subtree))
+                       (traverse (cdr subtree)))
+                     (push subtree list)))))
+      (traverse tree))
+    (nreverse list)))
diff --git a/deps/alexandria/macros.lisp b/deps/alexandria/macros.lisp
new file mode 100644 (file)
index 0000000..0ac3447
--- /dev/null
@@ -0,0 +1,314 @@
+(in-package :alexandria)
+
+(defmacro with-gensyms (names &body forms)
+  "Binds each variable named by a symbol in NAMES to a unique symbol around
+FORMS. Each of NAMES must either be either a symbol, or of the form:
+
+ (symbol string-designator)
+
+Bare symbols appearing in NAMES are equivalent to:
+
+ (symbol symbol)
+
+The string-designator is used as the argument to GENSYM when constructing the
+unique symbol the named variable will be bound to."
+  `(let ,(mapcar (lambda (name)
+                   (multiple-value-bind (symbol string)
+                       (etypecase name
+                         (symbol
+                          (values name (symbol-name name)))
+                         ((cons symbol (cons string-designator null))
+                          (values (first name) (string (second name)))))
+                     `(,symbol (gensym ,string))))
+                 names)
+     ,@forms))
+
+(defmacro with-unique-names (names &body forms)
+  "Alias for WITH-GENSYMS."
+  `(with-gensyms ,names ,@forms))
+
+(defmacro once-only (specs &body forms)
+  "Evaluates FORMS with symbols specified in SPECS rebound to temporary
+variables, ensuring that each initform is evaluated only once.
+
+Each of SPECS must either be a symbol naming the variable to be rebound, or of
+the form:
+
+  (symbol initform)
+
+Bare symbols in SPECS are equivalent to
+
+  (symbol symbol)
+
+Example:
+
+  (defmacro cons1 (x) (once-only (x) `(cons ,x ,x)))
+  (let ((y 0)) (cons1 (incf y))) => (1 . 1)
+"
+  (let ((gensyms (make-gensym-list (length specs) "ONCE-ONLY"))
+        (names-and-forms (mapcar (lambda (spec)
+                                   (etypecase spec
+                                     (list
+                                      (destructuring-bind (name form) spec
+                                        (cons name form)))
+                                     (symbol
+                                      (cons spec spec))))
+                                 specs)))
+    ;; bind in user-macro
+    `(let ,(mapcar (lambda (g n) (list g `(gensym ,(string (car n)))))
+                   gensyms names-and-forms)
+       ;; bind in final expansion
+       `(let (,,@(mapcar (lambda (g n)
+                           ``(,,g ,,(cdr n)))
+                         gensyms names-and-forms))
+          ;; bind in user-macro
+          ,(let ,(mapcar (lambda (n g) (list (car n) g))
+                         names-and-forms gensyms)
+             ,@forms)))))
+
+(defun parse-body (body &key documentation whole)
+  "Parses BODY into (values remaining-forms declarations doc-string).
+Documentation strings are recognized only if DOCUMENTATION is true.
+Syntax errors in body are signalled and WHOLE is used in the signal
+arguments when given."
+  (let ((doc nil)
+        (decls nil)
+        (current nil))
+    (tagbody
+     :declarations
+       (setf current (car body))
+       (when (and documentation (stringp current) (cdr body))
+         (if doc
+             (error "Too many documentation strings in ~S." (or whole body))
+             (setf doc (pop body)))
+         (go :declarations))
+       (when (and (listp current) (eql (first current) 'declare))
+         (push (pop body) decls)
+         (go :declarations)))
+    (values body (nreverse decls) doc)))
+
+(defun parse-ordinary-lambda-list (lambda-list &key (normalize t)
+                                   allow-specializers
+                                   (normalize-optional normalize)
+                                   (normalize-keyword normalize)
+                                   (normalize-auxilary normalize))
+  "Parses an ordinary lambda-list, returning as multiple values:
+
+1. Required parameters.
+
+2. Optional parameter specifications, normalized into form:
+
+   (name init suppliedp)
+
+3. Name of the rest parameter, or NIL.
+
+4. Keyword parameter specifications, normalized into form:
+
+   ((keyword-name name) init suppliedp)
+
+5. Boolean indicating &ALLOW-OTHER-KEYS presence.
+
+6. &AUX parameter specifications, normalized into form
+
+   (name init).
+
+7. Existence of &KEY in the lambda-list.
+
+Signals a PROGRAM-ERROR is the lambda-list is malformed."
+  (let ((state :required)
+        (allow-other-keys nil)
+        (auxp nil)
+        (required nil)
+        (optional nil)
+        (rest nil)
+        (keys nil)
+        (keyp nil)
+        (aux nil))
+    (labels ((fail (elt)
+               (simple-program-error "Misplaced ~S in ordinary lambda-list:~%  ~S"
+                                     elt lambda-list))
+             (check-variable (elt what &optional (allow-specializers allow-specializers))
+               (unless (and (or (symbolp elt)
+                                (and allow-specializers
+                                     (consp elt) (= 2 (length elt)) (symbolp (first elt))))
+                            (not (constantp elt)))
+                 (simple-program-error "Invalid ~A ~S in ordinary lambda-list:~%  ~S"
+                                       what elt lambda-list)))
+             (check-spec (spec what)
+               (destructuring-bind (init suppliedp) spec
+                 (declare (ignore init))
+                 (check-variable suppliedp what nil))))
+      (dolist (elt lambda-list)
+        (case elt
+          (&optional
+           (if (eq state :required)
+               (setf state elt)
+               (fail elt)))
+          (&rest
+           (if (member state '(:required &optional))
+               (setf state elt)
+               (fail elt)))
+          (&key
+           (if (member state '(:required &optional :after-rest))
+               (setf state elt)
+               (fail elt))
+           (setf keyp t))
+          (&allow-other-keys
+           (if (eq state '&key)
+               (setf allow-other-keys t
+                     state elt)
+               (fail elt)))
+          (&aux
+           (cond ((eq state '&rest)
+                  (fail elt))
+                 (auxp
+                  (simple-program-error "Multiple ~S in ordinary lambda-list:~%  ~S"
+                                        elt lambda-list))
+                 (t
+                  (setf auxp t
+                        state elt))
+                 ))
+          (otherwise
+           (when (member elt '#.(set-difference lambda-list-keywords
+                                                '(&optional &rest &key &allow-other-keys &aux)))
+             (simple-program-error
+              "Bad lambda-list keyword ~S in ordinary lambda-list:~%  ~S"
+              elt lambda-list))
+           (case state
+             (:required
+              (check-variable elt "required parameter")
+              (push elt required))
+             (&optional
+              (cond ((consp elt)
+                     (destructuring-bind (name &rest tail) elt
+                       (check-variable name "optional parameter")
+                       (cond ((cdr tail)
+                              (check-spec tail "optional-supplied-p parameter"))
+                             ((and normalize-optional tail)
+                              (setf elt (append elt '(nil))))
+                             (normalize-optional
+                              (setf elt (append elt '(nil nil)))))))
+                    (t
+                     (check-variable elt "optional parameter")
+                     (when normalize-optional
+                       (setf elt (cons elt '(nil nil))))))
+              (push (ensure-list elt) optional))
+             (&rest
+              (check-variable elt "rest parameter")
+              (setf rest elt
+                    state :after-rest))
+             (&key
+              (cond ((consp elt)
+                     (destructuring-bind (var-or-kv &rest tail) elt
+                       (cond ((consp var-or-kv)
+                              (destructuring-bind (keyword var) var-or-kv
+                                (unless (symbolp keyword)
+                                  (simple-program-error "Invalid keyword name ~S in ordinary ~
+                                                         lambda-list:~%  ~S"
+                                                        keyword lambda-list))
+                                (check-variable var "keyword parameter")))
+                             (t
+                              (check-variable var-or-kv "keyword parameter")
+                              (when normalize-keyword
+                                (setf var-or-kv (list (make-keyword var-or-kv) var-or-kv)))))
+                       (cond ((cdr tail)
+                              (check-spec tail "keyword-supplied-p parameter"))
+                             ((and normalize-keyword tail)
+                              (setf tail (append tail '(nil))))
+                             (normalize-keyword
+                              (setf tail '(nil nil))))
+                       (setf elt (cons var-or-kv tail))))
+                    (t
+                     (check-variable elt "keyword parameter")
+                     (setf elt (if normalize-keyword
+                                   (list (list (make-keyword elt) elt) nil nil)
+                                   elt))))
+              (push elt keys))
+             (&aux
+              (if (consp elt)
+                  (destructuring-bind (var &optional init) elt
+                    (declare (ignore init))
+                    (check-variable var "&aux parameter"))
+                  (progn
+                    (check-variable elt "&aux parameter")
+                    (setf elt (list* elt (when normalize-auxilary
+                                           '(nil))))))
+              (push elt aux))
+             (t
+              (simple-program-error "Invalid ordinary lambda-list:~%  ~S" lambda-list)))))))
+    (values (nreverse required) (nreverse optional) rest (nreverse keys)
+            allow-other-keys (nreverse aux) keyp)))
+
+;;;; DESTRUCTURING-*CASE
+
+(defun expand-destructuring-case (key clauses case)
+  (once-only (key)
+    `(if (typep ,key 'cons)
+         (,case (car ,key)
+           ,@(mapcar (lambda (clause)
+                       (destructuring-bind ((keys . lambda-list) &body body) clause
+                         `(,keys
+                           (destructuring-bind ,lambda-list (cdr ,key)
+                             ,@body))))
+                     clauses))
+         (error "Invalid key to DESTRUCTURING-~S: ~S" ',case ,key))))
+
+(defmacro destructuring-case (keyform &body clauses)
+  "DESTRUCTURING-CASE, -CCASE, and -ECASE are a combination of CASE and DESTRUCTURING-BIND.
+KEYFORM must evaluate to a CONS.
+
+Clauses are of the form:
+
+  ((CASE-KEYS . DESTRUCTURING-LAMBDA-LIST) FORM*)
+
+The clause whose CASE-KEYS matches CAR of KEY, as if by CASE, CCASE, or ECASE,
+is selected, and FORMs are then executed with CDR of KEY is destructured and
+bound by the DESTRUCTURING-LAMBDA-LIST.
+
+Example:
+
+ (defun dcase (x)
+   (destructuring-case x
+     ((:foo a b)
+      (format nil \"foo: ~S, ~S\" a b))
+     ((:bar &key a b)
+      (format nil \"bar, ~S, ~S\" a b))
+     (((:alt1 :alt2) a)
+      (format nil \"alt: ~S\" a))
+     ((t &rest rest)
+      (format nil \"unknown: ~S\" rest))))
+
+  (dcase (list :foo 1 2))        ; => \"foo: 1, 2\"
+  (dcase (list :bar :a 1 :b 2))  ; => \"bar: 1, 2\"
+  (dcase (list :alt1 1))         ; => \"alt: 1\"
+  (dcase (list :alt2 2))         ; => \"alt: 2\"
+  (dcase (list :quux 1 2 3))     ; => \"unknown: 1, 2, 3\"
+
+ (defun decase (x)
+   (destructuring-case x
+     ((:foo a b)
+      (format nil \"foo: ~S, ~S\" a b))
+     ((:bar &key a b)
+      (format nil \"bar, ~S, ~S\" a b))
+     (((:alt1 :alt2) a)
+      (format nil \"alt: ~S\" a))))
+
+  (decase (list :foo 1 2))        ; => \"foo: 1, 2\"
+  (decase (list :bar :a 1 :b 2))  ; => \"bar: 1, 2\"
+  (decase (list :alt1 1))         ; => \"alt: 1\"
+  (decase (list :alt2 2))         ; => \"alt: 2\"
+  (decase (list :quux 1 2 3))     ; =| error
+"
+  (expand-destructuring-case keyform clauses 'case))
+
+(defmacro destructuring-ccase (keyform &body clauses)
+  (expand-destructuring-case keyform clauses 'ccase))
+
+(defmacro destructuring-ecase (keyform &body clauses)
+  (expand-destructuring-case keyform clauses 'ecase))
+
+(dolist (name '(destructuring-ccase destructuring-ecase))
+  (setf (documentation name 'function) (documentation 'destructuring-case 'function)))
+
+
+
diff --git a/deps/alexandria/numbers.lisp b/deps/alexandria/numbers.lisp
new file mode 100644 (file)
index 0000000..ec40683
--- /dev/null
@@ -0,0 +1,260 @@
+(in-package :alexandria)
+
+(declaim (inline clamp))
+(defun clamp (number min max)
+  "Clamps the NUMBER into [min, max] range. Returns MIN if NUMBER is lesser then
+MIN and MAX if NUMBER is greater then MAX, otherwise returns NUMBER."
+  (if (< number min)
+      min
+      (if (> number max)
+          max
+          number)))
+
+(defun gaussian-random (&optional min max)
+  "Returns two gaussian random double floats as the primary and secondary value,
+optionally constrained by MIN and MAX. Gaussian random numbers form a standard
+normal distribution around 0.0d0.
+
+Sufficiently positive MIN or negative MAX will cause the algorithm used to
+take a very long time. If MIN is positive it should be close to zero, and
+similarly if MAX is negative it should be close to zero."
+  (macrolet
+      ((valid (x)
+         `(<= (or min ,x) ,x (or max ,x)) ))
+    (labels
+        ((gauss ()
+           (loop
+                 for x1 = (- (random 2.0d0) 1.0d0)
+                 for x2 = (- (random 2.0d0) 1.0d0)
+                 for w = (+ (expt x1 2) (expt x2 2))
+                 when (< w 1.0d0)
+                 do (let ((v (sqrt (/ (* -2.0d0 (log w)) w))))
+                      (return (values (* x1 v) (* x2 v))))))
+         (guard (x)
+           (unless (valid x)
+             (tagbody
+              :retry
+                (multiple-value-bind (x1 x2) (gauss)
+                  (when (valid x1)
+                    (setf x x1)
+                    (go :done))
+                  (when (valid x2)
+                    (setf x x2)
+                    (go :done))
+                  (go :retry))
+              :done))
+           x))
+      (multiple-value-bind
+            (g1 g2) (gauss)
+        (values (guard g1) (guard g2))))))
+
+(declaim (inline iota))
+(defun iota (n &key (start 0) (step 1))
+  "Return a list of n numbers, starting from START (with numeric contagion
+from STEP applied), each consequtive number being the sum of the previous one
+and STEP. START defaults to 0 and STEP to 1.
+
+Examples:
+
+  (iota 4)                      => (0 1 2 3)
+  (iota 3 :start 1 :step 1.0)   => (1.0 2.0 3.0)
+  (iota 3 :start -1 :step -1/2) => (-1 -3/2 -2)
+"
+  (declare (type (integer 0) n) (number start step))
+  (loop repeat n
+        ;; KLUDGE: get numeric contagion right for the first element too
+        for i = (+ (- (+ start step) step)) then (+ i step)
+        collect i))
+
+(declaim (inline map-iota))
+(defun map-iota (function n &key (start 0) (step 1))
+  "Calls FUNCTION with N numbers, starting from START (with numeric contagion
+from STEP applied), each consequtive number being the sum of the previous one
+and STEP. START defaults to 0 and STEP to 1. Returns N.
+
+Examples:
+
+  (map-iota #'print 3 :start 1 :step 1.0) => 3
+    ;;; 1.0
+    ;;; 2.0
+    ;;; 3.0
+"
+  (declare (type (integer 0) n) (number start step))
+  (loop repeat n
+        ;; KLUDGE: get numeric contagion right for the first element too
+        for i = (+ start (- step step)) then (+ i step)
+        do (funcall function i))
+  n)
+
+(declaim (inline lerp))
+(defun lerp (v a b)
+  "Returns the result of linear interpolation between A and B, using the
+interpolation coefficient V."
+  ;; The correct version is numerically stable, at the expense of an
+  ;; extra multiply. See (lerp 0.1 4 25) with (+ a (* v (- b a))). The
+  ;; unstable version can often be converted to a fast instruction on
+  ;; a lot of machines, though this is machine/implementation
+  ;; specific. As alexandria is more about correct code, than
+  ;; efficiency, and we're only talking about a single extra multiply,
+  ;; many would prefer the stable version
+  (+ (* (- 1.0 v) a) (* v b)))
+
+(declaim (inline mean))
+(defun mean (sample)
+  "Returns the mean of SAMPLE. SAMPLE must be a sequence of numbers."
+  (/ (reduce #'+ sample) (length sample)))
+
+(declaim (inline median))
+(defun median (sample)
+  "Returns median of SAMPLE. SAMPLE must be a sequence of real numbers."
+  (let* ((vector (sort (copy-sequence 'vector sample) #'<))
+         (length (length vector))
+         (middle (truncate length 2)))
+    (if (oddp length)
+        (aref vector middle)
+        (/ (+ (aref vector middle) (aref vector (1- middle))) 2))))
+
+(declaim (inline variance))
+(defun variance (sample &key (biased t))
+  "Variance of SAMPLE. Returns the biased variance if BIASED is true (the default),
+and the unbiased estimator of variance if BIASED is false. SAMPLE must be a
+sequence of numbers."
+  (let ((mean (mean sample)))
+    (/ (reduce (lambda (a b)
+                 (+ a (expt (- b mean) 2)))
+               sample
+               :initial-value 0)
+       (- (length sample) (if biased 0 1)))))
+
+(declaim (inline standard-deviation))
+(defun standard-deviation (sample &key (biased t))
+  "Standard deviation of SAMPLE. Returns the biased standard deviation if
+BIASED is true (the default), and the square root of the unbiased estimator
+for variance if BIASED is false (which is not the same as the unbiased
+estimator for standard deviation). SAMPLE must be a sequence of numbers."
+  (sqrt (variance sample :biased biased)))
+
+(define-modify-macro maxf (&rest numbers) max
+  "Modify-macro for MAX. Sets place designated by the first argument to the
+maximum of its original value and NUMBERS.")
+
+(define-modify-macro minf (&rest numbers) min
+  "Modify-macro for MIN. Sets place designated by the first argument to the
+minimum of its original value and NUMBERS.")
+
+;;;; Factorial
+
+;;; KLUDGE: This is really dependant on the numbers in question: for
+;;; small numbers this is larger, and vice versa. Ideally instead of a
+;;; constant we would have RANGE-FAST-TO-MULTIPLY-DIRECTLY-P.
+(defconstant +factorial-bisection-range-limit+ 8)
+
+;;; KLUDGE: This is really platform dependant: ideally we would use
+;;; (load-time-value (find-good-direct-multiplication-limit)) instead.
+(defconstant +factorial-direct-multiplication-limit+ 13)
+
+(defun %multiply-range (i j)
+  ;; We use a a bit of cleverness here:
+  ;;
+  ;; 1. For large factorials we bisect in order to avoid expensive bignum
+  ;;    multiplications: 1 x 2 x 3 x ... runs into bignums pretty soon,
+  ;;    and once it does that all further multiplications will be with bignums.
+  ;;
+  ;;    By instead doing the multiplication in a tree like
+  ;;       ((1 x 2) x (3 x 4)) x ((5 x 6) x (7 x 8))
+  ;;    we manage to get less bignums.
+  ;;
+  ;; 2. Division isn't exactly free either, however, so we don't bisect
+  ;;    all the way down, but multiply ranges of integers close to each
+  ;;    other directly.
+  ;;
+  ;; For even better results it should be possible to use prime
+  ;; factorization magic, but Nikodemus ran out of steam.
+  ;;
+  ;; KLUDGE: We support factorials of bignums, but it seems quite
+  ;; unlikely anyone would ever be able to use them on a modern lisp,
+  ;; since the resulting numbers are unlikely to fit in memory... but
+  ;; it would be extremely unelegant to define FACTORIAL only on
+  ;; fixnums, _and_ on lisps with 16 bit fixnums this can actually be
+  ;; needed.
+  (labels ((bisect (j k)
+             (declare (type (integer 1 #.most-positive-fixnum) j k))
+             (if (< (- k j) +factorial-bisection-range-limit+)
+                 (multiply-range j k)
+                 (let ((middle (+ j (truncate (- k j) 2))))
+                   (* (bisect j middle)
+                      (bisect (+ middle 1) k)))))
+           (bisect-big (j k)
+             (declare (type (integer 1) j k))
+             (if (= j k)
+                 j
+                 (let ((middle (+ j (truncate (- k j) 2))))
+                   (* (if (<= middle most-positive-fixnum)
+                          (bisect j middle)
+                          (bisect-big j middle))
+                      (bisect-big (+ middle 1) k)))))
+           (multiply-range (j k)
+             (declare (type (integer 1 #.most-positive-fixnum) j k))
+             (do ((f k (* f m))
+                  (m (1- k) (1- m)))
+                 ((< m j) f)
+               (declare (type (integer 0 (#.most-positive-fixnum)) m)
+                        (type unsigned-byte f)))))
+    (if (and (typep i 'fixnum) (typep j 'fixnum))
+        (bisect i j)
+        (bisect-big i j))))
+
+(declaim (inline factorial))
+(defun %factorial (n)
+  (if (< n 2)
+      1
+      (%multiply-range 1 n)))
+
+(defun factorial (n)
+  "Factorial of non-negative integer N."
+  (check-type n (integer 0))
+  (%factorial n))
+
+;;;; Combinatorics
+
+(defun binomial-coefficient (n k)
+  "Binomial coefficient of N and K, also expressed as N choose K. This is the
+number of K element combinations given N choises. N must be equal to or
+greater then K."
+  (check-type n (integer 0))
+  (check-type k (integer 0))
+  (assert (>= n k))
+  (if (or (zerop k) (= n k))
+      1
+      (let ((n-k (- n k)))
+        ;; Swaps K and N-K if K < N-K because the algorithm
+        ;; below is faster for bigger K and smaller N-K
+        (when (< k n-k)
+          (rotatef k n-k))
+        (if (= 1 n-k)
+            n
+            ;; General case, avoid computing the 1x...xK twice:
+            ;;
+            ;;    N!           1x...xN          (K+1)x...xN
+            ;; --------  =  ---------------- =  ------------, N>1
+            ;; K!(N-K)!     1x...xK x (N-K)!       (N-K)!
+            (/ (%multiply-range (+ k 1) n)
+               (%factorial n-k))))))
+
+(defun subfactorial (n)
+  "Subfactorial of the non-negative integer N."
+  (check-type n (integer 0))
+  (if (zerop n)
+      1
+      (do ((x 1 (1+ x))
+           (a 0 (* x (+ a b)))
+           (b 1 a))
+          ((= n x) a))))
+
+(defun count-permutations (n &optional (k n))
+  "Number of K element permutations for a sequence of N objects.
+K defaults to N"
+  (check-type n (integer 0))
+  (check-type k (integer 0))
+  (assert (>= n k))
+  (%multiply-range (1+ (- n k)) n))
diff --git a/deps/alexandria/package.lisp b/deps/alexandria/package.lisp
new file mode 100644 (file)
index 0000000..180e7e9
--- /dev/null
@@ -0,0 +1,244 @@
+(defpackage :alexandria.0.dev
+  (:nicknames :alexandria)
+  (:use :cl)
+  #+sb-package-locks
+  (:lock t)
+  (:export
+   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+   ;; BLESSED
+   ;;
+   ;; Binding constructs
+   #:if-let
+   #:when-let
+   #:when-let*
+   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+   ;; REVIEW IN PROGRESS
+   ;;
+   ;; Control flow
+   ;;
+   ;; -- no clear consensus yet --
+   #:cswitch
+   #:eswitch
+   #:switch
+   ;; -- problem free? --
+   #:multiple-value-prog2
+   #:nth-value-or
+   #:whichever
+   #:xor
+   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+   ;; REVIEW PENDING
+   ;;
+   ;; Definitions
+   #:define-constant
+   ;; Hash tables
+   #:alist-hash-table
+   #:copy-hash-table
+   #:ensure-gethash
+   #:hash-table-alist
+   #:hash-table-keys
+   #:hash-table-plist
+   #:hash-table-values
+   #:maphash-keys
+   #:maphash-values
+   #:plist-hash-table
+   ;; Functions
+   #:compose
+   #:conjoin
+   #:curry
+   #:disjoin
+   #:ensure-function
+   #:ensure-functionf
+   #:multiple-value-compose
+   #:named-lambda
+   #:rcurry
+   ;; Lists
+   #:alist-plist
+   #:appendf
+   #:nconcf
+   #:reversef
+   #:nreversef
+   #:circular-list
+   #:circular-list-p
+   #:circular-tree-p
+   #:doplist
+   #:ensure-car
+   #:ensure-cons
+   #:ensure-list
+   #:flatten
+   #:lastcar
+   #:make-circular-list
+   #:map-product
+   #:mappend
+   #:nunionf
+   #:plist-alist
+   #:proper-list
+   #:proper-list-length
+   #:proper-list-p
+   #:remove-from-plist
+   #:remove-from-plistf
+   #:delete-from-plist
+   #:delete-from-plistf
+   #:set-equal
+   #:setp
+   #:unionf
+   ;; Numbers
+   #:binomial-coefficient
+   #:clamp
+   #:count-permutations
+   #:factorial
+   #:gaussian-random
+   #:iota
+   #:lerp
+   #:map-iota
+   #:maxf
+   #:mean
+   #:median
+   #:minf
+   #:standard-deviation
+   #:subfactorial
+   #:variance
+   ;; Arrays
+   #:array-index
+   #:array-length
+   #:copy-array
+   ;; Sequences
+   #:copy-sequence
+   #:deletef
+   #:emptyp
+   #:ends-with
+   #:ends-with-subseq
+   #:extremum
+   #:first-elt
+   #:last-elt
+   #:length=
+   #:map-combinations
+   #:map-derangements
+   #:map-permutations
+   #:proper-sequence
+   #:random-elt
+   #:removef
+   #:rotate
+   #:sequence-of-length-p
+   #:shuffle
+   #:starts-with
+   #:starts-with-subseq
+   ;; Macros
+   #:once-only
+   #:parse-body
+   #:parse-ordinary-lambda-list
+   #:with-gensyms
+   #:with-unique-names
+   ;; Symbols
+   #:ensure-symbol
+   #:format-symbol
+   #:make-gensym
+   #:make-gensym-list
+   #:make-keyword
+   ;; Strings
+   #:string-designator
+   ;; Types
+   #:negative-double-float
+   #:negative-fixnum-p
+   #:negative-float
+   #:negative-float-p
+   #:negative-long-float
+   #:negative-long-float-p
+   #:negative-rational
+   #:negative-rational-p
+   #:negative-real
+   #:negative-single-float-p
+   #:non-negative-double-float
+   #:non-negative-double-float-p
+   #:non-negative-fixnum
+   #:non-negative-fixnum-p
+   #:non-negative-float
+   #:non-negative-float-p
+   #:non-negative-integer-p
+   #:non-negative-long-float
+   #:non-negative-rational
+   #:non-negative-real-p
+   #:non-negative-short-float-p
+   #:non-negative-single-float
+   #:non-negative-single-float-p
+   #:non-positive-double-float
+   #:non-positive-double-float-p
+   #:non-positive-fixnum
+   #:non-positive-fixnum-p
+   #:non-positive-float
+   #:non-positive-float-p
+   #:non-positive-integer
+   #:non-positive-rational
+   #:non-positive-real
+   #:non-positive-real-p
+   #:non-positive-short-float
+   #:non-positive-short-float-p
+   #:non-positive-single-float-p
+   #:ordinary-lambda-list-keywords
+   #:positive-double-float
+   #:positive-double-float-p
+   #:positive-fixnum
+   #:positive-fixnum-p
+   #:positive-float
+   #:positive-float-p
+   #:positive-integer
+   #:positive-rational
+   #:positive-real
+   #:positive-real-p
+   #:positive-short-float
+   #:positive-short-float-p
+   #:positive-single-float
+   #:positive-single-float-p
+   #:coercef
+   #:negative-double-float-p
+   #:negative-fixnum
+   #:negative-integer
+   #:negative-integer-p
+   #:negative-real-p
+   #:negative-short-float
+   #:negative-short-float-p
+   #:negative-single-float
+   #:non-negative-integer
+   #:non-negative-long-float-p
+   #:non-negative-rational-p
+   #:non-negative-real
+   #:non-negative-short-float
+   #:non-positive-integer-p
+   #:non-positive-long-float
+   #:non-positive-long-float-p
+   #:non-positive-rational-p
+   #:non-positive-single-float
+   #:of-type
+   #:positive-integer-p
+   #:positive-long-float
+   #:positive-long-float-p
+   #:positive-rational-p
+   #:type=
+   ;; Conditions
+   #:required-argument
+   #:ignore-some-conditions
+   #:simple-style-warning
+   #:simple-reader-error
+   #:simple-parse-error
+   #:simple-program-error
+   #:unwind-protect-case
+   ;; Features
+   #:featurep
+   ;; io
+   #:with-input-from-file
+   #:with-output-to-file
+   #:read-stream-content-into-string
+   #:read-file-into-string
+   #:write-string-into-file
+   #:read-stream-content-into-byte-vector
+   #:read-file-into-byte-vector
+   #:write-byte-vector-into-file
+   #:copy-stream
+   #:copy-file
+   ;; new additions collected at the end (subject to removal or further changes)
+   #:symbolicate
+   #:assoc-value
+   #:rassoc-value
+   #:destructuring-case
+   #:destructuring-ccase
+   #:destructuring-ecase
+   ))
diff --git a/deps/alexandria/sequences.lisp b/deps/alexandria/sequences.lisp
new file mode 100644 (file)
index 0000000..ef594dd
--- /dev/null
@@ -0,0 +1,555 @@
+(in-package :alexandria)
+
+;; Make these inlinable by declaiming them INLINE here and some of them
+;; NOTINLINE at the end of the file. Exclude functions that have a compiler
+;; macro, because NOTINLINE is required to prevent compiler-macro expansion.
+(declaim (inline copy-sequence sequence-of-length-p))
+
+(defun sequence-of-length-p (sequence length)
+  "Return true if SEQUENCE is a sequence of length LENGTH. Signals an error if
+SEQUENCE is not a sequence. Returns FALSE for circular lists."
+  (declare (type array-index length)
+           (inline length)
+           (optimize speed))
+  (etypecase sequence
+    (null
+     (zerop length))
+    (cons
+     (let ((n (1- length)))
+       (unless (minusp n)
+         (let ((tail (nthcdr n sequence)))
+           (and tail
+                (null (cdr tail)))))))
+    (vector
+     (= length (length sequence)))
+    (sequence
+     (= length (length sequence)))))
+
+(defun rotate-tail-to-head (sequence n)
+  (declare (type (integer 1) n))
+  (if (listp sequence)
+      (let ((m (mod n (proper-list-length sequence))))
+        (if (null (cdr sequence))
+            sequence
+            (let* ((tail (last sequence (+ m 1)))
+                   (last (cdr tail)))
+              (setf (cdr tail) nil)
+              (nconc last sequence))))
+      (let* ((len (length sequence))
+             (m (mod n len))
+             (tail (subseq sequence (- len m))))
+        (replace sequence sequence :start1 m :start2 0)
+        (replace sequence tail)
+        sequence)))
+
+(defun rotate-head-to-tail (sequence n)
+  (declare (type (integer 1) n))
+  (if (listp sequence)
+      (let ((m (mod (1- n) (proper-list-length sequence))))
+        (if (null (cdr sequence))
+            sequence
+            (let* ((headtail (nthcdr m sequence))
+                   (tail (cdr headtail)))
+              (setf (cdr headtail) nil)
+              (nconc tail sequence))))
+      (let* ((len (length sequence))
+             (m (mod n len))
+             (head (subseq sequence 0 m)))
+        (replace sequence sequence :start1 0 :start2 m)
+        (replace sequence head :start1 (- len m))
+        sequence)))
+
+(defun rotate (sequence &optional (n 1))
+  "Returns a sequence of the same type as SEQUENCE, with the elements of
+SEQUENCE rotated by N: N elements are moved from the end of the sequence to
+the front if N is positive, and -N elements moved from the front to the end if
+N is negative. SEQUENCE must be a proper sequence. N must be an integer,
+defaulting to 1.
+
+If absolute value of N is greater then the length of the sequence, the results
+are identical to calling ROTATE with
+
+  (* (signum n) (mod n (length sequence))).
+
+Note: the original sequence may be destructively altered, and result sequence may
+share structure with it."
+  (if (plusp n)
+      (rotate-tail-to-head sequence n)
+      (if (minusp n)
+          (rotate-head-to-tail sequence (- n))
+          sequence)))
+
+(defun shuffle (sequence &key (start 0) end)
+  "Returns a random permutation of SEQUENCE bounded by START and END.
+Original sequece may be destructively modified, and share storage with
+the original one. Signals an error if SEQUENCE is not a proper
+sequence."
+  (declare (type fixnum start)
+           (type (or fixnum null) end))
+  (etypecase sequence
+    (list
+     (let* ((end (or end (proper-list-length sequence)))
+            (n (- end start)))
+       (do ((tail (nthcdr start sequence) (cdr tail)))
+           ((zerop n))
+         (rotatef (car tail) (car (nthcdr (random n) tail)))
+         (decf n))))
+    (vector
+     (let ((end (or end (length sequence))))
+       (loop for i from start below end
+             do (rotatef (aref sequence i)
+                         (aref sequence (+ i (random (- end i))))))))
+    (sequence
+     (let ((end (or end (length sequence))))
+       (loop for i from (- end 1) downto start
+             do (rotatef (elt sequence i)
+                         (elt sequence (+ i (random (- end i)))))))))
+  sequence)
+
+(defun random-elt (sequence &key (start 0) end)
+  "Returns a random element from SEQUENCE bounded by START and END. Signals an
+error if the SEQUENCE is not a proper non-empty sequence, or if END and START
+are not proper bounding index designators for SEQUENCE."
+  (declare (sequence sequence) (fixnum start) (type (or fixnum null) end))
+  (let* ((size (if (listp sequence)
+                   (proper-list-length sequence)
+                   (length sequence)))
+         (end2 (or end size)))
+    (cond ((zerop size)
+           (error 'type-error
+                  :datum sequence
+                  :expected-type `(and sequence (not (satisfies emptyp)))))
+          ((not (and (<= 0 start) (< start end2) (<= end2 size)))
+           (error 'simple-type-error
+                  :datum (cons start end)
+                  :expected-type `(cons (integer 0 (,end2))
+                                        (or null (integer (,start) ,size)))
+                  :format-control "~@<~S and ~S are not valid bounding index designators for ~
+                                   a sequence of length ~S.~:@>"
+                  :format-arguments (list start end size)))
+          (t
+           (let ((index (+ start (random (- end2 start)))))
+             (elt sequence index))))))
+
+(declaim (inline remove/swapped-arguments))
+(defun remove/swapped-arguments (sequence item &rest keyword-arguments)
+  (apply #'remove item sequence keyword-arguments))
+
+(define-modify-macro removef (item &rest remove-keywords)
+  remove/swapped-arguments
+  "Modify-macro for REMOVE. Sets place designated by the first argument to
+the result of calling REMOVE with ITEM, place, and the REMOVE-KEYWORDS.")
+
+(declaim (inline delete/swapped-arguments))
+(defun delete/swapped-arguments (sequence item &rest keyword-arguments)
+  (apply #'delete item sequence keyword-arguments))
+
+(define-modify-macro deletef (item &rest remove-keywords)
+  delete/swapped-arguments
+  "Modify-macro for DELETE. Sets place designated by the first argument to
+the result of calling DELETE with ITEM, place, and the REMOVE-KEYWORDS.")
+
+(deftype proper-sequence ()
+  "Type designator for proper sequences, that is proper lists and sequences
+that are not lists."
+  `(or proper-list
+       (and (not list) sequence)))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (when (and (find-package '#:sequence)
+             (find-symbol (string '#:emptyp) '#:sequence))
+    (pushnew 'sequence-emptyp *features*)))
+
+#-alexandria::sequence-emptyp
+(defun emptyp (sequence)
+  "Returns true if SEQUENCE is an empty sequence. Signals an error if SEQUENCE
+is not a sequence."
+  (etypecase sequence
+    (list (null sequence))
+    (sequence (zerop (length sequence)))))
+
+#+alexandria::sequence-emptyp
+(declaim (ftype (function (sequence) (values boolean &optional)) emptyp))
+#+alexandria::sequence-emptyp
+(setf (symbol-function 'emptyp) (symbol-function 'sequence:emptyp))
+#+alexandria::sequence-emptyp
+(define-compiler-macro emptyp (sequence)
+  `(sequence:emptyp ,sequence))
+
+(defun length= (&rest sequences)
+  "Takes any number of sequences or integers in any order. Returns true iff
+the length of all the sequences and the integers are equal. Hint: there's a
+compiler macro that expands into more efficient code if the first argument
+is a literal integer."
+  (declare (dynamic-extent sequences)
+           (inline sequence-of-length-p)
+           (optimize speed))
+  (unless (cdr sequences)
+    (error "You must call LENGTH= with at least two arguments"))
+  ;; There's room for optimization here: multiple list arguments could be
+  ;; traversed in parallel.
+  (let* ((first (pop sequences))
+         (current (if (integerp first)
+                      first
+                      (length first))))
+    (declare (type array-index current))
+    (dolist (el sequences)
+      (if (integerp el)
+          (unless (= el current)
+            (return-from length= nil))
+          (unless (sequence-of-length-p el current)
+            (return-from length= nil)))))
+  t)
+
+(define-compiler-macro length= (&whole form length &rest sequences)
+  (cond
+    ((zerop (length sequences))
+     form)
+    (t
+     (let ((optimizedp (integerp length)))
+       (with-unique-names (tmp current)
+         (declare (ignorable current))
+         `(locally
+              (declare (inline sequence-of-length-p))
+            (let ((,tmp)
+                  ,@(unless optimizedp
+                     `((,current ,length))))
+              ,@(unless optimizedp
+                  `((unless (integerp ,current)
+                      (setf ,current (length ,current)))))
+              (and
+               ,@(loop
+                    :for sequence :in sequences
+                    :collect `(progn
+                                (setf ,tmp ,sequence)
+                                (if (integerp ,tmp)
+                                    (= ,tmp ,(if optimizedp
+                                                 length
+                                                 current))
+                                    (sequence-of-length-p ,tmp ,(if optimizedp
+                                                                    length
+                                                                    current)))))))))))))
+
+(defun copy-sequence (type sequence)
+  "Returns a fresh sequence of TYPE, which has the same elements as
+SEQUENCE."
+  (if (typep sequence type)
+      (copy-seq sequence)
+      (coerce sequence type)))
+
+(defun first-elt (sequence)
+  "Returns the first element of SEQUENCE. Signals a type-error if SEQUENCE is
+not a sequence, or is an empty sequence."
+  ;; Can't just directly use ELT, as it is not guaranteed to signal the
+  ;; type-error.
+  (cond  ((consp sequence)
+          (car sequence))
+         ((and (typep sequence 'sequence) (not (emptyp sequence)))
+          (elt sequence 0))
+         (t
+          (error 'type-error
+                 :datum sequence
+                 :expected-type '(and sequence (not (satisfies emptyp)))))))
+
+(defun (setf first-elt) (object sequence)
+  "Sets the first element of SEQUENCE. Signals a type-error if SEQUENCE is
+not a sequence, is an empty sequence, or if OBJECT cannot be stored in SEQUENCE."
+  ;; Can't just directly use ELT, as it is not guaranteed to signal the
+  ;; type-error.
+  (cond ((consp sequence)
+         (setf (car sequence) object))
+        ((and (typep sequence 'sequence) (not (emptyp sequence)))
+         (setf (elt sequence 0) object))
+        (t
+         (error 'type-error
+                :datum sequence
+                :expected-type '(and sequence (not (satisfies emptyp)))))))
+
+(defun last-elt (sequence)
+  "Returns the last element of SEQUENCE. Signals a type-error if SEQUENCE is
+not a proper sequence, or is an empty sequence."
+  ;; Can't just directly use ELT, as it is not guaranteed to signal the
+  ;; type-error.
+  (let ((len 0))
+    (cond ((consp sequence)
+           (lastcar sequence))
+          ((and (typep sequence '(and sequence (not list))) (plusp (setf len (length sequence))))
+           (elt sequence (1- len)))
+          (t
+           (error 'type-error
+                  :datum sequence
+                  :expected-type '(and proper-sequence (not (satisfies emptyp))))))))
+
+(defun (setf last-elt) (object sequence)
+  "Sets the last element of SEQUENCE. Signals a type-error if SEQUENCE is not a proper
+sequence, is an empty sequence, or if OBJECT cannot be stored in SEQUENCE."
+  (let ((len 0))
+    (cond ((consp sequence)
+           (setf (lastcar sequence) object))
+          ((and (typep sequence '(and sequence (not list))) (plusp (setf len (length sequence))))
+           (setf (elt sequence (1- len)) object))
+          (t
+           (error 'type-error
+                  :datum sequence
+                  :expected-type '(and proper-sequence (not (satisfies emptyp))))))))
+
+(defun starts-with-subseq (prefix sequence &rest args
+                           &key
+                           (return-suffix nil return-suffix-supplied-p)
+                           &allow-other-keys)
+  "Test whether the first elements of SEQUENCE are the same (as per TEST) as the elements of PREFIX.
+
+If RETURN-SUFFIX is T the function returns, as a second value, a
+sub-sequence or displaced array pointing to the sequence after PREFIX."
+  (declare (dynamic-extent args))
+  (let ((sequence-length (length sequence))
+        (prefix-length (length prefix)))
+    (when (< sequence-length prefix-length)
+      (return-from starts-with-subseq (values nil nil)))
+    (flet ((make-suffix (start)
+             (when return-suffix
+               (cond
+                 ((not (arrayp sequence))
+                  (if start
+                      (subseq sequence start)
+                      (subseq sequence 0 0)))
+                 ((not start)
+                  (make-array 0
+                              :element-type (array-element-type sequence)
+                              :adjustable nil))
+                 (t
+                  (make-array (- sequence-length start)
+                              :element-type (array-element-type sequence)
+                              :displaced-to sequence
+                              :displaced-index-offset start
+                              :adjustable nil))))))
+      (let ((mismatch (apply #'mismatch prefix sequence
+                             (if return-suffix-supplied-p
+                                 (remove-from-plist args :return-suffix)
+                                 args))))
+        (cond
+          ((not mismatch)
+           (values t (make-suffix nil)))
+          ((= mismatch prefix-length)
+           (values t (make-suffix mismatch)))
+          (t
+           (values nil nil)))))))
+
+(defun ends-with-subseq (suffix sequence &key (test #'eql))
+  "Test whether SEQUENCE ends with SUFFIX. In other words: return true if
+the last (length SUFFIX) elements of SEQUENCE are equal to SUFFIX."
+  (let ((sequence-length (length sequence))
+        (suffix-length (length suffix)))
+    (when (< sequence-length suffix-length)
+      ;; if SEQUENCE is shorter than SUFFIX, then SEQUENCE can't end with SUFFIX.
+      (return-from ends-with-subseq nil))
+    (loop for sequence-index from (- sequence-length suffix-length) below sequence-length
+          for suffix-index from 0 below suffix-length
+          when (not (funcall test (elt sequence sequence-index) (elt suffix suffix-index)))
+          do (return-from ends-with-subseq nil)
+          finally (return t))))
+
+(defun starts-with (object sequence &key (test #'eql) (key #'identity))
+  "Returns true if SEQUENCE is a sequence whose first element is EQL to OBJECT.
+Returns NIL if the SEQUENCE is not a sequence or is an empty sequence."
+  (let ((first-elt (typecase sequence
+                     (cons (car sequence))
+                     (sequence
+                      (if (emptyp sequence)
+                          (return-from starts-with nil)
+                          (elt sequence 0)))
+                     (t
+                      (return-from starts-with nil)))))
+    (funcall test (funcall key first-elt) object)))
+
+(defun ends-with (object sequence &key (test #'eql) (key #'identity))
+  "Returns true if SEQUENCE is a sequence whose last element is EQL to OBJECT.
+Returns NIL if the SEQUENCE is not a sequence or is an empty sequence. Signals
+an error if SEQUENCE is an improper list."
+  (let ((last-elt (typecase sequence
+                    (cons
+                     (lastcar sequence)) ; signals for improper lists
+                    (sequence
+                     ;; Can't use last-elt, as that signals an error
+                     ;; for empty sequences
+                     (let ((len (length sequence)))
+                       (if (plusp len)
+                           (elt sequence (1- len))
+                           (return-from ends-with nil))))
+                    (t
+                     (return-from ends-with nil)))))
+    (funcall test (funcall key last-elt) object)))
+
+(defun map-combinations (function sequence &key (start 0) end length (copy t))
+  "Calls FUNCTION with each combination of LENGTH constructable from the
+elements of the subsequence of SEQUENCE delimited by START and END. START
+defaults to 0, END to length of SEQUENCE, and LENGTH to the length of the
+delimited subsequence. (So unless LENGTH is specified there is only a single
+combination, which has the same elements as the delimited subsequence.) If
+COPY is true (the default) each combination is freshly allocated. If COPY is
+false all combinations are EQ to each other, in which case consequences are
+specified if a combination is modified by FUNCTION."
+  (let* ((end (or end (length sequence)))
+         (size (- end start))
+         (length (or length size))
+         (combination (subseq sequence 0 length))
+         (function (ensure-function function)))
+    (if (= length size)
+        (funcall function combination)
+        (flet ((call ()
+                 (funcall function (if copy
+                                       (copy-seq combination)
+                                       combination))))
+          (etypecase sequence
+            ;; When dealing with lists we prefer walking back and
+            ;; forth instead of using indexes.
+            (list
+             (labels ((combine-list (c-tail o-tail)
+                        (if (not c-tail)
+                            (call)
+                            (do ((tail o-tail (cdr tail)))
+                                ((not tail))
+                              (setf (car c-tail) (car tail))
+                              (combine-list (cdr c-tail) (cdr tail))))))
+               (combine-list combination (nthcdr start sequence))))
+            (vector
+             (labels ((combine (count start)
+                        (if (zerop count)
+                            (call)
+                            (loop for i from start below end
+                                  do (let ((j (- count 1)))
+                                       (setf (aref combination j) (aref sequence i))
+                                       (combine j (+ i 1)))))))
+               (combine length start)))
+            (sequence
+             (labels ((combine (count start)
+                        (if (zerop count)
+                            (call)
+                            (loop for i from start below end
+                                  do (let ((j (- count 1)))
+                                       (setf (elt combination j) (elt sequence i))
+                                       (combine j (+ i 1)))))))
+               (combine length start)))))))
+  sequence)
+
+(defun map-permutations (function sequence &key (start 0) end length (copy t))
+  "Calls function with each permutation of LENGTH constructable
+from the subsequence of SEQUENCE delimited by START and END. START
+defaults to 0, END to length of the sequence, and LENGTH to the
+length of the delimited subsequence."
+  (let* ((end (or end (length sequence)))
+         (size (- end start))
+         (length (or length size)))
+    (labels ((permute (seq n)
+               (let ((n-1 (- n 1)))
+                 (if (zerop n-1)
+                     (funcall function (if copy
+                                           (copy-seq seq)
+                                           seq))
+                     (loop for i from 0 upto n-1
+                           do (permute seq n-1)
+                           (if (evenp n-1)
+                               (rotatef (elt seq 0) (elt seq n-1))
+                               (rotatef (elt seq i) (elt seq n-1)))))))
+             (permute-sequence (seq)
+               (permute seq length)))
+      (if (= length size)
+          ;; Things are simple if we need to just permute the
+          ;; full START-END range.
+          (permute-sequence (subseq sequence start end))
+          ;; Otherwise we need to generate all the combinations
+          ;; of LENGTH in the START-END range, and then permute
+          ;; a copy of the result: can't permute the combination
+          ;; directly, as they share structure with each other.
+          (let ((permutation (subseq sequence 0 length)))
+            (flet ((permute-combination (combination)
+                     (permute-sequence (replace permutation combination))))
+              (declare (dynamic-extent #'permute-combination))
+              (map-combinations #'permute-combination sequence
+                                :start start
+                                :end end
+                                :length length
+                                :copy nil)))))))
+
+(defun map-derangements (function sequence &key (start 0) end (copy t))
+  "Calls FUNCTION with each derangement of the subsequence of SEQUENCE denoted
+by the bounding index designators START and END. Derangement is a permutation
+of the sequence where no element remains in place. SEQUENCE is not modified,
+but individual derangements are EQ to each other. Consequences are unspecified
+if calling FUNCTION modifies either the derangement or SEQUENCE."
+  (let* ((end (or end (length sequence)))
+         (size (- end start))
+         ;; We don't really care about the elements here.
+         (derangement (subseq sequence 0 size))
+         ;; Bitvector that has 1 for elements that have been deranged.
+         (mask (make-array size :element-type 'bit :initial-element 0)))
+    (declare (dynamic-extent mask))
+    ;; ad hoc algorith
+    (labels ((derange (place n)
+               ;; Perform one recursive step in deranging the
+               ;; sequence: PLACE is index of the original sequence
+               ;; to derange to another index, and N is the number of
+               ;; indexes not yet deranged.
+               (if (zerop n)
+                   (funcall function (if copy
+                                         (copy-seq derangement)
+                                         derangement))
+                   ;; Itarate over the indexes I of the subsequence to
+                   ;; derange: if I != PLACE and I has not yet been
+                   ;; deranged by an earlier call put the element from
+                   ;; PLACE to I, mark I as deranged, and recurse,
+                   ;; finally removing the mark.
+                   (loop for i from 0 below size
+                         do
+                         (unless (or (= place (+ i start)) (not (zerop (bit mask i))))
+                           (setf (elt derangement i) (elt sequence place)
+                                 (bit mask i) 1)
+                           (derange (1+ place) (1- n))
+                           (setf (bit mask i) 0))))))
+      (derange start size)
+      sequence)))
+
+(declaim (notinline sequence-of-length-p))
+
+(defun extremum (sequence predicate &key key (start 0) end)
+  "Returns the element of SEQUENCE that would appear first if the subsequence
+bounded by START and END was sorted using PREDICATE and KEY.
+
+EXTREMUM determines the relationship between two elements of SEQUENCE by using
+the PREDICATE function. PREDICATE should return true if and only if the first
+argument is strictly less than the second one (in some appropriate sense). Two
+arguments X and Y are considered to be equal if (FUNCALL PREDICATE X Y)
+and (FUNCALL PREDICATE Y X) are both false.
+
+The arguments to the PREDICATE function are computed from elements of SEQUENCE
+using the KEY function, if supplied. If KEY is not supplied or is NIL, the
+sequence element itself is used.
+
+If SEQUENCE is empty, NIL is returned."
+  (let* ((pred-fun (ensure-function predicate))
+         (key-fun (unless (or (not key) (eq key 'identity) (eq key #'identity))
+                    (ensure-function key)))
+         (real-end (or end (length sequence))))
+    (cond ((> real-end start)
+           (if key-fun
+               (flet ((reduce-keys (a b)
+                        (if (funcall pred-fun
+                                     (funcall key-fun a)
+                                     (funcall key-fun b))
+                            a
+                            b)))
+                 (declare (dynamic-extent #'reduce-keys))
+                 (reduce #'reduce-keys sequence :start start :end real-end))
+               (flet ((reduce-elts (a b)
+                        (if (funcall pred-fun a b)
+                            a
+                            b)))
+                 (declare (dynamic-extent #'reduce-elts))
+                 (reduce #'reduce-elts sequence :start start :end real-end))))
+          ((= real-end start)
+           nil)
+          (t
+           (error "Invalid bounding indexes for sequence of length ~S: ~S ~S, ~S ~S"
+                  (length sequence)
+                  :start start
+                  :end end)))))
diff --git a/deps/alexandria/strings.lisp b/deps/alexandria/strings.lisp
new file mode 100644 (file)
index 0000000..e9fd91c
--- /dev/null
@@ -0,0 +1,6 @@
+(in-package :alexandria)
+
+(deftype string-designator ()
+  "A string designator type. A string designator is either a string, a symbol,
+or a character."
+  `(or symbol string character))
diff --git a/deps/alexandria/symbols.lisp b/deps/alexandria/symbols.lisp
new file mode 100644 (file)
index 0000000..5733d3e
--- /dev/null
@@ -0,0 +1,65 @@
+(in-package :alexandria)
+
+(declaim (inline ensure-symbol))
+(defun ensure-symbol (name &optional (package *package*))
+  "Returns a symbol with name designated by NAME, accessible in package
+designated by PACKAGE. If symbol is not already accessible in PACKAGE, it is
+interned there. Returns a secondary value reflecting the status of the symbol
+in the package, which matches the secondary return value of INTERN.
+
+Example:
+
+  (ensure-symbol :cons :cl) => cl:cons, :external
+"
+  (intern (string name) package))
+
+(defun maybe-intern (name package)
+  (values
+   (if package
+       (intern name (if (eq t package) *package* package))
+       (make-symbol name))))
+
+(declaim (inline format-symbol))
+(defun format-symbol (package control &rest arguments)
+  "Constructs a string by applying ARGUMENTS to string designator CONTROL as
+if by FORMAT within WITH-STANDARD-IO-SYNTAX, and then creates a symbol named
+by that string.
+
+If PACKAGE is NIL, returns an uninterned symbol, if package is T, returns a
+symbol interned in the current package, and otherwise returns a symbol
+interned in the package designated by PACKAGE."
+  (maybe-intern (with-standard-io-syntax
+                  (apply #'format nil (string control) arguments))
+                package))
+
+(defun make-keyword (name)
+  "Interns the string designated by NAME in the KEYWORD package."
+  (intern (string name) :keyword))
+
+(defun make-gensym (name)
+  "If NAME is a non-negative integer, calls GENSYM using it. Otherwise NAME
+must be a string designator, in which case calls GENSYM using the designated
+string as the argument."
+  (gensym (if (typep name '(integer 0))
+              name
+              (string name))))
+
+(defun make-gensym-list (length &optional (x "G"))
+  "Returns a list of LENGTH gensyms, each generated as if with a call to MAKE-GENSYM,
+using the second (optional, defaulting to \"G\") argument."
+  (let ((g (if (typep x '(integer 0)) x (string x))))
+    (loop repeat length
+          collect (gensym g))))
+
+(defun symbolicate (&rest things)
+  "Concatenate together the names of some strings and symbols,
+producing a symbol in the current package."
+  (let* ((length (reduce #'+ things
+                         :key (lambda (x) (length (string x)))))
+         (name (make-array length :element-type 'character)))
+    (let ((index 0))
+      (dolist (thing things (values (intern name)))
+        (let* ((x (string thing))
+               (len (length x)))
+          (replace name x :start1 index)
+          (incf index len))))))
diff --git a/deps/alexandria/tests.lisp b/deps/alexandria/tests.lisp
new file mode 100644 (file)
index 0000000..a00a700
--- /dev/null
@@ -0,0 +1,2029 @@
+(in-package :cl-user)
+
+(defpackage :alexandria-tests
+  (:use :cl :alexandria #+sbcl :sb-rt #-sbcl :rtest)
+  (:import-from #+sbcl :sb-rt #-sbcl :rtest
+                #:*compile-tests* #:*expected-failures*))
+
+(in-package :alexandria-tests)
+
+(defun run-tests (&key ((:compiled *compile-tests*)))
+  (do-tests))
+
+(defun hash-table-test-name (name)
+  ;; Workaround for Clisp calling EQL in a hash-table FASTHASH-EQL.
+  (hash-table-test (make-hash-table :test name)))
+
+;;;; Arrays
+
+(deftest copy-array.1
+    (let* ((orig (vector 1 2 3))
+           (copy (copy-array orig)))
+      (values (eq orig copy) (equalp orig copy)))
+  nil t)
+
+(deftest copy-array.2
+    (let ((orig (make-array 1024 :fill-pointer 0)))
+      (vector-push-extend 1 orig)
+      (vector-push-extend 2 orig)
+      (vector-push-extend 3 orig)
+      (let ((copy (copy-array orig)))
+        (values (eq orig copy) (equalp orig copy)
+                (array-has-fill-pointer-p copy)
+                (eql (fill-pointer orig) (fill-pointer copy)))))
+  nil t t t)
+
+(deftest copy-array.3
+    (let* ((orig (vector 1 2 3))
+           (copy (copy-array orig)))
+      (typep copy 'simple-array))
+  t)
+
+(deftest copy-array.4
+   (let ((orig (make-array 21
+                           :adjustable t
+                           :fill-pointer 0)))
+     (dotimes (n 42)
+       (vector-push-extend n orig))
+     (let ((copy (copy-array orig
+                             :adjustable nil
+                             :fill-pointer nil)))
+       (typep copy 'simple-array)))
+ t)
+
+(deftest array-index.1
+    (typep 0 'array-index)
+  t)
+
+;;;; Conditions
+
+(deftest unwind-protect-case.1
+    (let (result)
+      (unwind-protect-case ()
+          (random 10)
+        (:normal (push :normal result))
+        (:abort  (push :abort result))
+        (:always (push :always result)))
+      result)
+  (:always :normal))
+
+(deftest unwind-protect-case.2
+    (let (result)
+      (unwind-protect-case ()
+          (random 10)
+        (:always (push :always result))
+        (:normal (push :normal result))
+        (:abort  (push :abort result)))
+      result)
+  (:normal :always))
+
+(deftest unwind-protect-case.3
+    (let (result1 result2 result3)
+      (ignore-errors
+        (unwind-protect-case ()
+            (error "FOOF!")
+          (:normal (push :normal result1))
+          (:abort  (push :abort result1))
+          (:always (push :always result1))))
+      (catch 'foof
+        (unwind-protect-case ()
+            (throw 'foof 42)
+          (:normal (push :normal result2))
+          (:abort  (push :abort result2))
+          (:always (push :always result2))))
+      (block foof
+        (unwind-protect-case ()
+            (return-from foof 42)
+          (:normal (push :normal result3))
+          (:abort  (push :abort result3))
+          (:always (push :always result3))))
+      (values result1 result2 result3))
+  (:always :abort)
+  (:always :abort)
+  (:always :abort))
+
+(deftest unwind-protect-case.4
+    (let (result)
+      (unwind-protect-case (aborted-p)
+          (random 42)
+        (:always (setq result aborted-p)))
+      result)
+  nil)
+
+(deftest unwind-protect-case.5
+    (let (result)
+      (block foof
+        (unwind-protect-case (aborted-p)
+            (return-from foof)
+          (:always (setq result aborted-p))))
+      result)
+  t)
+
+;;;; Control flow
+
+(deftest switch.1
+    (switch (13 :test =)
+      (12 :oops)
+      (13.0 :yay))
+  :yay)
+
+(deftest switch.2
+    (switch (13)
+      ((+ 12 2) :oops)
+      ((- 13 1) :oops2)
+      (t :yay))
+  :yay)
+
+(deftest eswitch.1
+    (let ((x 13))
+      (eswitch (x :test =)
+        (12 :oops)
+        (13.0 :yay)))
+  :yay)
+
+(deftest eswitch.2
+    (let ((x 13))
+      (eswitch (x :key 1+)
+        (11 :oops)
+        (14 :yay)))
+  :yay)
+
+(deftest cswitch.1
+    (cswitch (13 :test =)
+      (12 :oops)
+      (13.0 :yay))
+  :yay)
+
+(deftest cswitch.2
+    (cswitch (13 :key 1-)
+      (12 :yay)
+      (13.0 :oops))
+  :yay)
+
+(deftest multiple-value-prog2.1
+    (multiple-value-prog2
+        (values 1 1 1)
+        (values 2 20 200)
+      (values 3 3 3))
+  2 20 200)
+
+(deftest nth-value-or.1
+    (multiple-value-bind (a b c)
+        (nth-value-or 1
+                      (values 1 nil 1)
+                      (values 2 2 2))
+      (= a b c 2))
+  t)
+
+(deftest whichever.1
+    (let ((x (whichever 1 2 3)))
+      (and (member x '(1 2 3)) t))
+  t)
+
+(deftest whichever.2
+    (let* ((a 1)
+           (b 2)
+           (c 3)
+           (x (whichever a b c)))
+      (and (member x '(1 2 3)) t))
+  t)
+
+(deftest xor.1
+    (xor nil nil 1 nil)
+  1
+  t)
+
+(deftest xor.2
+    (xor nil nil 1 2)
+  nil
+  nil)
+
+(deftest xor.3
+    (xor nil nil nil)
+  nil
+  t)
+
+;;;; Definitions
+
+(deftest define-constant.1
+    (let ((name (gensym)))
+      (eval `(define-constant ,name "FOO" :test 'equal))
+      (eval `(define-constant ,name "FOO" :test 'equal))
+      (values (equal "FOO" (symbol-value name))
+              (constantp name)))
+  t
+  t)
+
+(deftest define-constant.2
+    (let ((name (gensym)))
+      (eval `(define-constant ,name 13))
+      (eval `(define-constant ,name 13))
+      (values (eql 13 (symbol-value name))
+              (constantp name)))
+  t
+  t)
+
+;;;; Errors
+
+;;; TYPEP is specified to return a generalized boolean and, for
+;;; example, ECL exploits this by returning the superclasses of ERROR
+;;; in this case.
+(defun errorp (x)
+  (not (null (typep x 'error))))
+
+(deftest required-argument.1
+    (multiple-value-bind (res err)
+        (ignore-errors (required-argument))
+      (errorp err))
+  t)
+
+;;;; Hash tables
+
+(deftest ensure-gethash.1
+    (let ((table (make-hash-table))
+          (x (list 1)))
+      (multiple-value-bind (value already-there)
+          (ensure-gethash x table 42)
+        (and (= value 42)
+             (not already-there)
+             (= 42 (gethash x table))
+             (multiple-value-bind (value2 already-there2)
+                 (ensure-gethash x table 13)
+               (and (= value2 42)
+                    already-there2
+                    (= 42 (gethash x table)))))))
+  t)
+
+(deftest ensure-gethash.2
+    (let ((table (make-hash-table))
+          (count 0))
+      (multiple-value-call #'values
+        (ensure-gethash (progn (incf count) :foo)
+                        (progn (incf count) table)
+                        (progn (incf count) :bar))
+        (gethash :foo table)
+        count))
+  :bar nil :bar t 3)
+
+(deftest copy-hash-table.1
+    (let ((orig (make-hash-table :test 'eq :size 123))
+          (foo "foo"))
+      (setf (gethash orig orig) t
+            (gethash foo orig) t)
+      (let ((eq-copy (copy-hash-table orig))
+            (eql-copy (copy-hash-table orig :test 'eql))
+            (equal-copy (copy-hash-table orig :test 'equal))
+            (equalp-copy (copy-hash-table orig :test 'equalp)))
+        (list (eql (hash-table-size eq-copy) (hash-table-size orig))
+              (eql (hash-table-rehash-size eq-copy)
+                   (hash-table-rehash-size orig))
+              (hash-table-count eql-copy)
+              (gethash orig eq-copy)
+              (gethash (copy-seq foo) eql-copy)
+              (gethash foo eql-copy)
+              (gethash (copy-seq foo) equal-copy)
+              (gethash "FOO" equal-copy)
+              (gethash "FOO" equalp-copy))))
+  (t t 2 t nil t t nil t))
+
+(deftest copy-hash-table.2
+    (let ((ht (make-hash-table))
+          (list (list :list (vector :A :B :C))))
+      (setf (gethash 'list ht) list)
+      (let* ((shallow-copy (copy-hash-table ht))
+             (deep1-copy (copy-hash-table ht :key 'copy-list))
+             (list         (gethash 'list ht))
+             (shallow-list (gethash 'list shallow-copy))
+             (deep1-list   (gethash 'list deep1-copy)))
+        (list (eq ht shallow-copy)
+              (eq ht deep1-copy)
+              (eq list shallow-list)
+              (eq list deep1-list)                   ; outer list was copied.
+              (eq (second list) (second shallow-list))
+              (eq (second list) (second deep1-list)) ; inner vector wasn't copied.
+              )))
+  (nil nil t nil t t))
+
+(deftest maphash-keys.1
+    (let ((keys nil)
+          (table (make-hash-table)))
+      (declare (notinline maphash-keys))
+      (dotimes (i 10)
+        (setf (gethash i table) t))
+      (maphash-keys (lambda (k) (push k keys)) table)
+      (set-equal keys '(0 1 2 3 4 5 6 7 8 9)))
+  t)
+
+(deftest maphash-values.1
+    (let ((vals nil)
+          (table (make-hash-table)))
+      (declare (notinline maphash-values))
+      (dotimes (i 10)
+        (setf (gethash i table) (- i)))
+      (maphash-values (lambda (v) (push v vals)) table)
+      (set-equal vals '(0 -1 -2 -3 -4 -5 -6 -7 -8 -9)))
+  t)
+
+(deftest hash-table-keys.1
+    (let ((table (make-hash-table)))
+      (dotimes (i 10)
+        (setf (gethash i table) t))
+      (set-equal (hash-table-keys table) '(0 1 2 3 4 5 6 7 8 9)))
+  t)
+
+(deftest hash-table-values.1
+    (let ((table (make-hash-table)))
+      (dotimes (i 10)
+        (setf (gethash (gensym) table) i))
+      (set-equal (hash-table-values table) '(0 1 2 3 4 5 6 7 8 9)))
+  t)
+
+(deftest hash-table-alist.1
+    (let ((table (make-hash-table)))
+      (dotimes (i 10)
+        (setf (gethash i table) (- i)))
+      (let ((alist (hash-table-alist table)))
+        (list (length alist)
+              (assoc 0 alist)
+              (assoc 3 alist)
+              (assoc 9 alist)
+              (assoc nil alist))))
+  (10 (0 . 0) (3 . -3) (9 . -9) nil))
+
+(deftest hash-table-plist.1
+    (let ((table (make-hash-table)))
+      (dotimes (i 10)
+        (setf (gethash i table) (- i)))
+      (let ((plist (hash-table-plist table)))
+        (list (length plist)
+              (getf plist 0)
+              (getf plist 2)
+              (getf plist 7)
+              (getf plist nil))))
+  (20 0 -2 -7 nil))
+
+(deftest alist-hash-table.1
+    (let* ((alist '((0 a) (1 b) (2 c)))
+           (table (alist-hash-table alist)))
+      (list (hash-table-count table)
+            (gethash 0 table)
+            (gethash 1 table)
+            (gethash 2 table)
+            (eq (hash-table-test-name 'eql)
+                (hash-table-test table))))
+  (3 (a) (b) (c) t))
+
+(deftest plist-hash-table.1
+    (let* ((plist '(:a 1 :b 2 :c 3))
+           (table (plist-hash-table plist :test 'eq)))
+      (list (hash-table-count table)
+            (gethash :a table)
+            (gethash :b table)
+            (gethash :c table)
+            (gethash 2 table)
+            (gethash nil table)
+            (eq (hash-table-test-name 'eq)
+                (hash-table-test table))))
+  (3 1 2 3 nil nil t))
+
+;;;; Functions
+
+(deftest disjoin.1
+    (let ((disjunction (disjoin (lambda (x)
+                                  (and (consp x) :cons))
+                                (lambda (x)
+                                  (and (stringp x) :string)))))
+      (list (funcall disjunction 'zot)
+            (funcall disjunction '(foo bar))
+            (funcall disjunction "test")))
+  (nil :cons :string))
+
+(deftest disjoin.2
+    (let ((disjunction (disjoin #'zerop)))
+      (list (funcall disjunction 0)
+            (funcall disjunction 1)))
+  (t nil))
+
+(deftest conjoin.1
+    (let ((conjunction (conjoin #'consp
+                                (lambda (x)
+                                  (stringp (car x)))
+                                (lambda (x)
+                                  (char (car x) 0)))))
+      (list (funcall conjunction 'zot)
+            (funcall conjunction '(foo))
+            (funcall conjunction '("foo"))))
+  (nil nil #\f))
+
+(deftest conjoin.2
+    (let ((conjunction (conjoin #'zerop)))
+      (list (funcall conjunction 0)
+            (funcall conjunction 1)))
+  (t nil))
+
+(deftest compose.1
+    (let ((composite (compose '1+
+                              (lambda (x)
+                                (* x 2))
+                              #'read-from-string)))
+      (funcall composite "1"))
+  3)
+
+(deftest compose.2
+    (let ((composite
+           (locally (declare (notinline compose))
+             (compose '1+
+                      (lambda (x)
+                        (* x 2))
+                      #'read-from-string))))
+      (funcall composite "2"))
+  5)
+
+(deftest compose.3
+    (let ((compose-form (funcall (compiler-macro-function 'compose)
+                                 '(compose '1+
+                                   (lambda (x)
+                                     (* x 2))
+                                   #'read-from-string)
+                                 nil)))
+      (let ((fun (funcall (compile nil `(lambda () ,compose-form)))))
+        (funcall fun "3")))
+  7)
+
+(deftest compose.4
+    (let ((composite (compose #'zerop)))
+      (list (funcall composite 0)
+            (funcall composite 1)))
+  (t nil))
+
+(deftest multiple-value-compose.1
+    (let ((composite (multiple-value-compose
+                      #'truncate
+                      (lambda (x y)
+                        (values y x))
+                      (lambda (x)
+                        (with-input-from-string (s x)
+                          (values (read s) (read s)))))))
+      (multiple-value-list (funcall composite "2 7")))
+  (3 1))
+
+(deftest multiple-value-compose.2
+    (let ((composite (locally (declare (notinline multiple-value-compose))
+                       (multiple-value-compose
+                        #'truncate
+                        (lambda (x y)
+                          (values y x))
+                       (lambda (x)
+                         (with-input-from-string (s x)
+                           (values (read s) (read s))))))))
+      (multiple-value-list (funcall composite "2 11")))
+  (5 1))
+
+(deftest multiple-value-compose.3
+    (let ((compose-form (funcall (compiler-macro-function 'multiple-value-compose)
+                                 '(multiple-value-compose
+                                   #'truncate
+                                   (lambda (x y)
+                                     (values y x))
+                                   (lambda (x)
+                                     (with-input-from-string (s x)
+                                       (values (read s) (read s)))))
+                                 nil)))
+      (let ((fun (funcall (compile nil `(lambda () ,compose-form)))))
+        (multiple-value-list (funcall fun "2 9"))))
+  (4 1))
+
+(deftest multiple-value-compose.4
+    (let ((composite (multiple-value-compose #'truncate)))
+      (multiple-value-list (funcall composite 9 2)))
+  (4 1))
+
+(deftest curry.1
+    (let ((curried (curry '+ 3)))
+      (funcall curried 1 5))
+  9)
+
+(deftest curry.2
+    (let ((curried (locally (declare (notinline curry))
+                     (curry '* 2 3))))
+      (funcall curried 7))
+  42)
+
+(deftest curry.3
+    (let ((curried-form (funcall (compiler-macro-function 'curry)
+                                 '(curry '/ 8)
+                                 nil)))
+      (let ((fun (funcall (compile nil `(lambda () ,curried-form)))))
+        (funcall fun 2)))
+  4)
+
+(deftest curry.4
+    (let* ((x 1)
+           (curried (curry (progn
+                             (incf x)
+                             (lambda (y z) (* x y z)))
+                           3)))
+      (list (funcall curried 7)
+            (funcall curried 7)
+            x))
+  (42 42 2))
+
+(deftest rcurry.1
+    (let ((r (rcurry '/ 2)))
+      (funcall r 8))
+  4)
+
+(deftest rcurry.2
+    (let* ((x 1)
+           (curried (rcurry (progn
+                              (incf x)
+                              (lambda (y z) (* x y z)))
+                            3)))
+      (list (funcall curried 7)
+            (funcall curried 7)
+            x))
+  (42 42 2))
+
+(deftest named-lambda.1
+    (let ((fac (named-lambda fac (x)
+                 (if (> x 1)
+                     (* x (fac (- x 1)))
+                     x))))
+      (funcall fac 5))
+  120)
+
+(deftest named-lambda.2
+    (let ((fac (named-lambda fac (&key x)
+                 (if (> x 1)
+                     (* x (fac :x (- x 1)))
+                     x))))
+      (funcall fac :x 5))
+  120)
+
+;;;; Lists
+
+(deftest alist-plist.1
+    (alist-plist '((a . 1) (b . 2) (c . 3)))
+  (a 1 b 2 c 3))
+
+(deftest plist-alist.1
+    (plist-alist '(a 1 b 2 c 3))
+  ((a . 1) (b . 2) (c . 3)))
+
+(deftest unionf.1
+    (let* ((list (list 1 2 3))
+           (orig list))
+      (unionf list (list 1 2 4))
+      (values (equal orig (list 1 2 3))
+              (eql (length list) 4)
+              (set-difference list (list 1 2 3 4))
+              (set-difference (list 1 2 3 4) list)))
+  t
+  t
+  nil
+  nil)
+
+(deftest nunionf.1
+    (let ((list (list 1 2 3)))
+      (nunionf list (list 1 2 4))
+      (values (eql (length list) 4)
+              (set-difference (list 1 2 3 4) list)
+              (set-difference list (list 1 2 3 4))))
+  t
+  nil
+  nil)
+
+(deftest appendf.1
+    (let* ((list (list 1 2 3))
+           (orig list))
+      (appendf list '(4 5 6) '(7 8))
+      (list list (eq list orig)))
+  ((1 2 3 4 5 6 7 8) nil))
+
+(deftest nconcf.1
+    (let ((list1 (list 1 2 3))
+          (list2 (list 4 5 6)))
+      (nconcf list1 list2 (list 7 8 9))
+      list1)
+  (1 2 3 4 5 6 7 8 9))
+
+(deftest circular-list.1
+    (let ((circle (circular-list 1 2 3)))
+      (list (first circle)
+            (second circle)
+            (third circle)
+            (fourth circle)
+            (eq circle (nthcdr 3 circle))))
+  (1 2 3 1 t))
+
+(deftest circular-list-p.1
+    (let* ((circle (circular-list 1 2 3 4))
+           (tree (list circle circle))
+           (dotted (cons circle t))
+           (proper (list 1 2 3 circle))
+           (tailcirc (list* 1 2 3 circle)))
+      (list (circular-list-p circle)
+            (circular-list-p tree)
+            (circular-list-p dotted)
+            (circular-list-p proper)
+            (circular-list-p tailcirc)))
+  (t nil nil nil t))
+
+(deftest circular-list-p.2
+    (circular-list-p 'foo)
+  nil)
+
+(deftest circular-tree-p.1
+    (let* ((circle (circular-list 1 2 3 4))
+           (tree1 (list circle circle))
+           (tree2 (let* ((level2 (list 1 nil 2))
+                         (level1 (list level2)))
+                    (setf (second level2) level1)
+                    level1))
+           (dotted (cons circle t))
+           (proper (list 1 2 3 circle))
+           (tailcirc (list* 1 2 3 circle))
+           (quite-proper (list 1 2 3))
+           (quite-dotted (list 1 (cons 2 3))))
+      (list (circular-tree-p circle)
+            (circular-tree-p tree1)
+            (circular-tree-p tree2)
+            (circular-tree-p dotted)
+            (circular-tree-p proper)
+            (circular-tree-p tailcirc)
+            (circular-tree-p quite-proper)
+            (circular-tree-p quite-dotted)))
+  (t t t t t t nil nil))
+
+(deftest circular-tree-p.2
+    (alexandria:circular-tree-p '#1=(#1#))
+  t)
+
+(deftest proper-list-p.1
+    (let ((l1 (list 1))
+          (l2 (list 1 2))
+          (l3 (cons 1 2))
+          (l4 (list (cons 1 2) 3))
+          (l5 (circular-list 1 2)))
+      (list (proper-list-p l1)
+            (proper-list-p l2)
+            (proper-list-p l3)
+            (proper-list-p l4)
+            (proper-list-p l5)))
+  (t t nil t nil))
+
+(deftest proper-list-p.2
+    (proper-list-p '(1 2 . 3))
+  nil)
+
+(deftest proper-list.type.1
+    (let ((l1 (list 1))
+          (l2 (list 1 2))
+          (l3 (cons 1 2))
+          (l4 (list (cons 1 2) 3))
+          (l5 (circular-list 1 2)))
+      (list (typep l1 'proper-list)
+            (typep l2 'proper-list)
+            (typep l3 'proper-list)
+            (typep l4 'proper-list)
+            (typep l5 'proper-list)))
+  (t t nil t nil))
+
+(deftest proper-list-length.1
+    (values
+     (proper-list-length nil)
+     (proper-list-length (list 1))
+     (proper-list-length (list 2 2))
+     (proper-list-length (list 3 3 3))
+     (proper-list-length (list 4 4 4 4))
+     (proper-list-length (list 5 5 5 5 5))
+     (proper-list-length (list 6 6 6 6 6 6))
+     (proper-list-length (list 7 7 7 7 7 7 7))
+     (proper-list-length (list 8 8 8 8 8 8 8 8))
+     (proper-list-length (list 9 9 9 9 9 9 9 9 9)))
+  0 1 2 3 4 5 6 7 8 9)
+
+(deftest proper-list-length.2
+    (flet ((plength (x)
+             (handler-case
+                 (proper-list-length x)
+               (type-error ()
+                 :ok))))
+      (values
+       (plength (list* 1))
+       (plength (list* 2 2))
+       (plength (list* 3 3 3))
+       (plength (list* 4 4 4 4))
+       (plength (list* 5 5 5 5 5))
+       (plength (list* 6 6 6 6 6 6))
+       (plength (list* 7 7 7 7 7 7 7))
+       (plength (list* 8 8 8 8 8 8 8 8))
+       (plength (list* 9 9 9 9 9 9 9 9 9))))
+  :ok :ok :ok
+  :ok :ok :ok
+  :ok :ok :ok)
+
+(deftest lastcar.1
+    (let ((l1 (list 1))
+          (l2 (list 1 2)))
+      (list (lastcar l1)
+            (lastcar l2)))
+  (1 2))
+
+(deftest lastcar.error.2
+    (handler-case
+        (progn
+          (lastcar (circular-list 1 2 3))
+          nil)
+      (error ()
+        t))
+  t)
+
+(deftest setf-lastcar.1
+    (let ((l (list 1 2 3 4)))
+      (values (lastcar l)
+              (progn
+                (setf (lastcar l) 42)
+                (lastcar l))))
+  4
+  42)
+
+(deftest setf-lastcar.2
+    (let ((l (circular-list 1 2 3)))
+      (multiple-value-bind (res err)
+          (ignore-errors (setf (lastcar l) 4))
+        (typep err 'type-error)))
+  t)
+
+(deftest make-circular-list.1
+    (let ((l (make-circular-list 3 :initial-element :x)))
+      (setf (car l) :y)
+      (list (eq l (nthcdr 3 l))
+            (first l)
+            (second l)
+            (third l)
+            (fourth l)))
+  (t :y :x :x :y))
+
+(deftest circular-list.type.1
+    (let* ((l1 (list 1 2 3))
+           (l2 (circular-list 1 2 3))
+           (l3 (list* 1 2 3 l2)))
+      (list (typep l1 'circular-list)
+            (typep l2 'circular-list)
+            (typep l3 'circular-list)))
+  (nil t t))
+
+(deftest ensure-list.1
+    (let ((x (list 1))
+          (y 2))
+      (list (ensure-list x)
+            (ensure-list y)))
+  ((1) (2)))
+
+(deftest ensure-cons.1
+    (let ((x (cons 1 2))
+          (y nil)
+          (z "foo"))
+      (values (ensure-cons x)
+              (ensure-cons y)
+              (ensure-cons z)))
+  (1 . 2)
+  (nil)
+  ("foo"))
+
+(deftest setp.1
+    (setp '(1))
+  t)
+
+(deftest setp.2
+    (setp nil)
+  t)
+
+(deftest setp.3
+    (setp "foo")
+  nil)
+
+(deftest setp.4
+    (setp '(1 2 3 1))
+  nil)
+
+(deftest setp.5
+    (setp '(1 2 3))
+  t)
+
+(deftest setp.6
+    (setp '(a :a))
+  t)
+
+(deftest setp.7
+    (setp '(a :a) :key 'character)
+  nil)
+
+(deftest setp.8
+    (setp '(a :a) :key 'character :test (constantly nil))
+  t)
+
+(deftest set-equal.1
+    (set-equal '(1 2 3) '(3 1 2))
+  t)
+
+(deftest set-equal.2
+    (set-equal '("Xa") '("Xb")
+               :test (lambda (a b) (eql (char a 0) (char b 0))))
+  t)
+
+(deftest set-equal.3
+    (set-equal '(1 2) '(4 2))
+  nil)
+
+(deftest set-equal.4
+    (set-equal '(a b c) '(:a :b :c) :key 'string :test 'equal)
+  t)
+
+(deftest set-equal.5
+    (set-equal '(a d c) '(:a :b :c) :key 'string :test 'equal)
+  nil)
+
+(deftest set-equal.6
+    (set-equal '(a b c) '(a b c d))
+  nil)
+
+(deftest map-product.1
+    (map-product 'cons '(2 3) '(1 4))
+  ((2 . 1) (2 . 4) (3 . 1) (3 . 4)))
+
+(deftest map-product.2
+    (map-product #'cons '(2 3) '(1 4))
+  ((2 . 1) (2 . 4) (3 . 1) (3 . 4)))
+
+(deftest flatten.1
+    (flatten '((1) 2 (((3 4))) ((((5)) 6)) 7))
+  (1 2 3 4 5 6 7))
+
+(deftest remove-from-plist.1
+    (let ((orig '(a 1 b 2 c 3 d 4)))
+      (list (remove-from-plist orig 'a 'c)
+            (remove-from-plist orig 'b 'd)
+            (remove-from-plist orig 'b)
+            (remove-from-plist orig 'a)
+            (remove-from-plist orig 'd 42 "zot")
+            (remove-from-plist orig 'a 'b 'c 'd)
+            (remove-from-plist orig 'a 'b 'c 'd 'x)
+            (equal orig '(a 1 b 2 c 3 d 4))))
+  ((b 2 d 4)
+   (a 1 c 3)
+   (a 1 c 3 d 4)
+   (b 2 c 3 d 4)
+   (a 1 b 2 c 3)
+   nil
+   nil
+   t))
+
+(deftest delete-from-plist.1
+    (let ((orig '(a 1 b 2 c 3 d 4 d 5)))
+      (list (delete-from-plist (copy-list orig) 'a 'c)
+            (delete-from-plist (copy-list orig) 'b 'd)
+            (delete-from-plist (copy-list orig) 'b)
+            (delete-from-plist (copy-list orig) 'a)
+            (delete-from-plist (copy-list orig) 'd 42 "zot")
+            (delete-from-plist (copy-list orig) 'a 'b 'c 'd)
+            (delete-from-plist (copy-list orig) 'a 'b 'c 'd 'x)
+            (equal orig (delete-from-plist orig))
+            (eq orig (delete-from-plist orig))))
+  ((b 2 d 4 d 5)
+   (a 1 c 3)
+   (a 1 c 3 d 4 d 5)
+   (b 2 c 3 d 4 d 5)
+   (a 1 b 2 c 3)
+   nil
+   nil
+   t
+   t))
+
+(deftest mappend.1
+    (mappend (compose 'list '*) '(1 2 3) '(1 2 3))
+  (1 4 9))
+
+(deftest assoc-value.1
+    (let ((key1 '(complex key))
+          (key2 'simple-key)
+          (alist '())
+          (result '()))
+      (push 1 (assoc-value alist key1 :test #'equal))
+      (push 2 (assoc-value alist key1 :test 'equal))
+      (push 42 (assoc-value alist key2))
+      (push 43 (assoc-value alist key2 :test 'eq))
+      (push (assoc-value alist key1 :test #'equal) result)
+      (push (assoc-value alist key2) result)
+
+      (push 'very (rassoc-value alist (list 2 1) :test #'equal))
+      (push (cdr (assoc '(very complex key) alist :test #'equal)) result)
+      result)
+  ((2 1) (43 42) (2 1)))
+
+;;;; Numbers
+
+(deftest clamp.1
+    (list (clamp 1.5 1 2)
+          (clamp 2.0 1 2)
+          (clamp 1.0 1 2)
+          (clamp 3 1 2)
+          (clamp 0 1 2))
+  (1.5 2.0 1.0 2 1))
+
+(deftest gaussian-random.1
+    (let ((min -0.2)
+          (max +0.2))
+      (multiple-value-bind (g1 g2)
+          (gaussian-random min max)
+        (values (<= min g1 max)
+                (<= min g2 max)
+                (/= g1 g2) ;uh
+                )))
+  t
+  t
+  t)
+
+#+sbcl
+(deftest gaussian-random.2
+    (handler-case
+        (sb-ext:with-timeout 2
+          (progn
+            (loop
+              :repeat 10000
+              :do (gaussian-random 0 nil))
+            'done))
+      (sb-ext:timeout ()
+        'timed-out))
+  done)
+
+(deftest iota.1
+    (iota 3)
+  (0 1 2))
+
+(deftest iota.2
+    (iota 3 :start 0.0d0)
+  (0.0d0 1.0d0 2.0d0))
+
+(deftest iota.3
+    (iota 3 :start 2 :step 3.0)
+  (2.0 5.0 8.0))
+
+(deftest map-iota.1
+    (let (all)
+      (declare (notinline map-iota))
+      (values (map-iota (lambda (x) (push x all))
+                        3
+                        :start 2
+                        :step 1.1d0)
+              all))
+  3
+  (4.2d0 3.1d0 2.0d0))
+
+(deftest lerp.1
+    (lerp 0.5 1 2)
+  1.5)
+
+(deftest lerp.2
+    (lerp 0.1 1 2)
+  1.1)
+
+(deftest lerp.3
+    (lerp 0.1 4 25)
+  6.1)
+
+(deftest mean.1
+    (mean '(1 2 3))
+  2)
+
+(deftest mean.2
+    (mean '(1 2 3 4))
+  5/2)
+
+(deftest mean.3
+    (mean '(1 2 10))
+  13/3)
+
+(deftest median.1
+    (median '(100 0 99 1 98 2 97))
+  97)
+
+(deftest median.2
+    (median '(100 0 99 1 98 2 97 96))
+  193/2)
+
+(deftest variance.1
+    (variance (list 1 2 3))
+  2/3)
+
+(deftest standard-deviation.1
+    (< 0 (standard-deviation (list 1 2 3)) 1)
+  t)
+
+(deftest maxf.1
+    (let ((x 1))
+      (maxf x 2)
+      x)
+  2)
+
+(deftest maxf.2
+    (let ((x 1))
+      (maxf x 0)
+      x)
+  1)
+
+(deftest maxf.3
+    (let ((x 1)
+          (c 0))
+      (maxf x (incf c))
+      (list x c))
+  (1 1))
+
+(deftest maxf.4
+    (let ((xv (vector 0 0 0))
+          (p 0))
+      (maxf (svref xv (incf p)) (incf p))
+      (list p xv))
+  (2 #(0 2 0)))
+
+(deftest minf.1
+    (let ((y 1))
+      (minf y 0)
+      y)
+  0)
+
+(deftest minf.2
+    (let ((xv (vector 10 10 10))
+          (p 0))
+      (minf (svref xv (incf p)) (incf p))
+      (list p xv))
+  (2 #(10 2 10)))
+
+(deftest subfactorial.1
+    (mapcar #'subfactorial (iota 22))
+  (1
+   0
+   1
+   2
+   9
+   44
+   265
+   1854
+   14833
+   133496
+   1334961
+   14684570
+   176214841
+   2290792932
+   32071101049
+   481066515734
+   7697064251745
+   130850092279664
+   2355301661033953
+   44750731559645106
+   895014631192902121
+   18795307255050944540))
+
+;;;; Arrays
+
+#+nil
+(deftest array-index.type)
+
+#+nil
+(deftest copy-array)
+
+;;;; Sequences
+
+(deftest rotate.1
+    (list (rotate (list 1 2 3) 0)
+          (rotate (list 1 2 3) 1)
+          (rotate (list 1 2 3) 2)
+          (rotate (list 1 2 3) 3)
+          (rotate (list 1 2 3) 4))
+  ((1 2 3)
+   (3 1 2)
+   (2 3 1)
+   (1 2 3)
+   (3 1 2)))
+
+(deftest rotate.2
+    (list (rotate (vector 1 2 3 4) 0)
+          (rotate (vector 1 2 3 4))
+          (rotate (vector 1 2 3 4) 2)
+          (rotate (vector 1 2 3 4) 3)
+          (rotate (vector 1 2 3 4) 4)
+          (rotate (vector 1 2 3 4) 5))
+  (#(1 2 3 4)
+    #(4 1 2 3)
+    #(3 4 1 2)
+    #(2 3 4 1)
+    #(1 2 3 4)
+    #(4 1 2 3)))
+
+(deftest rotate.3
+    (list (rotate (list 1 2 3) 0)
+          (rotate (list 1 2 3) -1)
+          (rotate (list 1 2 3) -2)
+          (rotate (list 1 2 3) -3)
+          (rotate (list 1 2 3) -4))
+  ((1 2 3)
+   (2 3 1)
+   (3 1 2)
+   (1 2 3)
+   (2 3 1)))
+
+(deftest rotate.4
+    (list (rotate (vector 1 2 3 4) 0)
+          (rotate (vector 1 2 3 4) -1)
+          (rotate (vector 1 2 3 4) -2)
+          (rotate (vector 1 2 3 4) -3)
+          (rotate (vector 1 2 3 4) -4)
+          (rotate (vector 1 2 3 4) -5))
+  (#(1 2 3 4)
+   #(2 3 4 1)
+   #(3 4 1 2)
+   #(4 1 2 3)
+   #(1 2 3 4)
+   #(2 3 4 1)))
+
+(deftest rotate.5
+    (values (rotate (list 1) 17)
+            (rotate (list 1) -5))
+  (1)
+  (1))
+
+(deftest shuffle.1
+    (let ((s (shuffle (iota 100))))
+      (list (equal s (iota 100))
+            (every (lambda (x)
+                     (member x s))
+                   (iota 100))
+            (every (lambda (x)
+                     (typep x '(integer 0 99)))
+                   s)))
+  (nil t t))
+
+(deftest shuffle.2
+    (let ((s (shuffle (coerce (iota 100) 'vector))))
+      (list (equal s (coerce (iota 100) 'vector))
+            (every (lambda (x)
+                     (find x s))
+                   (iota 100))
+            (every (lambda (x)
+                     (typep x '(integer 0 99)))
+                   s)))
+  (nil t t))
+
+(deftest shuffle.3
+    (let* ((orig (coerce (iota 21) 'vector))
+           (copy (copy-seq orig)))
+      (shuffle copy :start 10 :end 15)
+      (list (every #'eql (subseq copy 0 10) (subseq orig 0 10))
+            (every #'eql (subseq copy 15) (subseq orig 15))))
+  (t t))
+
+(deftest random-elt.1
+    (let ((s1 #(1 2 3 4))
+          (s2 '(1 2 3 4)))
+      (list (dotimes (i 1000 nil)
+              (unless (member (random-elt s1) s2)
+                (return nil))
+              (when (/= (random-elt s1) (random-elt s1))
+                (return t)))
+            (dotimes (i 1000 nil)
+              (unless (member (random-elt s2) s2)
+                (return nil))
+              (when (/= (random-elt s2) (random-elt s2))
+                (return t)))))
+  (t t))
+
+(deftest removef.1
+    (let* ((x '(1 2 3))
+           (x* x)
+           (y #(1 2 3))
+           (y* y))
+      (removef x 1)
+      (removef y 3)
+      (list x x* y y*))
+  ((2 3)
+   (1 2 3)
+   #(1 2)
+   #(1 2 3)))
+
+(deftest deletef.1
+    (let* ((x (list 1 2 3))
+           (x* x)
+           (y (vector 1 2 3)))
+      (deletef x 2)
+      (deletef y 1)
+      (list x x* y))
+  ((1 3)
+   (1 3)
+   #(2 3)))
+
+(deftest map-permutations.1
+    (let ((seq (list 1 2 3))
+          (seen nil)
+          (ok t))
+      (map-permutations (lambda (s)
+                          (unless (set-equal s seq)
+                            (setf ok nil))
+                          (when (member s seen :test 'equal)
+                            (setf ok nil))
+                          (push s seen))
+                        seq
+                        :copy t)
+      (values ok (length seen)))
+  t
+  6)
+
+(deftest proper-sequence.type.1
+    (mapcar (lambda (x)
+              (typep x 'proper-sequence))
+            (list (list 1 2 3)
+                  (vector 1 2 3)
+                  #2a((1 2) (3 4))
+                  (circular-list 1 2 3 4)))
+  (t t nil nil))
+
+(deftest emptyp.1
+    (mapcar #'emptyp
+            (list (list 1)
+                  (circular-list 1)
+                  nil
+                  (vector)
+                  (vector 1)))
+  (nil nil t t nil))
+
+(deftest sequence-of-length-p.1
+    (mapcar #'sequence-of-length-p
+            (list nil
+                  #()
+                  (list 1)
+                  (vector 1)
+                  (list 1 2)
+                  (vector 1 2)
+                  (list 1 2)
+                  (vector 1 2)
+                  (list 1 2)
+                  (vector 1 2))
+            (list 0
+                  0
+                  1
+                  1
+                  2
+                  2
+                  1
+                  1
+                  4
+                  4))
+  (t t t t t t nil nil nil nil))
+
+(deftest length=.1
+    (mapcar #'length=
+            (list nil
+                  #()
+                  (list 1)
+                  (vector 1)
+                  (list 1 2)
+                  (vector 1 2)
+                  (list 1 2)
+                  (vector 1 2)
+                  (list 1 2)
+                  (vector 1 2))
+            (list 0
+                  0
+                  1
+                  1
+                  2
+                  2
+                  1
+                  1
+                  4
+                  4))
+  (t t t t t t nil nil nil nil))
+
+(deftest length=.2
+    ;; test the compiler macro
+    (macrolet ((x (&rest args)
+                 (funcall
+                  (compile nil
+                           `(lambda ()
+                              (length= ,@args))))))
+      (list (x 2 '(1 2))
+            (x '(1 2) '(3 4))
+            (x '(1 2) 2)
+            (x '(1 2) 2 '(3 4))
+            (x 1 2 3)))
+  (t t t t nil))
+
+(deftest copy-sequence.1
+    (let ((l (list 1 2 3))
+          (v (vector #\a #\b #\c)))
+      (declare (notinline copy-sequence))
+      (let ((l.list (copy-sequence 'list l))
+            (l.vector (copy-sequence 'vector l))
+            (l.spec-v (copy-sequence '(vector fixnum) l))
+            (v.vector (copy-sequence 'vector v))
+            (v.list (copy-sequence 'list v))
+            (v.string (copy-sequence 'string v)))
+        (list (member l (list l.list l.vector l.spec-v))
+              (member v (list v.vector v.list v.string))
+              (equal l.list l)
+              (equalp l.vector #(1 2 3))
+              (type= (upgraded-array-element-type 'fixnum)
+                     (array-element-type l.spec-v))
+              (equalp v.vector v)
+              (equal v.list '(#\a #\b #\c))
+              (equal "abc" v.string))))
+  (nil nil t t t t t t))
+
+(deftest first-elt.1
+    (mapcar #'first-elt
+            (list (list 1 2 3)
+                  "abc"
+                  (vector :a :b :c)))
+  (1 #\a :a))
+
+(deftest first-elt.error.1
+    (mapcar (lambda (x)
+              (handler-case
+                  (first-elt x)
+                (type-error ()
+                  :type-error)))
+            (list nil
+                  #()
+                  12
+                  :zot))
+  (:type-error
+   :type-error
+   :type-error
+   :type-error))
+
+(deftest setf-first-elt.1
+    (let ((l (list 1 2 3))
+          (s (copy-seq "foobar"))
+          (v (vector :a :b :c)))
+      (setf (first-elt l) -1
+            (first-elt s) #\x
+            (first-elt v) 'zot)
+      (values l s v))
+  (-1 2 3)
+  "xoobar"
+  #(zot :b :c))
+
+(deftest setf-first-elt.error.1
+    (let ((l 'foo))
+      (multiple-value-bind (res err)
+          (ignore-errors (setf (first-elt l) 4))
+        (typep err 'type-error)))
+  t)
+
+(deftest last-elt.1
+    (mapcar #'last-elt
+            (list (list 1 2 3)
+                  (vector :a :b :c)
+                  "FOOBAR"
+                  #*001
+                  #*010))
+  (3 :c #\R 1 0))
+
+(deftest last-elt.error.1
+    (mapcar (lambda (x)
+              (handler-case
+                  (last-elt x)
+                (type-error ()
+                  :type-error)))
+            (list nil
+                  #()
+                  12
+                  :zot
+                  (circular-list 1 2 3)
+                  (list* 1 2 3 (circular-list 4 5))))
+  (:type-error
+   :type-error
+   :type-error
+   :type-error
+   :type-error
+   :type-error))
+
+(deftest setf-last-elt.1
+    (let ((l (list 1 2 3))
+          (s (copy-seq "foobar"))
+          (b (copy-seq #*010101001)))
+      (setf (last-elt l) '???
+            (last-elt s) #\?
+            (last-elt b) 0)
+      (values l s b))
+  (1 2 ???)
+  "fooba?"
+  #*010101000)
+
+(deftest setf-last-elt.error.1
+    (handler-case
+        (setf (last-elt 'foo) 13)
+      (type-error ()
+        :type-error))
+  :type-error)
+
+(deftest starts-with.1
+    (list (starts-with 1 '(1 2 3))
+          (starts-with 1 #(1 2 3))
+          (starts-with #\x "xyz")
+          (starts-with 2 '(1 2 3))
+          (starts-with 3 #(1 2 3))
+          (starts-with 1 1)
+          (starts-with nil nil))
+  (t t t nil nil nil nil))
+
+(deftest starts-with.2
+    (values (starts-with 1 '(-1 2 3) :key '-)
+            (starts-with "foo" '("foo" "bar") :test 'equal)
+            (starts-with "f" '(#\f) :key 'string :test 'equal)
+            (starts-with -1 '(0 1 2) :key #'1+)
+            (starts-with "zot" '("ZOT") :test 'equal))
+  t
+  t
+  t
+  nil
+  nil)
+
+(deftest ends-with.1
+    (list (ends-with 3 '(1 2 3))
+          (ends-with 3 #(1 2 3))
+          (ends-with #\z "xyz")
+          (ends-with 2 '(1 2 3))
+          (ends-with 1 #(1 2 3))
+          (ends-with 1 1)
+          (ends-with nil nil))
+  (t t t nil nil nil nil))
+
+(deftest ends-with.2
+    (values (ends-with 2 '(0 13 1) :key '1+)
+            (ends-with "foo" (vector "bar" "foo") :test 'equal)
+            (ends-with "X" (vector 1 2 #\X) :key 'string :test 'equal)
+            (ends-with "foo" "foo" :test 'equal))
+  t
+  t
+  t
+  nil)
+
+(deftest ends-with.error.1
+    (handler-case
+        (ends-with 3 (circular-list 3 3 3 1 3 3))
+      (type-error ()
+        :type-error))
+  :type-error)
+
+(deftest sequences.passing-improper-lists
+    (macrolet ((signals-error-p (form)
+                 `(handler-case
+                      (progn ,form nil)
+                    (type-error (e)
+                      t)))
+               (cut (fn &rest args)
+                 (with-gensyms (arg)
+                   (print`(lambda (,arg)
+                       (apply ,fn (list ,@(substitute arg '_ args))))))))
+      (let ((circular-list (make-circular-list 5 :initial-element :foo))
+            (dotted-list (list* 'a 'b 'c 'd)))
+        (loop for nth from 0
+              for fn in (list
+                         (cut #'lastcar _)
+                         (cut #'rotate _ 3)
+                         (cut #'rotate _ -3)
+                         (cut #'shuffle _)
+                         (cut #'random-elt _)
+                         (cut #'last-elt _)
+                         (cut #'ends-with :foo _))
+              nconcing
+                 (let ((on-circular-p (signals-error-p (funcall fn circular-list)))
+                       (on-dotted-p (signals-error-p (funcall fn dotted-list))))
+                   (when (or (not on-circular-p) (not on-dotted-p))
+                     (append
+                      (unless on-circular-p
+                        (let ((*print-circle* t))
+                          (list
+                           (format nil
+                                   "No appropriate error signalled when passing ~S to ~Ath entry."
+                                   circular-list nth))))
+                      (unless on-dotted-p
+                        (list
+                         (format nil
+                                 "No appropriate error signalled when passing ~S to ~Ath entry."
+                                 dotted-list nth)))))))))
+  nil)
+
+;;;; IO
+
+(deftest read-stream-content-into-string.1
+    (values (with-input-from-string (stream "foo bar")
+              (read-stream-content-into-string stream))
+            (with-input-from-string (stream "foo bar")
+              (read-stream-content-into-string stream :buffer-size 1))
+            (with-input-from-string (stream "foo bar")
+              (read-stream-content-into-string stream :buffer-size 6))
+            (with-input-from-string (stream "foo bar")
+              (read-stream-content-into-string stream :buffer-size 7)))
+  "foo bar"
+  "foo bar"
+  "foo bar"
+  "foo bar")
+
+(deftest read-stream-content-into-string.2
+    (handler-case
+        (let ((stream (make-broadcast-stream)))
+          (read-stream-content-into-string stream :buffer-size 0))
+      (type-error ()
+        :type-error))
+  :type-error)
+
+#+(or)
+(defvar *octets*
+  (map '(simple-array (unsigned-byte 8) (7)) #'char-code "foo bar"))
+
+#+(or)
+(deftest read-stream-content-into-byte-vector.1
+    (values (with-input-from-byte-vector (stream *octets*)
+              (read-stream-content-into-byte-vector stream))
+            (with-input-from-byte-vector (stream *octets*)
+              (read-stream-content-into-byte-vector stream :initial-size 1))
+            (with-input-from-byte-vector (stream *octets*)
+              (read-stream-content-into-byte-vector stream 'alexandria::%length 6))
+            (with-input-from-byte-vector (stream *octets*)
+              (read-stream-content-into-byte-vector stream 'alexandria::%length 3)))
+  *octets*
+  *octets*
+  *octets*
+  (subseq *octets* 0 3))
+
+(deftest read-stream-content-into-byte-vector.2
+    (handler-case
+        (let ((stream (make-broadcast-stream)))
+          (read-stream-content-into-byte-vector stream :initial-size 0))
+      (type-error ()
+        :type-error))
+  :type-error)
+
+;;;; Macros
+
+(deftest with-unique-names.1
+    (let ((*gensym-counter* 0))
+      (let ((syms (with-unique-names (foo bar quux)
+                    (list foo bar quux))))
+        (list (find-if #'symbol-package syms)
+              (equal '("FOO0" "BAR1" "QUUX2")
+                     (mapcar #'symbol-name syms)))))
+  (nil t))
+
+(deftest with-unique-names.2
+    (let ((*gensym-counter* 0))
+      (let ((syms (with-unique-names ((foo "_foo_") (bar -bar-) (quux #\q))
+                    (list foo bar quux))))
+        (list (find-if #'symbol-package syms)
+              (equal '("_foo_0" "-BAR-1" "q2")
+                     (mapcar #'symbol-name syms)))))
+  (nil t))
+
+(deftest with-unique-names.3
+    (let ((*gensym-counter* 0))
+      (multiple-value-bind (res err)
+          (ignore-errors
+            (eval
+             '(let ((syms
+                     (with-unique-names ((foo "_foo_") (bar -bar-) (quux 42))
+                       (list foo bar quux))))
+               (list (find-if #'symbol-package syms)
+                (equal '("_foo_0" "-BAR-1" "q2")
+                 (mapcar #'symbol-name syms))))))
+        (errorp err)))
+  t)
+
+(deftest once-only.1
+    (macrolet ((cons1.good (x)
+                 (once-only (x)
+                   `(cons ,x ,x)))
+               (cons1.bad (x)
+                 `(cons ,x ,x)))
+      (let ((y 0))
+        (list (cons1.good (incf y))
+              y
+              (cons1.bad (incf y))
+              y)))
+  ((1 . 1) 1 (2 . 3) 3))
+
+(deftest once-only.2
+    (macrolet ((cons1 (x)
+                 (once-only ((y x))
+                   `(cons ,y ,y))))
+      (let ((z 0))
+        (list (cons1 (incf z))
+              z
+              (cons1 (incf z)))))
+  ((1 . 1) 1 (2 . 2)))
+
+(deftest parse-body.1
+    (parse-body '("doc" "body") :documentation t)
+  ("body")
+  nil
+  "doc")
+
+(deftest parse-body.2
+    (parse-body '("body") :documentation t)
+  ("body")
+  nil
+  nil)
+
+(deftest parse-body.3
+    (parse-body '("doc" "body"))
+  ("doc" "body")
+  nil
+  nil)
+
+(deftest parse-body.4
+    (parse-body '((declare (foo)) "doc" (declare (bar)) body) :documentation t)
+  (body)
+  ((declare (foo)) (declare (bar)))
+  "doc")
+
+(deftest parse-body.5
+    (parse-body '((declare (foo)) "doc" (declare (bar)) body))
+  ("doc" (declare (bar)) body)
+  ((declare (foo)))
+  nil)
+
+(deftest parse-body.6
+    (multiple-value-bind (res err)
+        (ignore-errors
+          (parse-body '("foo" "bar" "quux")
+                      :documentation t))
+      (errorp err))
+  t)
+
+;;;; Symbols
+
+(deftest ensure-symbol.1
+    (ensure-symbol :cons :cl)
+  cons
+  :external)
+
+(deftest ensure-symbol.2
+    (ensure-symbol "CONS" :alexandria)
+  cons
+  :inherited)
+
+(deftest ensure-symbol.3
+    (ensure-symbol 'foo :keyword)
+  :foo
+  :external)
+
+(deftest ensure-symbol.4
+    (ensure-symbol #\* :alexandria)
+  *
+  :inherited)
+
+(deftest format-symbol.1
+    (let ((s (format-symbol nil '#:x-~d 13)))
+      (list (symbol-package s)
+            (string= (string '#:x-13) (symbol-name s))))
+  (nil t))
+
+(deftest format-symbol.2
+    (format-symbol :keyword '#:sym-~a (string :bolic))
+  :sym-bolic)
+
+(deftest format-symbol.3
+    (let ((*package* (find-package :cl)))
+      (format-symbol t '#:find-~a (string 'package)))
+  find-package)
+
+(deftest make-keyword.1
+    (list (make-keyword 'zot)
+          (make-keyword "FOO")
+          (make-keyword #\Q))
+  (:zot :foo :q))
+
+(deftest make-gensym-list.1
+    (let ((*gensym-counter* 0))
+      (let ((syms (make-gensym-list 3 "FOO")))
+        (list (find-if 'symbol-package syms)
+              (equal '("FOO0" "FOO1" "FOO2")
+                     (mapcar 'symbol-name syms)))))
+  (nil t))
+
+(deftest make-gensym-list.2
+    (let ((*gensym-counter* 0))
+      (let ((syms (make-gensym-list 3)))
+        (list (find-if 'symbol-package syms)
+              (equal '("G0" "G1" "G2")
+                     (mapcar 'symbol-name syms)))))
+  (nil t))
+
+;;;; Type-system
+
+(deftest of-type.1
+    (locally
+        (declare (notinline of-type))
+    (let ((f (of-type 'string)))
+      (list (funcall f "foo")
+            (funcall f 'bar))))
+  (t nil))
+
+(deftest type=.1
+    (type= 'string 'string)
+  t
+  t)
+
+(deftest type=.2
+    (type= 'list '(or null cons))
+  t
+  t)
+
+(deftest type=.3
+    (type= 'null '(and symbol list))
+  t
+  t)
+
+(deftest type=.4
+    (type= 'string '(satisfies emptyp))
+  nil
+  nil)
+
+(deftest type=.5
+    (type= 'string 'list)
+  nil
+  t)
+
+(macrolet
+    ((test (type numbers)
+       `(deftest ,(format-symbol t '#:cdr5.~a (string type))
+            (let ((numbers ,numbers))
+              (values (mapcar (of-type ',(format-symbol t '#:negative-~a (string type))) numbers)
+                      (mapcar (of-type ',(format-symbol t '#:non-positive-~a (string type))) numbers)
+                      (mapcar (of-type ',(format-symbol t '#:non-negative-~a (string type))) numbers)
+                      (mapcar (of-type ',(format-symbol t '#:positive-~a (string type))) numbers)))
+          (t t t nil nil nil nil)
+          (t t t t nil nil nil)
+          (nil nil nil t t t t)
+          (nil nil nil nil t t t))))
+  (test fixnum       (list most-negative-fixnum       -42      -1     0     1     42      most-positive-fixnum))
+  (test integer      (list (1- most-negative-fixnum)  -42      -1     0     1     42      (1+ most-positive-fixnum)))
+  (test rational     (list (1- most-negative-fixnum)  -42/13   -1     0     1     42/13   (1+ most-positive-fixnum)))
+  (test real         (list most-negative-long-float   -42/13   -1     0     1     42/13   most-positive-long-float))
+  (test float        (list most-negative-short-float  -42.02   -1.0   0.0   1.0   42.02   most-positive-short-float))
+  (test short-float  (list most-negative-short-float  -42.02s0 -1.0s0 0.0s0 1.0s0 42.02s0 most-positive-short-float))
+  (test single-float (list most-negative-single-float -42.02f0 -1.0f0 0.0f0 1.0f0 42.02f0 most-positive-single-float))
+  (test double-float (list most-negative-double-float -42.02d0 -1.0d0 0.0d0 1.0d0 42.02d0 most-positive-double-float))
+  (test long-float   (list most-negative-long-float   -42.02l0 -1.0l0 0.0l0 1.0l0 42.02l0 most-positive-long-float)))
+
+;;;; Bindings
+
+(declaim (notinline opaque))
+(defun opaque (x)
+  x)
+
+(deftest if-let.1
+    (if-let (x (opaque :ok))
+            x
+            :bad)
+  :ok)
+
+(deftest if-let.2
+    (if-let (x (opaque nil))
+            :bad
+            (and (not x) :ok))
+  :ok)
+
+(deftest if-let.3
+    (let ((x 1))
+      (if-let ((x 2)
+               (y x))
+              (+ x y)
+              :oops))
+  3)
+
+(deftest if-let.4
+    (if-let ((x 1)
+             (y nil))
+            :oops
+            (and (not y) x))
+  1)
+
+(deftest if-let.5
+    (if-let (x)
+            :oops
+            (not x))
+  t)
+
+(deftest if-let.error.1
+    (handler-case
+        (eval '(if-let x
+                :oops
+                :oops))
+      (type-error ()
+        :type-error))
+  :type-error)
+
+(deftest when-let.1
+    (when-let (x (opaque :ok))
+      (setf x (cons x x))
+      x)
+  (:ok . :ok))
+
+(deftest when-let.2
+    (when-let ((x 1)
+               (y nil)
+               (z 3))
+      :oops)
+  nil)
+
+(deftest when-let.3
+    (let ((x 1))
+      (when-let ((x 2)
+                 (y x))
+        (+ x y)))
+  3)
+
+(deftest when-let.error.1
+    (handler-case
+        (eval '(when-let x :oops))
+      (type-error ()
+        :type-error))
+  :type-error)
+
+(deftest when-let*.1
+    (let ((x 1))
+      (when-let* ((x 2)
+                  (y x))
+        (+ x y)))
+  4)
+
+(deftest when-let*.2
+    (let ((y 1))
+      (when-let* (x y)
+        (1+ x)))
+  2)
+
+(deftest when-let*.3
+    (when-let* ((x t)
+                (y (consp x))
+                (z (error "OOPS")))
+      t)
+  nil)
+
+(deftest when-let*.error.1
+    (handler-case
+        (eval '(when-let* x :oops))
+      (type-error ()
+        :type-error))
+  :type-error)
+
+(deftest doplist.1
+    (let (keys values)
+      (doplist (k v '(a 1 b 2 c 3) (values t (reverse keys) (reverse values) k v))
+        (push k keys)
+        (push v values)))
+  t
+  (a b c)
+  (1 2 3)
+  nil
+  nil)
+
+(deftest count-permutations.1
+    (values (count-permutations 31 7)
+            (count-permutations 1 1)
+            (count-permutations 2 1)
+            (count-permutations 2 2)
+            (count-permutations 3 2)
+            (count-permutations 3 1))
+  13253058000
+  1
+  2
+  2
+  6
+  3)
+
+(deftest binomial-coefficient.1
+    (alexandria:binomial-coefficient 1239 139)
+  28794902202288970200771694600561826718847179309929858835480006683522184441358211423695124921058123706380656375919763349913245306834194782172712255592710204598527867804110129489943080460154)
+
+;; Exercise bignum case (at least on x86).
+(deftest binomial-coefficient.2
+    (alexandria:binomial-coefficient 2000000000000 20)
+  430998041177272843950422879590338454856322722740402365741730748431530623813012487773080486408378680853987520854296499536311275320016878730999689934464711239072435565454954447356845336730100919970769793030177499999999900000000000)
+
+(deftest copy-stream.1
+    (let ((data "sdkfjhsakfh weior763495ewofhsdfk sdfadlkfjhsadf woif sdlkjfhslkdfh sdklfjh"))
+      (values (equal data
+                     (with-input-from-string (in data)
+                       (with-output-to-string (out)
+                         (alexandria:copy-stream in out))))
+              (equal (subseq data 10 20)
+                     (with-input-from-string (in data)
+                       (with-output-to-string (out)
+                         (alexandria:copy-stream in out :start 10 :end 20))))
+              (equal (subseq data 10)
+                     (with-input-from-string (in data)
+                       (with-output-to-string (out)
+                         (alexandria:copy-stream in out :start 10))))
+              (equal (subseq data 0 20)
+                     (with-input-from-string (in data)
+                       (with-output-to-string (out)
+                         (alexandria:copy-stream in out :end 20))))))
+  t
+  t
+  t
+  t)
+
+(deftest extremum.1
+    (let ((n 0))
+      (dotimes (i 10)
+       (let ((data (shuffle (coerce (iota 10000 :start i) 'vector)))
+             (ok t))
+         (unless (eql i (extremum data #'<))
+           (setf ok nil))
+         (unless (eql i (extremum (coerce data 'list) #'<))
+           (setf ok nil))
+         (unless (eql (+ 9999 i) (extremum data #'>))
+           (setf ok nil))
+         (unless (eql (+ 9999 i) (extremum (coerce  data 'list) #'>))
+           (setf ok nil))
+         (when ok
+           (incf n))))
+      (when (eql 10 (extremum #(100 1 10 1000) #'> :start 1 :end 3))
+        (incf n))
+      (when (eql -1000 (extremum #(100 1 10 -1000) #'> :key 'abs))
+        (incf n))
+      (when (eq nil (extremum "" (lambda (a b) (error "wtf? ~S, ~S" a b))))
+        (incf n))
+      n)
+  13)
+
+(deftest starts-with-subseq.string
+    (starts-with-subseq "f" "foo" :return-suffix t)
+  t
+  "oo")
+
+(deftest starts-with-subseq.vector
+    (starts-with-subseq #(1) #(1 2 3) :return-suffix t)
+  t
+  #(2 3))
+
+(deftest starts-with-subseq.list
+    (starts-with-subseq '(1) '(1 2 3) :return-suffix t)
+  t
+  (2 3))
+
+(deftest starts-with-subseq.start1
+    (starts-with-subseq "foo" "oop" :start1 1)
+  t
+  nil)
+
+(deftest starts-with-subseq.start2
+    (starts-with-subseq "foo" "xfoop" :start2 1)
+  t
+  nil)
+
+(deftest format-symbol.print-case-bound
+    (let ((upper (intern "FOO-BAR"))
+          (lower (intern "foo-bar"))
+          (*print-escape* nil))
+      (values
+       (let ((*print-case* :downcase))
+         (and (eq upper (format-symbol t "~A" upper))
+               (eq lower (format-symbol t "~A" lower))))
+       (let ((*print-case* :upcase))
+         (and (eq upper (format-symbol t "~A" upper))
+               (eq lower (format-symbol t "~A" lower))))
+       (let ((*print-case* :capitalize))
+         (and (eq upper (format-symbol t "~A" upper))
+              (eq lower (format-symbol t "~A" lower))))))
+  t
+  t
+  t)
+
+(deftest iota.fp-start-and-complex-integer-step
+    (equal '(#C(0.0 0.0) #C(0.0 2.0) #C(0.0 4.0))
+           (iota 3 :start 0.0 :step #C(0 2)))
+  t)
+
+(deftest parse-ordinary-lambda-list.1
+    (multiple-value-bind (req opt rest keys allowp aux keyp)
+        (parse-ordinary-lambda-list '(a b c
+                                      &optional o1 (o2 42) (o3 42 o3-supplied?)
+                                      &key (k1) ((:key k2)) (k3 42 k3-supplied?))
+                                    :normalize t)
+      (and (equal '(a b c) req)
+           (equal '((o1 nil nil)
+                    (o2 42 nil)
+                    (o3 42 o3-supplied?))
+                  opt)
+           (equal '(((:k1 k1) nil nil)
+                    ((:key k2) nil nil)
+                    ((:k3 k3) 42 k3-supplied?))
+                  keys)
+           (not allowp)
+           (not aux)
+           (eq t keyp)))
+  t)
diff --git a/deps/alexandria/types.lisp b/deps/alexandria/types.lisp
new file mode 100644 (file)
index 0000000..1942d0e
--- /dev/null
@@ -0,0 +1,137 @@
+(in-package :alexandria)
+
+(deftype array-index (&optional (length (1- array-dimension-limit)))
+  "Type designator for an index into array of LENGTH: an integer between
+0 (inclusive) and LENGTH (exclusive). LENGTH defaults to one less than
+ARRAY-DIMENSION-LIMIT."
+  `(integer 0 (,length)))
+
+(deftype array-length (&optional (length (1- array-dimension-limit)))
+  "Type designator for a dimension of an array of LENGTH: an integer between
+0 (inclusive) and LENGTH (inclusive). LENGTH defaults to one less than
+ARRAY-DIMENSION-LIMIT."
+  `(integer 0 ,length))
+
+;; This MACROLET will generate most of CDR5 (http://cdr.eurolisp.org/document/5/)
+;; except the RATIO related definitions and ARRAY-INDEX.
+(macrolet
+    ((frob (type &optional (base-type type))
+       (let ((subtype-names (list))
+             (predicate-names (list)))
+         (flet ((make-subtype-name (format-control)
+                  (let ((result (format-symbol :alexandria format-control
+                                               (symbol-name type))))
+                    (push result subtype-names)
+                    result))
+                (make-predicate-name (sybtype-name)
+                  (let ((result (format-symbol :alexandria '#:~A-p
+                                               (symbol-name sybtype-name))))
+                    (push result predicate-names)
+                    result))
+               (make-docstring (range-beg range-end range-type)
+                 (let ((inf (ecase range-type (:negative "-inf") (:positive "+inf"))))
+                   (format nil "Type specifier denoting the ~(~A~) range from ~A to ~A."
+                           type
+                           (if (equal range-beg ''*) inf (ensure-car range-beg))
+                           (if (equal range-end ''*) inf (ensure-car range-end))))))
+           (let* ((negative-name     (make-subtype-name '#:negative-~a))
+                  (non-positive-name (make-subtype-name '#:non-positive-~a))
+                  (non-negative-name (make-subtype-name '#:non-negative-~a))
+                  (positive-name     (make-subtype-name '#:positive-~a))
+                  (negative-p-name     (make-predicate-name negative-name))
+                  (non-positive-p-name (make-predicate-name non-positive-name))
+                  (non-negative-p-name (make-predicate-name non-negative-name))
+                  (positive-p-name     (make-predicate-name positive-name))
+                  (negative-extremum)
+                  (positive-extremum)
+                  (below-zero)
+                  (above-zero)
+                  (zero))
+             (setf (values negative-extremum below-zero
+                           above-zero positive-extremum zero)
+                   (ecase type
+                     (fixnum       (values 'most-negative-fixnum -1 1 'most-positive-fixnum 0))
+                     (integer      (values ''* -1       1        ''* 0))
+                     (rational     (values ''* '(0)     '(0)     ''* 0))
+                     (real         (values ''* '(0)     '(0)     ''* 0))
+                     (float        (values ''* '(0.0E0) '(0.0E0) ''* 0.0E0))
+                     (short-float  (values ''* '(0.0S0) '(0.0S0) ''* 0.0S0))
+                     (single-float (values ''* '(0.0F0) '(0.0F0) ''* 0.0F0))
+                     (double-float (values ''* '(0.0D0) '(0.0D0) ''* 0.0D0))
+                     (long-float   (values ''* '(0.0L0) '(0.0L0) ''* 0.0L0))))
+             `(progn
+                (deftype ,negative-name ()
+                 ,(make-docstring negative-extremum below-zero :negative)
+                 `(,',base-type ,,negative-extremum ,',below-zero))
+
+                (deftype ,non-positive-name ()
+                 ,(make-docstring negative-extremum zero :negative)
+                 `(,',base-type ,,negative-extremum ,',zero))
+
+                (deftype ,non-negative-name ()
+                 ,(make-docstring zero positive-extremum :positive)
+                 `(,',base-type ,',zero ,,positive-extremum))
+
+                (deftype ,positive-name ()
+                 ,(make-docstring above-zero positive-extremum :positive)
+                 `(,',base-type ,',above-zero ,,positive-extremum))
+
+                (declaim (inline ,@predicate-names))
+
+                (defun ,negative-p-name (n)
+                  (and (typep n ',type)
+                       (< n ,zero)))
+
+                (defun ,non-positive-p-name (n)
+                  (and (typep n ',type)
+                       (<= n ,zero)))
+
+                (defun ,non-negative-p-name (n)
+                  (and (typep n ',type)
+                       (<= ,zero n)))
+
+                (defun ,positive-p-name (n)
+                  (and (typep n ',type)
+                       (< ,zero n)))))))))
+  (frob fixnum integer)
+  (frob integer)
+  (frob rational)
+  (frob real)
+  (frob float)
+  (frob short-float)
+  (frob single-float)
+  (frob double-float)
+  (frob long-float))
+
+(defun of-type (type)
+  "Returns a function of one argument, which returns true when its argument is
+of TYPE."
+  (lambda (thing) (typep thing type)))
+
+(define-compiler-macro of-type (&whole form type &environment env)
+  ;; This can yeild a big benefit, but no point inlining the function
+  ;; all over the place if TYPE is not constant.
+  (if (constantp type env)
+      (with-gensyms (thing)
+        `(lambda (,thing)
+           (typep ,thing ,type)))
+      form))
+
+(declaim (inline type=))
+(defun type= (type1 type2)
+  "Returns a primary value of T is TYPE1 and TYPE2 are the same type,
+and a secondary value that is true is the type equality could be reliably
+determined: primary value of NIL and secondary value of T indicates that the
+types are not equivalent."
+  (multiple-value-bind (sub ok) (subtypep type1 type2)
+    (cond ((and ok sub)
+           (subtypep type2 type1))
+          (ok
+           (values nil ok))
+          (t
+           (multiple-value-bind (sub ok) (subtypep type2 type1)
+             (declare (ignore sub))
+             (values nil ok))))))
+
+(define-modify-macro coercef (type-spec) coerce
+  "Modify-macro for COERCE.")
diff --git a/deps/bordeaux-threads/CONTRIBUTORS b/deps/bordeaux-threads/CONTRIBUTORS
new file mode 100644 (file)
index 0000000..cb7ff3c
--- /dev/null
@@ -0,0 +1,17 @@
+-*- outline -*-
+
+Based on original Bordeaux-MP spec by Dan Barlow <dan@telent.net>
+
+Contributors:
+
+* Attila Lendvai <attila.lendvai@gmail.com>
+  - better handling of unsupported Lisps
+* Vladimir Sekissov <svg@surnet.ru>
+  - fixes for CMUCL implementation
+* Pierre Thierry <nowhere.man@levallois.eu.org>
+ - added license information
+* Stelian Ionescu <sionescu@cddr.org>
+  - finished conversion from generic functions
+  - enabled running thread-safe code in unthreaded lisps
+* Douglas Crosher <dtc@scieneer.com>
+  - added Scieneer Common Lisp support
diff --git a/deps/bordeaux-threads/LICENSE b/deps/bordeaux-threads/LICENSE
new file mode 100644 (file)
index 0000000..3ce400f
--- /dev/null
@@ -0,0 +1,20 @@
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/deps/bordeaux-threads/README b/deps/bordeaux-threads/README
new file mode 100644 (file)
index 0000000..1531578
--- /dev/null
@@ -0,0 +1,2 @@
+You can find API documentation on the project's wiki:
+       http://trac.common-lisp.net/bordeaux-threads/wiki/ApiDocumentation
diff --git a/deps/bordeaux-threads/bordeaux-threads-test.asd b/deps/bordeaux-threads/bordeaux-threads-test.asd
new file mode 100644 (file)
index 0000000..ab73313
--- /dev/null
@@ -0,0 +1,17 @@
+#|
+Copyright 2006,2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(asdf:defsystem :bordeaux-threads-test
+  :depends-on (:bordeaux-threads :fiveam)
+  :version #.(with-open-file
+                 (vers (merge-pathnames "version.lisp-expr" *load-truename*))
+               (read vers))
+  :components ((:module "test"
+                :components ((:file "bordeaux-threads-test"))))
+  :in-order-to ((asdf:test-op (asdf:load-op bordeaux-threads-test)))
+  :perform (asdf:test-op :after (op c)
+             (describe (funcall (intern (string '#:run!) :fiveam)
+                                :bordeaux-threads))))
diff --git a/deps/bordeaux-threads/bordeaux-threads.asd b/deps/bordeaux-threads/bordeaux-threads.asd
new file mode 100644 (file)
index 0000000..655b68d
--- /dev/null
@@ -0,0 +1,61 @@
+;;;; -*- Mode: Lisp; indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006,2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  #+allegro (require :smputil)
+  #+corman  (require :threads))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  #+(or armedbear
+        (and allegro multiprocessing)
+        (and clisp mt)
+        (and openmcl openmcl-native-threads)
+        (and cmu mp)
+        corman
+        (and ecl threads)
+        mkcl
+        lispworks
+        (and digitool ccl-5.1)
+        (and sbcl sb-thread)
+        scl)
+  (pushnew :thread-support *features*))
+
+(asdf:defsystem :bordeaux-threads
+  :author "Greg Pfeil <greg@technomadic.org>"
+  :licence "MIT"
+  :description "Bordeaux Threads makes writing portable multi-threaded apps simple"
+  :version #.(with-open-file
+                 (vers (merge-pathnames "version.lisp-expr" *load-truename*))
+               (read vers))
+  :depends-on (:alexandria)
+  :components ((:module "src"
+                :serial t
+                :components
+                ((:file "pkgdcl")
+                 (:file "bordeaux-threads")
+                 (:file #+(and thread-support armedbear) "impl-abcl"
+                        #+(and thread-support allegro)   "impl-allegro"
+                        #+(and thread-support clisp)     "impl-clisp"
+                        #+(and thread-support openmcl)   "impl-clozure"
+                        #+(and thread-support cmu)       "impl-cmucl"
+                        #+(and thread-support corman)    "impl-corman"
+                        #+(and thread-support ecl)       "impl-ecl"
+                        #+(and thread-support mkcl)      "impl-mkcl"
+                        #+(and thread-support lispworks) "impl-lispworks"
+                        #+(and thread-support digitool)  "impl-mcl"
+                        #+(and thread-support sbcl)      "impl-sbcl"
+                        #+(and thread-support scl)       "impl-scl"
+                        #-thread-support                 "impl-null")
+                 #+(and thread-support lispworks (not lispworks6))
+                 (:file "impl-lispworks-condition-variables")
+                 #+(and thread-support digitool)
+                 (:file "condition-variables")
+                 (:file "default-implementations"))))
+  :in-order-to ((asdf:test-op (asdf:load-op bordeaux-threads-test)))
+  :perform (asdf:test-op :after (op c)
+             (asdf:oos 'asdf:test-op :bordeaux-threads-test)))
diff --git a/deps/bordeaux-threads/src/bordeaux-threads.lisp b/deps/bordeaux-threads/src/bordeaux-threads.lisp
new file mode 100644 (file)
index 0000000..d862d09
--- /dev/null
@@ -0,0 +1,110 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+(defvar *supports-threads-p* nil
+  "This should be set to T if the running instance has thread support.")
+
+(defun mark-supported ()
+  (setf *supports-threads-p* t)
+  (pushnew :bordeaux-threads *features*))
+
+(define-condition bordeaux-mp-condition (error)
+  ((message :initarg :message :reader message))
+  (:report (lambda (condition stream)
+             (format stream (message condition)))))
+
+(defgeneric make-threading-support-error ()
+  (:documentation "Creates a BORDEAUX-THREADS condition which specifies
+  whether there is no BORDEAUX-THREADS support for the implementation, no
+  threads enabled for the system, or no support for a particular
+  function.")
+  (:method ()
+    (make-condition
+     'bordeaux-mp-condition
+     :message (if *supports-threads-p*
+                  "There is no support for this method on this implementation."
+                  "There is no thread support in this instance."))))
+
+#-sbcl
+(define-condition timeout (serious-condition)
+  ((length :initform nil
+             :initarg :length
+             :reader timeout-length))
+  (:report (lambda (c s)
+             (if (timeout-length c)
+                 (format s "A timeout set to ~A seconds occurred."
+                         (timeout-length c))
+                 (format s "A timeout occurred.")))))
+
+
+;;; Thread Creation
+
+;;; See default-implementations.lisp for MAKE-THREAD.
+
+;; Forms are evaluated in the new thread or in the calling thread?
+(defvar *default-special-bindings* nil
+  "This variable holds an alist associating special variable symbols
+  to forms to evaluate. Special variables named in this list will
+  be locally bound in the new thread before it begins executing user code.
+
+  This variable may be rebound around calls to MAKE-THREAD to
+  add/alter default bindings. The effect of mutating this list is
+  undefined, but earlier forms take precedence over later forms for
+  the same symbol, so defaults may be overridden by consing to the
+  head of the list.")
+
+(defmacro defbindings (name docstring &body initforms)
+  (check-type docstring string)
+  `(defparameter ,name
+     (list
+      ,@(loop for (special form) in initforms
+              collect `(cons ',special ',form)))
+     ,docstring))
+
+;; Forms are evaluated in the new thread or in the calling thread?
+(defbindings *standard-io-bindings*
+  "Standard bindings of printer/reader control variables as per CL:WITH-STANDARD-IO-SYNTAX."
+  (*package*                   (find-package :common-lisp-user))
+  (*print-array*               t)
+  (*print-base*                10)
+  (*print-case*                :upcase)
+  (*print-circle*              nil)
+  (*print-escape*              t)
+  (*print-gensym*              t)
+  (*print-length*              nil)
+  (*print-level*               nil)
+  (*print-lines*               nil)
+  (*print-miser-width*         nil)
+  (*print-pprint-dispatch*     (copy-pprint-dispatch nil))
+  (*print-pretty*              nil)
+  (*print-radix*               nil)
+  (*print-readably*            t)
+  (*print-right-margin*        nil)
+  (*read-base*                 10)
+  (*read-default-float-format* 'single-float)
+  (*read-eval*                 t)
+  (*read-suppress*             nil)
+  (*readtable*                 (copy-readtable nil)))
+
+(defun binding-default-specials (function special-bindings)
+  "Return a closure that binds the symbols in SPECIAL-BINDINGS and calls
+FUNCTION."
+  (let ((specials (remove-duplicates special-bindings :from-end t :key #'car)))
+    (lambda ()
+      (progv (mapcar #'car specials)
+          (loop for (nil . form) in specials collect (eval form))
+        (funcall function)))))
+
+;;; FIXME: This test won't work if CURRENT-THREAD
+;;;        conses a new object each time
+(defun signal-error-if-current-thread (thread)
+  (when (eq thread (current-thread))
+    (error 'bordeaux-mp-condition
+           :message "Cannot destroy the current thread")))
diff --git a/deps/bordeaux-threads/src/condition-variables.lisp b/deps/bordeaux-threads/src/condition-variables.lisp
new file mode 100644 (file)
index 0000000..99b7356
--- /dev/null
@@ -0,0 +1,34 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+;;; This file provides a portable implementation of condition
+;;; variables (given a working WITH-LOCK-HELD and THREAD-YIELD), and
+;;; should be used if there is no condition variable implementation in
+;;; the host Lisp.
+
+(defstruct condition-var
+  name
+  lock
+  active)
+
+(defun condition-wait (condition-variable lock)
+  (check-type condition-variable condition-var)
+  (setf (condition-var-active condition-variable) nil)
+  (release-lock lock)
+  (do ()
+      ((when (condition-var-active condition-variable)
+         (acquire-lock lock)
+         t))
+    (thread-yield)))
+
+(defun condition-notify (condition-variable)
+  (check-type condition-variable condition-var)
+  (with-lock-held ((condition-var-lock condition-variable))
+    (setf (condition-var-active condition-variable) t)))
diff --git a/deps/bordeaux-threads/src/default-implementations.lisp b/deps/bordeaux-threads/src/default-implementations.lisp
new file mode 100644 (file)
index 0000000..c12dfbe
--- /dev/null
@@ -0,0 +1,321 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+(in-package #:bordeaux-threads)
+
+;;; Helper macros
+
+(defmacro defdfun (name args doc &body body)
+  `(eval-when (:compile-toplevel :load-toplevel :execute)
+     (unless (fboundp ',name)
+       (defun ,name ,args ,@body))
+     (setf (documentation ',name 'function)
+           (or (documentation ',name 'function) ,doc))))
+
+(defmacro defdmacro (name args doc &body body)
+  `(eval-when (:compile-toplevel :load-toplevel :execute)
+     (unless (fboundp ',name)
+       (defmacro ,name ,args ,@body))
+     (setf (documentation ',name 'function)
+           (or (documentation ',name 'function) ,doc))))
+
+;;; Thread Creation
+
+(defdfun start-multiprocessing ()
+  "If the host implementation uses user-level threads, start the
+scheduler and multiprocessing, otherwise do nothing.
+It is safe to call repeatedly."
+  nil)
+
+(defdfun make-thread (function &key name
+                      (initial-bindings *default-special-bindings*))
+  "Creates and returns a thread named NAME, which will call the
+  function FUNCTION with no arguments: when FUNCTION returns, the
+  thread terminates. NAME defaults to \"Anonymous thread\" if unsupplied.
+
+  On systems that do not support multi-threading, MAKE-THREAD will
+  signal an error.
+
+  The interaction between threads and dynamic variables is in some
+  cases complex, and depends on whether the variable has only a global
+  binding (as established by e.g. DEFVAR/DEFPARAMETER/top-level SETQ)
+  or has been bound locally (e.g. with LET or LET*) in the calling
+  thread.
+
+  - Global bindings are shared between threads: the initial value of a
+    global variable in the new thread will be the same as in the
+    parent, and an assignment to such a variable in any thread will be
+    visible to all threads in which the global binding is visible.
+
+  - Local bindings, such as the ones introduced by INITIAL-BINDINGS,
+    are local to the thread they are introduced in, except that
+
+  - Local bindings in the the caller of MAKE-THREAD may or may not be
+    shared with the new thread that it creates: this is
+    implementation-defined. Portable code should not depend on
+    particular behaviour in this case, nor should it assign to such
+    variables without first rebinding them in the new thread."
+  (%make-thread (binding-default-specials function initial-bindings)
+                (or name "Anonymous thread")))
+
+(defdfun %make-thread (function name)
+  "The actual implementation-dependent function that creates threads."
+  (declare (ignore function name))
+  (error (make-threading-support-error)))
+
+(defdfun current-thread ()
+  "Returns the thread object for the calling
+  thread. This is the same kind of object as would be returned by
+  MAKE-THREAD."
+  nil)
+
+(defdfun threadp (object)
+  "Returns true if object is a thread, otherwise NIL."
+  (declare (ignore object))
+  nil)
+
+(defdfun thread-name (thread)
+  "Returns the name of the thread, as supplied to MAKE-THREAD."
+  (declare (ignore thread))
+  "Main thread")
+
+;;; Resource contention: locks and recursive locks
+
+(defdfun make-lock (&optional name)
+  "Creates a lock (a mutex) whose name is NAME. If the system does not
+  support multiple threads this will still return some object, but it
+  may not be used for very much."
+  ;; In CLIM-SYS this is a freshly consed list (NIL). I don't know if
+  ;; there's some good reason it should be said structure or that it
+  ;; be freshly consed - EQ comparison of locks?
+  (declare (ignore name))
+  (list nil))
+
+(defdfun acquire-lock (lock &optional wait-p)
+  "Acquire the lock LOCK for the calling thread.
+  WAIT-P governs what happens if the lock is not available: if WAIT-P
+  is true, the calling thread will wait until the lock is available
+  and then acquire it; if WAIT-P is NIL, ACQUIRE-LOCK will return
+  immediately. ACQUIRE-LOCK returns true if the lock was acquired and
+  NIL otherwise.
+
+  This specification does not define what happens if a thread
+  attempts to acquire a lock that it already holds. For applications
+  that require locks to be safe when acquired recursively, see instead
+  MAKE-RECURSIVE-LOCK and friends."
+  (declare (ignore lock wait-p))
+  t)
+
+(defdfun release-lock (lock)
+  "Release LOCK. It is an error to call this unless
+  the lock has previously been acquired (and not released) by the same
+  thread. If other threads are waiting for the lock, the
+  ACQUIRE-LOCK call in one of them will now be able to continue.
+
+  This function has no interesting return value."
+  (declare (ignore lock))
+  (values))
+
+(defdmacro with-lock-held ((place) &body body)
+  "Evaluates BODY with the lock named by PLACE, the value of which
+  is a lock created by MAKE-LOCK. Before the forms in BODY are
+  evaluated, the lock is acquired as if by using ACQUIRE-LOCK. After the
+  forms in BODY have been evaluated, or if a non-local control transfer
+  is caused (e.g. by THROW or SIGNAL), the lock is released as if by
+  RELEASE-LOCK.
+
+  Note that if the debugger is entered, it is unspecified whether the
+  lock is released at debugger entry or at debugger exit when execution
+  is restarted."
+  `(when (acquire-lock ,place t)
+     (unwind-protect
+          (locally ,@body)
+       (release-lock ,place))))
+
+(defdfun make-recursive-lock (&optional name)
+  "Create and return a recursive lock whose name is NAME. A recursive
+  lock differs from an ordinary lock in that a thread that already
+  holds the recursive lock can acquire it again without blocking. The
+  thread must then release the lock twice before it becomes available
+  for another thread."
+  (declare (ignore name))
+  (list nil))
+
+(defdfun acquire-recursive-lock (lock)
+  "As for ACQUIRE-LOCK, but for recursive locks."
+  (declare (ignore lock))
+  t)
+
+(defdfun release-recursive-lock (lock)
+  "Release the recursive LOCK. The lock will only
+  become free after as many Release operations as there have been
+  Acquire operations. See RELEASE-LOCK for other information."
+  (declare (ignore lock))
+  (values))
+
+(defdmacro with-recursive-lock-held ((place &key timeout) &body body)
+  "Evaluates BODY with the recursive lock named by PLACE, which is a
+reference to a recursive lock created by MAKE-RECURSIVE-LOCK. See
+WITH-LOCK-HELD etc etc"
+  (declare (ignore timeout))
+  `(when (acquire-recursive-lock ,place)
+     (unwind-protect
+          (locally ,@body)
+       (release-recursive-lock ,place))))
+
+;;; Resource contention: condition variables
+
+;;; A condition variable provides a mechanism for threads to put
+;;; themselves to sleep while waiting for the state of something to
+;;; change, then to be subsequently woken by another thread which has
+;;; changed the state.
+;;;
+;;; A condition variable must be used in conjunction with a lock to
+;;; protect access to the state of the object of interest. The
+;;; procedure is as follows:
+;;;
+;;; Suppose two threads A and B, and some kind of notional event
+;;; channel C. A is consuming events in C, and B is producing them.
+;;; CV is a condition-variable
+;;;
+;;; 1) A acquires the lock that safeguards access to C
+;;; 2) A threads and removes all events that are available in C
+;;; 3) When C is empty, A calls CONDITION-WAIT, which atomically
+;;;    releases the lock and puts A to sleep on CV
+;;; 4) Wait to be notified; CONDITION-WAIT will acquire the lock again
+;;;    before returning
+;;; 5) Loop back to step 2, for as long as threading should continue
+;;;
+;;; When B generates an event E, it
+;;; 1) acquires the lock guarding C
+;;; 2) adds E to the channel
+;;; 3) calls CONDITION-NOTIFY on CV to wake any sleeping thread
+;;; 4) releases the lock
+;;;
+;;; To avoid the "lost wakeup" problem, the implementation must
+;;; guarantee that CONDITION-WAIT in thread A atomically releases the
+;;; lock and sleeps. If this is not guaranteed there is the
+;;; possibility that thread B can add an event and call
+;;; CONDITION-NOTIFY between the lock release and the sleep - in this
+;;; case the notify call would not see A, which would be left sleeping
+;;; despite there being an event available.
+
+(defdfun thread-yield ()
+  "Allows other threads to run. It may be necessary or desirable to
+  call this periodically in some implementations; others may schedule
+  threads automatically. On systems that do not support
+  multi-threading, this does nothing."
+  (values))
+
+(defdfun make-condition-variable (&key name)
+  "Returns a new condition-variable object for use
+  with CONDITION-WAIT and CONDITION-NOTIFY."
+  (declare (ignore name))
+  nil)
+
+(defdfun condition-wait (condition-variable lock)
+  "Atomically release LOCK and enqueue the calling
+  thread waiting for CONDITION-VARIABLE. The thread will resume when
+  another thread has notified it using CONDITION-NOTIFY; it may also
+  resume if interrupted by some external event or in other
+  implementation-dependent circumstances: the caller must always test
+  on waking that there is threading to be done, instead of assuming
+  that it can go ahead.
+
+  However and for whatever reason the thread is resumed, the system
+  always reacquires LOCK before returning to the caller. It is an
+  error to call this unless from the thread that holds LOCK.
+
+  In an implementation that does not support multiple threads, this
+  function signals an error."
+  (declare (ignore condition-variable lock))
+  (error (make-threading-support-error)))
+
+(defdfun condition-notify (condition-variable)
+  "Notify at least one of the threads waiting for
+  CONDITION-VARIABLE. It is implementation-dependent whether one or
+  more than one (and possibly all) threads are woken, but if the
+  implementation is capable of waking only a single thread (not all
+  are) this is probably preferable for efficiency reasons. The order
+  of wakeup is unspecified and does not necessarily relate to the
+  order that the threads went to sleep in.
+
+  CONDITION-NOTIFY has no useful return value. In an implementation
+  that does not support multiple threads, it has no effect."
+  (declare (ignore condition-variable))
+  (values))
+
+;;; Timeouts
+
+(defdmacro with-timeout ((timeout) &body body)
+  "Execute `BODY' and signal a condition of type TIMEOUT if the execution of
+BODY does not complete within `TIMEOUT' seconds. On implementations which do not
+support WITH-TIMEOUT natively and don't support threads either it has no effect."
+  (declare (ignorable timeout))
+  #+thread-support
+  (let ((ok-tag (gensym "OK"))
+        (timeout-tag (gensym "TIMEOUT"))
+        (caller (gensym "CALLER"))
+        (sleeper (gensym "SLEEPER")))
+    (once-only (timeout)
+      `(let (,sleeper)
+         (multiple-value-prog1
+             (catch ',ok-tag
+               (catch ',timeout-tag
+                 (let ((,caller (current-thread)))
+                   (setf ,sleeper
+                         (make-thread #'(lambda ()
+                                          (sleep ,timeout)
+                                          (interrupt-thread ,caller
+                                                            #'(lambda ()
+                                                                (ignore-errors
+                                                                  (throw ',timeout-tag nil)))))
+                                      :name (format nil "WITH-TIMEOUT thread serving: ~S."
+                                                    (thread-name ,caller))))
+                   (throw ',ok-tag (progn ,@body))))
+               (error 'timeout :length ,timeout))
+           (when (thread-alive-p ,sleeper)
+             (destroy-thread ,sleeper))))))
+  #-thread-support
+  `(progn
+     ,@body))
+
+;;; Introspection/debugging
+
+;;; The following functions may be provided for debugging purposes,
+;;; but are not advised to be called from normal user code.
+
+(defdfun all-threads ()
+  "Returns a sequence of all of the threads. This may not
+  be freshly-allocated, so the caller should not modify it."
+  (error (make-threading-support-error)))
+
+(defdfun interrupt-thread (thread function)
+  "Interrupt THREAD and cause it to evaluate FUNCTION
+  before continuing with the interrupted path of execution. This may
+  not be a good idea if THREAD is holding locks or doing anything
+  important. On systems that do not support multiple threads, this
+  function signals an error."
+  (declare (ignore thread function))
+  (error (make-threading-support-error)))
+
+(defdfun destroy-thread (thread)
+  "Terminates the thread THREAD, which is an object
+  as returned by MAKE-THREAD. This should be used with caution: it is
+  implementation-defined whether the thread runs cleanup forms or
+  releases its locks first.
+
+  Destroying the calling thread is an error."
+  (declare (ignore thread))
+  (error (make-threading-support-error)))
+
+(defdfun thread-alive-p (thread)
+  "Returns true if THREAD is alive, that is, if
+  DESTROY-THREAD has not been called on it."
+  (declare (ignore thread))
+  (error (make-threading-support-error)))
+
+(defdfun join-thread (thread)
+  "Wait until THREAD terminates. If THREAD
+  has already terminated, return immediately."
+  (declare (ignore thread))
+  (error (make-threading-support-error)))
diff --git a/deps/bordeaux-threads/src/impl-abcl.lisp b/deps/bordeaux-threads/src/impl-abcl.lisp
new file mode 100644 (file)
index 0000000..c904e7e
--- /dev/null
@@ -0,0 +1,132 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Reimplemented with java.util.concurrent.locks.ReentrantLock by Mark Evenson 2011.
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+;;; the implementation of the Armed Bear thread interface can be found in
+;;; src/org/armedbear/lisp/LispThread.java
+
+(deftype thread ()
+  'threads:thread)
+
+;;; Thread Creation
+
+(defun %make-thread (function name)
+  (threads:make-thread function :name name))
+
+(defun current-thread ()
+  (threads:current-thread))
+
+(defun thread-name (thread)
+  (threads:thread-name thread))
+
+(defun threadp (object)
+  (typep object 'thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defstruct mutex name lock)
+(defstruct (mutex-recursive (:include mutex)))
+
+;; Making methods constants in this manner avoids the runtime expense of
+;; introspection involved in JCALL with string arguments.
+(defconstant +lock+ 
+  (jmethod "java.util.concurrent.locks.ReentrantLock" "lock"))
+(defconstant +try-lock+ 
+  (jmethod "java.util.concurrent.locks.ReentrantLock" "tryLock"))
+(defconstant +is-held-by-current-thread+ 
+  (jmethod "java.util.concurrent.locks.ReentrantLock" "isHeldByCurrentThread"))
+(defconstant +unlock+ 
+  (jmethod "java.util.concurrent.locks.ReentrantLock" "unlock"))
+(defconstant +get-hold-count+ 
+  (jmethod "java.util.concurrent.locks.ReentrantLock" "getHoldCount"))
+
+(defun make-lock (&optional name)
+  (make-mutex 
+   :name (or name "Anonymous lock")
+   :lock (jnew "java.util.concurrent.locks.ReentrantLock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (check-type lock mutex)
+  (when (jcall +is-held-by-current-thread+ (mutex-lock lock))
+    (error "Non-recursive lock being reacquired by owner."))
+  (cond
+    (wait-p
+     (jcall +lock+ (mutex-lock lock))
+     t)
+    (t (jcall +try-lock+ (mutex-lock lock)))))
+
+(defun release-lock (lock)
+  (check-type lock mutex)
+  (unless (jcall +is-held-by-current-thread+ (mutex-lock lock))
+    (error "Attempt to release lock not held by calling thread."))
+  (jcall +unlock+ (mutex-lock lock))
+  (values))
+
+(defun make-recursive-lock (&optional name)
+  (make-mutex-recursive
+   :name (or name "Anonymous lock")
+   :lock (jnew "java.util.concurrent.locks.ReentrantLock")))
+
+(defun acquire-recursive-lock (lock &optional (wait-p t))
+  (check-type lock mutex-recursive)
+  (cond
+    (wait-p
+     (jcall +lock+ (mutex-recursive-lock lock))
+     t)
+    (t (jcall +try-lock+ (mutex-recursive-lock lock)))))
+
+(defun release-recursive-lock (lock)
+  (check-type lock mutex-recursive)
+  (unless (jcall +is-held-by-current-thread+ (mutex-lock lock))
+    (error "Attempt to release lock not held by calling thread."))
+  (jcall +unlock+ (mutex-lock lock))
+  (values))
+
+;;; Resource contention: condition variables
+
+(defun thread-yield ()
+  (sleep 0.01))
+
+(defstruct condition-variable
+  (name "Anonymous condition variable"))
+
+(defun condition-wait (condition lock)
+  (threads:synchronized-on condition
+    (release-lock lock)
+    (threads:object-wait condition))
+  (acquire-lock lock))
+
+(defun condition-notify (condition)
+  (threads:synchronized-on condition
+     (threads:object-notify condition)))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  (let ((threads ()))
+    (threads:mapcar-threads (lambda (thread)
+                             (push thread threads)))
+    (reverse threads)))
+
+(defun interrupt-thread (thread function &rest args)
+  (apply #'threads:interrupt-thread thread function args))
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (threads:destroy-thread thread))
+
+(defun thread-alive-p (thread)
+  (threads:thread-alive-p thread))
+
+(defun join-thread (thread)
+  (threads:thread-join thread))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-allegro.lisp b/deps/bordeaux-threads/src/impl-allegro.lisp
new file mode 100644 (file)
index 0000000..40769dd
--- /dev/null
@@ -0,0 +1,115 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+;;; documentation on the Allegro Multiprocessing interface can be found at
+;;; http://www.franz.com/support/documentation/8.1/doc/multiprocessing.htm
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (mp:make-process-lock :name (or name "Anonymous lock")))
+
+(defun make-recursive-lock (&optional name)
+  (mp:make-process-lock :name (or name "Anonymous recursive lock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (mp:process-lock lock mp:*current-process* "Lock" (if wait-p nil 0)))
+
+(defun release-lock (lock)
+  (mp:process-unlock lock))
+
+(defmacro with-lock-held ((place) &body body)
+  `(mp:with-process-lock (,place :norecursive t)
+     ,@body))
+
+(defmacro with-recursive-lock-held ((place &key timeout) &body body)
+  `(mp:with-process-lock (,place :timeout ,timeout)
+     ,@body))
+
+;;; Resource contention: condition variables
+
+(defun make-condition-variable (&key name)
+  (mp:make-condition-variable :name name))
+
+(defun condition-wait (condition-variable lock)
+  (mp:condition-variable-wait condition-variable lock))
+
+(defun condition-notify (condition-variable)
+  (mp:condition-variable-signal condition-variable))
+
+(defun thread-yield ()
+  (mp:process-allow-schedule))
+
+(deftype thread ()
+  'mp:process)
+
+;;; Thread Creation
+
+(defun start-multiprocessing ()
+  (mp:start-scheduler))
+
+(defun %make-thread (function name)
+  #+smp
+  (mp:process-run-function name function)
+  #-smp
+  (mp:process-run-function
+   name
+   (lambda ()
+     (let ((return-values
+             (multiple-value-list (funcall function))))
+       (setf (getf (mp:process-property-list mp:*current-process*)
+                   'return-values)
+             return-values)
+       (values-list return-values)))))
+
+(defun current-thread ()
+  mp:*current-process*)
+
+(defun threadp (object)
+  (typep object 'mp:process))
+
+(defun thread-name (thread)
+  (mp:process-name thread))
+
+;;; Timeouts
+
+(defmacro with-timeout ((timeout) &body body)
+  (once-only (timeout)
+    `(mp:with-timeout (,timeout (error 'timeout :length ,timeout))
+       ,@body)))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  mp:*all-processes*)
+
+(defun interrupt-thread (thread function &rest args)
+  (apply #'mp:process-interrupt thread function args))
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (mp:process-kill thread))
+
+(defun thread-alive-p (thread)
+  (mp:process-alive-p thread))
+
+(defun join-thread (thread)
+  #+smp
+  (mp:process-join thread)
+  #-smp
+  (progn
+    (mp:process-wait (format nil "Waiting for thread ~A to complete" thread)
+                     (complement #'mp:process-alive-p)
+                     thread)
+    (let ((return-values
+            (getf (mp:process-property-list thread) 'return-values)))
+      (values-list return-values))))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-clisp.lisp b/deps/bordeaux-threads/src/impl-clisp.lisp
new file mode 100644 (file)
index 0000000..22ef2c4
--- /dev/null
@@ -0,0 +1,92 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+(deftype thread ()
+  'mt:thread)
+
+;;; Thread Creation
+(defun %make-thread (function name)
+  (mt:make-thread function
+                  :name name
+                  :initial-bindings mt:*default-special-bindings*))
+
+(defun current-thread ()
+  (mt:current-thread))
+
+(defun threadp (object)
+  (mt:threadp object))
+
+(defun thread-name (thread)
+  (mt:thread-name thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (mt:make-mutex :name (or name "Anonymous lock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (mt:mutex-lock lock :timeout (if wait-p nil 0)))
+
+(defun release-lock (lock)
+  (mt:mutex-unlock lock))
+
+(defmacro with-lock-held ((place) &body body)
+  `(mt:with-mutex-lock (,place) ,@body))
+
+(defun make-recursive-lock (&optional name)
+  (mt:make-mutex :name (or name "Anonymous recursive lock")
+                 :recursive-p t))
+
+(defmacro with-recursive-lock-held ((place) &body body)
+  `(mt:with-mutex-lock (,place) ,@body))
+
+;;; Resource contention: condition variables
+
+(defun make-condition-variable (&key name)
+  (mt:make-exemption :name (or name "Anonymous condition variable")))
+
+(defun condition-wait (condition-variable lock)
+  (mt:exemption-wait condition-variable lock))
+
+(defun condition-notify (condition-variable)
+  (mt:exemption-signal condition-variable))
+
+(defun thread-yield ()
+  (mt:thread-yield))
+
+;;; Timeouts
+
+(defmacro with-timeout ((timeout) &body body)
+  (once-only (timeout)
+    `(mt:with-timeout (,timeout (error 'timeout :length ,timeout))
+       ,@body)))
+
+;;; Introspection/debugging
+
+;;; VTZ: mt:list-threads returns all threads that are not garbage collected.
+(defun all-threads ()
+  (delete-if-not #'mt:thread-active-p (mt:list-threads)))
+
+(defun interrupt-thread (thread function &rest args)
+  (mt:thread-interrupt thread :function function :arguments args))
+
+(defun destroy-thread (thread)
+  ;;; VTZ: actually we can kill ourselelf.
+  ;;; suicide is part of our contemporary life :)
+  (signal-error-if-current-thread thread)
+  (mt:thread-interrupt thread :function t))
+
+(defun thread-alive-p (thread)
+  (mt:thread-active-p thread))
+
+(defun join-thread (thread)
+  (mt:thread-join thread))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-clozure.lisp b/deps/bordeaux-threads/src/impl-clozure.lisp
new file mode 100644 (file)
index 0000000..85a6d3d
--- /dev/null
@@ -0,0 +1,98 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+;;; documentation on the OpenMCL Threads interface can be found at
+;;; http://openmcl.clozure.com/Doc/Programming-with-Threads.html
+
+(deftype thread ()
+  'ccl:process)
+
+;;; Thread Creation
+
+(defun %make-thread (function name)
+  (ccl:process-run-function name function))
+
+(defun current-thread ()
+  ccl:*current-process*)
+
+(defun threadp (object)
+  (typep object 'ccl:process))
+
+(defun thread-name (thread)
+  (ccl:process-name thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (ccl:make-lock (or name "Anonymous lock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (if wait-p
+      (ccl:grab-lock lock)
+      (ccl:try-lock lock)))
+
+(defun release-lock (lock)
+  (ccl:release-lock lock))
+
+(defmacro with-lock-held ((place) &body body)
+  `(ccl:with-lock-grabbed (,place)
+     ,@body))
+
+(defun make-recursive-lock (&optional name)
+  (ccl:make-lock (or name "Anonymous recursive lock")))
+
+(defun acquire-recursive-lock (lock)
+  (ccl:grab-lock lock))
+
+(defun release-recursive-lock (lock)
+  (ccl:release-lock lock))
+
+(defmacro with-recursive-lock-held ((place) &body body)
+  `(ccl:with-lock-grabbed (,place)
+     ,@body))
+
+;;; Resource contention: condition variables
+
+(defun make-condition-variable (&key name)
+  (declare (ignore name))
+  (ccl:make-semaphore))
+
+(defun condition-wait (condition-variable lock)
+  (release-lock lock)
+  (unwind-protect
+       (ccl:wait-on-semaphore condition-variable)
+    (acquire-lock lock t)))
+
+(defun condition-notify (condition-variable)
+  (ccl:signal-semaphore condition-variable))
+
+(defun thread-yield ()
+  (ccl:process-allow-schedule))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  (ccl:all-processes))
+
+(defun interrupt-thread (thread function &rest args)
+  (declare (dynamic-extent args))
+  (apply #'ccl:process-interrupt thread function args))
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (ccl:process-kill thread))
+
+(defun thread-alive-p (thread)
+  (ccl::process-active-p thread))
+
+(defun join-thread (thread)
+  (ccl:join-process thread))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-cmucl.lisp b/deps/bordeaux-threads/src/impl-cmucl.lisp
new file mode 100644 (file)
index 0000000..074646f
--- /dev/null
@@ -0,0 +1,137 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+(deftype thread ()
+  'mp::process)
+
+;;; Thread Creation
+
+(defun start-multiprocessing ()
+  (mp::startup-idle-and-top-level-loops))
+
+(defun %make-thread (function name)
+  #+#.(cl:if (cl:find-symbol (cl:string '#:process-join) :mp) '(and) '(or))
+  (mp:make-process function :name name)
+  #-#.(cl:if (cl:find-symbol (cl:string '#:process-join) :mp) '(and) '(or))
+  (mp:make-process (lambda ()
+                     (let ((return-values
+                             (multiple-value-list (funcall function))))
+                       (setf (getf (mp:process-property-list mp:*current-process*)
+                                   'return-values)
+                             return-values)
+                       (values-list return-values)))
+                   :name name))
+
+(defun current-thread ()
+  mp:*current-process*)
+
+(defmethod threadp (object)
+  (mp:processp object))
+
+(defun thread-name (thread)
+  (mp:process-name thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (mp:make-lock (or name "Anonymous lock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (if wait-p
+      (mp::lock-wait lock "Lock")
+      (mp::lock-wait-with-timeout lock "Lock" 0)))
+
+(defun release-lock (lock)
+  (setf (mp::lock-process lock) nil))
+
+(defmacro with-lock-held ((place) &body body)
+  `(mp:with-lock-held (,place) ,@body))
+
+(defmacro with-recursive-lock-held ((place &key timeout) &body body)
+  `(mp:with-lock-held (,place "Lock Wait" :timeout ,timeout) ,@body))
+
+;;; Note that the locks _are_ recursive, but not "balanced", and only
+;;; checked if they are being held by the same process by with-lock-held.
+;;; The default with-lock-held in bordeaux-mp.lisp sort of works, in that
+;;; it will wait for recursive locks by the same process as well.
+
+;;; Resource contention: condition variables
+
+;;; There's some stuff in x86-vm.lisp that might be worth investigating
+;;; whether to build on. There's also process-wait and friends.
+
+(defstruct condition-var
+  "CMUCL doesn't have conditions, so we need to create our own type."
+  name
+  lock
+  active)
+
+(defun make-condition-variable (&key name)
+  (make-condition-var :lock (make-lock)
+                      :name (or name "Anonymous condition variable")))
+
+(defun condition-wait (condition-variable lock)
+  (check-type condition-variable condition-var)
+  (with-lock-held ((condition-var-lock condition-variable))
+    (setf (condition-var-active condition-variable) nil))
+  (release-lock lock)
+  (mp:process-wait "Condition Wait"
+                   #'(lambda () (condition-var-active condition-variable)))
+  (acquire-lock lock)
+  t)
+
+(defun condition-notify (condition-variable)
+  (check-type condition-variable condition-var)
+  (with-lock-held ((condition-var-lock condition-variable))
+    (setf (condition-var-active condition-variable) t))
+  (thread-yield))
+
+(defun thread-yield ()
+  (mp:process-yield))
+
+;;; Timeouts
+
+(defmacro with-timeout ((timeout) &body body)
+  (once-only (timeout)
+    `(mp:with-timeout (,timeout (error 'timeout :length ,timeout))
+       ,@body)))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  (mp:all-processes))
+
+(defun interrupt-thread (thread function &rest args)
+  (flet ((apply-function ()
+           (if args
+               (lambda () (apply function args))
+               function)))
+    (declare (dynamic-extent #'apply-function))
+    (mp:process-interrupt thread (apply-function))))
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (mp:destroy-process thread))
+
+(defun thread-alive-p (thread)
+  (mp:process-active-p thread))
+
+(defun join-thread (thread)
+  #+#.(cl:if (cl:find-symbol (cl:string '#:process-join) :mp) '(and) '(or))
+  (mp:process-join thread)
+  #-#.(cl:if (cl:find-symbol (cl:string '#:process-join) :mp) '(and) '(or))
+  (progn
+    (mp:process-wait (format nil "Waiting for thread ~A to complete" thread)
+                     (lambda () (not (mp:process-alive-p thread))))
+    (let ((return-values
+            (getf (mp:process-property-list thread) 'return-values)))
+      (values-list return-values))))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-corman.lisp b/deps/bordeaux-threads/src/impl-corman.lisp
new file mode 100644 (file)
index 0000000..dad4524
--- /dev/null
@@ -0,0 +1,26 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+;;; Thread Creation
+
+(defun %make-thread (function name)
+  (declare (ignore name))
+  (threads:create-thread function))
+
+(defun current-thread ()
+  threads:*current-thread*)
+
+;;; Introspection/debugging
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (threads:terminate-thread thread))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-ecl.lisp b/deps/bordeaux-threads/src/impl-ecl.lisp
new file mode 100644 (file)
index 0000000..be8cc14
--- /dev/null
@@ -0,0 +1,95 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+;;; documentation on the ECL Multiprocessing interface can be found at
+;;; http://ecls.sourceforge.net/cgi-bin/view/Main/MultiProcessing
+
+(deftype thread ()
+  'mp:process)
+
+;;; Thread Creation
+
+(defun %make-thread (function name)
+  (mp:process-run-function name function))
+
+(defun current-thread ()
+  mp::*current-process*)
+
+(defun threadp (object)
+  (typep object 'mp:process))
+
+(defun thread-name (thread)
+  (mp:process-name thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (mp:make-lock :name (or name "Anonymous lock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (mp:get-lock lock wait-p))
+
+(defun release-lock (lock)
+  (mp:giveup-lock lock))
+
+(defmacro with-lock-held ((place) &body body)
+  `(mp:with-lock (,place) ,@body))
+
+(defun make-recursive-lock (&optional name)
+  (mp:make-lock :name (or name "Anonymous recursive lock") :recursive t))
+
+(defun acquire-recursive-lock (lock &optional (wait-p t))
+  (mp:get-lock lock wait-p))
+
+(defun release-recursive-lock (lock)
+  (mp:giveup-lock lock))
+
+(defmacro with-recursive-lock-held ((place) &body body)
+  `(mp:with-lock (,place) ,@body))
+
+;;; Resource contention: condition variables
+
+(defun make-condition-variable (&key name)
+  (declare (ignore name))
+  (mp:make-condition-variable))
+
+(defun condition-wait (condition-variable lock)
+  (mp:condition-variable-wait condition-variable lock))
+
+(defun condition-notify (condition-variable)
+  (mp:condition-variable-signal condition-variable))
+
+(defun thread-yield ()
+  (mp:process-yield))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  (mp:all-processes))
+
+(defun interrupt-thread (thread function &rest args)
+  (flet ((apply-function ()
+           (if args
+               (lambda () (apply function args))
+               function)))
+    (declare (dynamic-extent #'apply-function))
+    (mp:interrupt-process thread (apply-function))))
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (mp:process-kill thread))
+
+(defun thread-alive-p (thread)
+  (mp:process-active-p thread))
+
+(defun join-thread (thread)
+  (mp:process-join thread))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-lispworks-condition-variables.lisp b/deps/bordeaux-threads/src/impl-lispworks-condition-variables.lisp
new file mode 100644 (file)
index 0000000..c6dc20b
--- /dev/null
@@ -0,0 +1,138 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+;; Lispworks condition support is simulated, albeit via a lightweight wrapper over
+;; its own polling-based wait primitive.  Waiters register with the condition variable,
+;; and use MP:process-wait which queries for permission to proceed at its own (usspecified) interval.
+;; http://www.lispworks.com/documentation/lw51/LWRM/html/lwref-445.htm
+;; A wakeup callback (on notify) is provided to lighten this query to not have to do a hash lookup
+;; on every poll (or have to serialize on the condition variable) and a mechanism is put
+;; in place to unregister any waiter that exits wait for other reasons,
+;; and to resend any (single) notification that may have been consumed before this (corner
+;; case).  Much of the complexity present is to support single notification (as recommended in
+;; the spec); but a distinct condition-notify-all is provided for reference.
+;; Single-notification follows a first-in first-out ordering
+;;
+;; Performance:  With 1000 threads waiting on one condition-variable, the steady-state hit (at least
+;; as tested on a 3GHz Win32 box) is noise - hovering at 0% on Task manager.
+;; While not true zero like a true native solution, the use of the Lispworks native checks appear
+;; fast enough to be an equivalent substitute (thread count will cause issue before the
+;; waiting overhead becomes significant)
+(defstruct (condition-variable (:constructor make-lw-condition (name)))
+  name
+  (lock (mp:make-lock :name "For condition-variable") :type mp:lock :read-only t)
+  (wait-tlist (cons nil nil) :type cons :read-only t)
+  (wait-hash (make-hash-table :test 'eq) :type hash-table :read-only t)
+  ;; unconsumed-notifications is to track :remove-from-consideration
+  ;; for entries that may have exited prematurely - notification is sent through
+  ;; to someone else, and offender is removed from hash and list
+  (unconsumed-notifications (make-hash-table :test 'eq) :type hash-table :read-only t))
+
+(defun make-condition-variable (&key name)
+  (make-lw-condition name))
+
+(defmacro with-cv-access (condition-variable &body body)
+  (let ((cv-sym (gensym))
+        (slots '(lock wait-tlist wait-hash unconsumed-notifications)))
+    `(let ((,cv-sym ,condition-variable))
+       (with-slots ,slots
+           ,cv-sym
+         (macrolet ((locked (&body body) `(mp:with-lock (lock) ,@body)))
+           (labels ((,(gensym) () ,@slots))) ; Trigger expansion of the symbol-macrolets to ignore
+           ,@body)))))
+
+(defmacro defcvfun (function-name (condition-variable &rest args) &body body)
+  `(defun ,function-name (,condition-variable ,@args)
+     (with-cv-access ,condition-variable
+       ,@body)))
+#+lispworks (editor:setup-indent "defcvfun" 2 2 7) ; indent defcvfun
+
+; utility function thath assumes process is locked on condition-variable's lock.
+(defcvfun do-notify-single (condition-variable) ; assumes already locked
+  (let ((id (caar wait-tlist)))
+    (when id
+      (pop (car wait-tlist))
+      (unless (car wait-tlist) ; check for empty
+        (setf (cdr wait-tlist) nil))
+      (funcall (gethash id wait-hash)) ; call waiter-wakeup
+      (remhash id wait-hash) ; absence of entry = permission to proceed
+      (setf (gethash id unconsumed-notifications) t))))
+
+;; Added for completeness/to show how it's done in this paradigm; but
+;; The symbol for this call is not exposed in the api
+(defcvfun condition-notify-all (condition-variable)
+  (locked
+   (loop for waiter-wakeup being the hash-values in wait-hash do (funcall waiter-wakeup))
+   (clrhash wait-hash)
+   (clrhash unconsumed-notifications) ; don't care as everyone just got notified
+   (setf (car wait-tlist) nil)
+   (setf (cdr wait-tlist) nil)))
+
+;; Currently implemented so as to notify only one waiting thread
+(defcvfun condition-notify (condition-variable)
+  (locked (do-notify-single condition-variable)))
+
+(defun delete-from-tlist (tlist element)
+  (let ((deleter
+         (lambda ()
+           (setf (car tlist) (cdar tlist))
+           (unless (car tlist)
+             (setf (cdr tlist) nil)))))
+    (loop for cons in (car tlist) do
+          (if (eq element (car cons))
+              (progn
+                (funcall deleter)
+                (return nil))
+            (let ((cons cons))
+              (setq deleter
+                    (lambda ()
+                      (setf (cdr cons) (cddr cons))
+                      (unless (cdr cons)
+                        (setf (cdr tlist) cons)))))))))
+
+(defun add-to-tlist-tail (tlist element)
+  (let ((new-link (cons element nil)))
+    (cond
+     ((car tlist)
+      (setf (cddr tlist) new-link)
+      (setf (cdr tlist) new-link))
+     (t
+      (setf (car tlist) new-link)
+      (setf (cdr tlist) new-link)))))
+
+(defcvfun condition-wait (condition-variable lock-)
+  (mp:process-unlock lock-)
+  (unwind-protect ; for the re-taking of the lock.  Guarding all of the code
+      (let ((wakeup-allowed-to-proceed nil)
+            (wakeup-lock (mp:make-lock :name "wakeup lock for condition-wait")))
+        ;; wakeup-allowed-to-proceed is an optimisation to avoid having to serialize all waiters and
+        ;; search the hashtable.  That it is locked is for safety/completeness, although
+        ;; as wakeup-allowed-to-proceed only transitions nil -> t, and that missing it once or twice is
+        ;; moot in this situation, it would be redundant even if ever a Lispworks implementation ever became
+        ;; non-atomic in its assigments
+        (let ((id (cons nil nil))
+              (clean-exit nil))
+          (locked
+           (add-to-tlist-tail wait-tlist id)
+           (setf (gethash id wait-hash) (lambda () (mp:with-lock (wakeup-lock) (setq wakeup-allowed-to-proceed t)))))
+          (unwind-protect
+              (progn
+                (mp:process-wait
+                 "Waiting for notification"
+                 (lambda ()
+                   (when (mp:with-lock (wakeup-lock) wakeup-allowed-to-proceed)
+                     (locked (not (gethash id wait-hash))))))
+                (locked (remhash id unconsumed-notifications))
+                (setq clean-exit t)) ; Notification was consumed
+            ;; Have to call remove-from-consideration just in case process was interrupted
+            ;; rather than having condition met
+            (unless clean-exit ; clean-exit is just an optimization
+              (locked
+               (when (gethash id wait-hash) ; not notified - must have been interrupted
+                 ;; Have to unsubscribe
+                 (remhash id wait-hash)
+                 (delete-from-tlist wait-tlist id))
+               ;; note - it's possible to be removed from wait-hash/wait-tlist (in notify-single); but still have an unconsumed notification!
+               (when (gethash id unconsumed-notifications) ; Must have exited for reasons unrelated to notification
+                 (remhash id unconsumed-notifications) ; Have to pass on the notification to an eligible waiter
+                 (do-notify-single condition-variable)))))))
+    (mp:process-lock lock-)))
diff --git a/deps/bordeaux-threads/src/impl-lispworks.lisp b/deps/bordeaux-threads/src/impl-lispworks.lisp
new file mode 100644 (file)
index 0000000..882866b
--- /dev/null
@@ -0,0 +1,125 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+;;; documentation on the LispWorks Multiprocessing interface can be found at
+;;; http://www.lispworks.com/documentation/lw445/LWUG/html/lwuser-156.htm
+
+(deftype thread ()
+  'mp:process)
+
+;;; Thread Creation
+
+(defun start-multiprocessing ()
+  (mp:initialize-multiprocessing))
+
+(defun %make-thread (function name)
+  (mp:process-run-function
+   name nil
+   (lambda ()
+     (let ((return-values
+             (multiple-value-list (funcall function))))
+       (setf (mp:process-property 'return-values)
+             return-values)
+       (values-list return-values)))))
+
+(defun current-thread ()
+  #-#.(cl:if (cl:find-symbol (cl:string '#:get-current-process) :mp) '(and) '(or))
+  mp:*current-process*
+  ;; introduced in LispWorks 5.1
+  #+#.(cl:if (cl:find-symbol (cl:string '#:get-current-process) :mp) '(and) '(or))
+  (mp:get-current-process))
+
+(defun threadp (object)
+  (mp:process-p object))
+
+(defun thread-name (thread)
+  (mp:process-name thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (mp:make-lock :name (or name "Anonymous lock")
+                #-(or lispworks4 lispworks5) :recursivep
+                #-(or lispworks4 lispworks5) nil))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (mp:process-lock lock nil
+                   (cond ((null wait-p)         0)
+                         ((numberp wait-p) wait-p)
+                         (t                   nil))))
+
+(defun release-lock (lock)
+  (mp:process-unlock lock))
+
+(defmacro with-lock-held ((place) &body body)
+  `(mp:with-lock (,place) ,@body))
+
+(defun make-recursive-lock (&optional name)
+  (mp:make-lock :name (or name "Anonymous recursive lock")
+                #-(or lispworks4 lispworks5) :recursivep
+                #-(or lispworks4 lispworks5) t))
+
+(defun acquire-recursive-lock (lock &optional (wait-p t))
+  (acquire-lock lock wait-p))
+
+(defun release-recursive-lock (lock)
+  (release-lock lock))
+
+(defmacro with-recursive-lock-held ((place) &body body)
+  `(mp:with-lock (,place) ,@body))
+
+;;; Resource contention: condition variables
+
+#+(or lispworks6)
+(defun make-condition-variable (&key name)
+  (mp:make-condition-variable :name (or name "Anonymous condition variable")))
+
+#+(or lispworks6)
+(defun condition-wait (condition-variable lock)
+  (mp:condition-variable-wait condition-variable lock))
+
+#+(or lispworks6)
+(defun condition-notify (condition-variable)
+  (mp:condition-variable-signal condition-variable))
+
+(defun thread-yield ()
+  (mp:process-allow-scheduling))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  (mp:list-all-processes))
+
+(defun interrupt-thread (thread function &rest args)
+  (apply #'mp:process-interrupt thread function args))
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (mp:process-kill thread))
+
+(defun thread-alive-p (thread)
+  (mp:process-alive-p thread))
+
+(declaim (inline %join-thread))
+(defun %join-thread (thread)
+  #-#.(cl:if (cl:find-symbol (cl:string '#:process-join) :mp) '(and) '(or))
+  (mp:process-wait (format nil "Waiting for thread ~A to complete" thread)
+                   (complement #'mp:process-alive-p)
+                   thread)
+  #+#.(cl:if (cl:find-symbol (cl:string '#:process-join) :mp) '(and) '(or))
+  (mp:process-join thread))
+
+(defun join-thread (thread)
+  (%join-thread thread)
+  (let ((return-values
+          (mp:process-property 'return-values thread)))
+    (values-list return-values)))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-mcl.lisp b/deps/bordeaux-threads/src/impl-mcl.lisp
new file mode 100644 (file)
index 0000000..3530f5d
--- /dev/null
@@ -0,0 +1,63 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+(deftype thread ()
+  'ccl::process)
+
+;;; Thread Creation
+
+(defun %make-thread (function name)
+  (ccl:process-run-function name function))
+
+(defun current-thread ()
+  ccl:*current-process*)
+
+(defun threadp (object)
+  (ccl::processp object))
+
+(defun thread-name (thread)
+  (ccl:process-name thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (ccl:make-lock (or name "Anonymous lock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (if wait-p
+    (ccl:process-lock lock ccl:*current-process*)
+    ;; this is broken, but it's better than a no-op
+    (ccl:without-interrupts
+     (when (null (ccl::lock.value lock))
+       (ccl:process-lock lock ccl:*current-process*)))))
+
+(defun release-lock (lock)
+  (ccl:process-unlock lock))
+
+(defmacro with-lock-held ((place) &body body)
+  `(ccl:with-lock-grabbed (,place) ,@body))
+
+(defun thread-yield ()
+  (ccl:process-allow-schedule))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  ccl:*all-processes*)
+
+(defun interrupt-thread (thread function &rest args)
+  (declare (dynamic-extent args))
+  (apply #'ccl:process-interrupt thread function args))
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (ccl:process-kill thread))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-mkcl.lisp b/deps/bordeaux-threads/src/impl-mkcl.lisp
new file mode 100644 (file)
index 0000000..dfeef97
--- /dev/null
@@ -0,0 +1,93 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+Copyright 2010 Jean-Claude Beaudoin.
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+(deftype thread ()
+  'mt:thread)
+
+;;; Thread Creation
+
+(defun %make-thread (function name)
+  (mt:thread-run-function name function))
+
+(defun current-thread ()
+  mt::*thread*)
+
+(defun threadp (object)
+  (typep object 'mt:thread))
+
+(defun thread-name (thread)
+  (mt:thread-name thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (mt:make-lock :name (or name "Anonymous lock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (mt:get-lock lock wait-p))
+
+(defun release-lock (lock)
+  (mt:giveup-lock lock))
+
+(defmacro with-lock-held ((place) &body body)
+  `(mt:with-lock (,place) ,@body))
+
+(defun make-recursive-lock (&optional name)
+  (mt:make-lock :name (or name "Anonymous recursive lock") :recursive t))
+
+(defun acquire-recursive-lock (lock &optional (wait-p t))
+  (mt:get-lock lock wait-p))
+
+(defun release-recursive-lock (lock)
+  (mt:giveup-lock lock))
+
+(defmacro with-recursive-lock-held ((place) &body body)
+  `(mt:with-lock (,place) ,@body))
+
+;;; Resource contention: condition variables
+
+(defun make-condition-variable (&key name)
+  (declare (ignore name))
+  (mt:make-condition-variable))
+
+(defun condition-wait (condition-variable lock)
+  (mt:condition-wait condition-variable lock))
+
+(defun condition-notify (condition-variable)
+  (mt:condition-signal condition-variable))
+
+(defun thread-yield ()
+  (mt:thread-yield))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  (mt:all-threads))
+
+(defun interrupt-thread (thread function &rest args)
+  (flet ((apply-function ()
+           (if args
+               (lambda () (apply function args))
+               function)))
+    (declare (dynamic-extent #'apply-function))
+    (mt:interrupt-thread thread (apply-function))))
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (mt:thread-kill thread))
+
+(defun thread-alive-p (thread)
+  (mt:thread-active-p thread))
+
+(defun join-thread (thread)
+  (mt:thread-join thread))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-null.lisp b/deps/bordeaux-threads/src/impl-null.lisp
new file mode 100644 (file)
index 0000000..2cf4bbb
--- /dev/null
@@ -0,0 +1,3 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+(in-package #:bordeaux-threads)
diff --git a/deps/bordeaux-threads/src/impl-sbcl.lisp b/deps/bordeaux-threads/src/impl-sbcl.lisp
new file mode 100644 (file)
index 0000000..bf20a94
--- /dev/null
@@ -0,0 +1,106 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2006, 2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+;;; documentation on the SBCL Threads interface can be found at
+;;; http://www.sbcl.org/manual/Threading.html
+
+(deftype thread ()
+  'sb-thread:thread)
+
+;;; Thread Creation
+
+(defun %make-thread (function name)
+  (sb-thread:make-thread function :name name))
+
+(defun current-thread ()
+  sb-thread:*current-thread*)
+
+(defun threadp (object)
+  (typep object 'sb-thread:thread))
+
+(defun thread-name (thread)
+  (sb-thread:thread-name thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (sb-thread:make-mutex :name (or name "Anonymous lock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  #+#.(cl:if (cl:find-symbol (cl:string '#:grab-mutex) :sb-thread) '(and) '(or))
+  (sb-thread:grab-mutex lock :waitp wait-p)
+  #-#.(cl:if (cl:find-symbol (cl:string '#:grab-mutex) :sb-thread) '(and) '(or))
+  (sb-thread:get-mutex lock nil wait-p))
+
+(defun release-lock (lock)
+  (sb-thread:release-mutex lock))
+
+(defmacro with-lock-held ((place) &body body)
+  `(sb-thread:with-mutex (,place) ,@body))
+
+(defun make-recursive-lock (&optional name)
+  (sb-thread:make-mutex :name (or name "Anonymous recursive lock")))
+
+;;; XXX acquire-recursive-lock and release-recursive-lock are actually
+;;; complicated because we can't use control stack tricks.  We need to
+;;; actually count something to check that the acquire/releases are
+;;; balanced
+
+(defmacro with-recursive-lock-held ((place) &body body)
+  `(sb-thread:with-recursive-lock (,place)
+     ,@body))
+
+;;; Resource contention: condition variables
+
+(defun make-condition-variable (&key name)
+  (sb-thread:make-waitqueue :name (or name "Anonymous condition variable")))
+
+(defun condition-wait (condition-variable lock)
+  (sb-thread:condition-wait condition-variable lock))
+
+(defun condition-notify (condition-variable)
+  (sb-thread:condition-notify condition-variable))
+
+(defun thread-yield ()
+  (sb-thread:release-foreground))
+
+;;; Timeouts
+
+(deftype timeout ()
+  'sb-ext:timeout)
+
+(defmacro with-timeout ((timeout) &body body)
+  `(sb-ext:with-timeout ,timeout
+     ,@body))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  (sb-thread:list-all-threads))
+
+(defun interrupt-thread (thread function &rest args)
+  (flet ((apply-function ()
+           (if args
+               (lambda () (apply function args))
+               function)))
+    (declare (dynamic-extent #'apply-function))
+    (sb-thread:interrupt-thread thread (apply-function))))
+
+(defun destroy-thread (thread)
+  (signal-error-if-current-thread thread)
+  (sb-thread:terminate-thread thread))
+
+(defun thread-alive-p (thread)
+  (sb-thread:thread-alive-p thread))
+
+(defun join-thread (thread)
+  (sb-thread:join-thread thread))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/impl-scl.lisp b/deps/bordeaux-threads/src/impl-scl.lisp
new file mode 100644 (file)
index 0000000..0b798e2
--- /dev/null
@@ -0,0 +1,90 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+#|
+Copyright 2008 Scieneer Pty Ltd
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(in-package #:bordeaux-threads)
+
+(deftype thread ()
+  'thread:thread)
+
+(defun %make-thread (function name)
+  (thread:thread-create function :name name))
+
+(defun current-thread ()
+  thread:*thread*)
+
+(defun threadp (object)
+  (typep object 'thread:thread))
+
+(defun thread-name (thread)
+  (thread:thread-name thread))
+
+;;; Resource contention: locks and recursive locks
+
+(defun make-lock (&optional name)
+  (thread:make-lock (or name "Anonymous lock")))
+
+(defun acquire-lock (lock &optional (wait-p t))
+  (thread::acquire-lock lock nil wait-p))
+
+(defun release-lock (lock)
+  (thread::release-lock lock))
+
+(defmacro with-lock-held ((place) &body body)
+  `(thread:with-lock-held (,place) ,@body))
+
+(defun make-recursive-lock (&optional name)
+  (thread:make-lock (or name "Anonymous recursive lock")
+                    :type :recursive))
+
+;;; XXX acquire-recursive-lock and release-recursive-lock are actually
+;;; complicated because we can't use control stack tricks.  We need to
+;;; actually count something to check that the acquire/releases are
+;;; balanced
+
+(defmacro with-recursive-lock-held ((place) &body body)
+  `(thread:with-lock-held (,place)
+     ,@body))
+
+;;; Resource contention: condition variables
+
+(defun make-condition-variable (&key name)
+  (thread:make-cond-var (or name "Anonymous condition variable")))
+
+(defun condition-wait (condition-variable lock)
+  (thread:cond-var-wait condition-variable lock))
+
+(defun condition-notify (condition-variable)
+  (thread:cond-var-broadcast condition-variable))
+
+(defun thread-yield ()
+  (mp:process-yield))
+
+;;; Introspection/debugging
+
+(defun all-threads ()
+  (mp:all-processes))
+
+(defun interrupt-thread (thread function &rest args)
+  (flet ((apply-function ()
+           (if args
+               (lambda () (apply function args))
+               function)))
+    (declare (dynamic-extent #'apply-function))
+    (thread:thread-interrupt thread (apply-function))))
+
+(defun destroy-thread (thread)
+  (thread:destroy-thread thread))
+
+(defun thread-alive-p (thread)
+  (mp:process-alive-p thread))
+
+(defun join-thread (thread)
+  (mp:process-wait (format nil "Waiting for thread ~A to complete" thread)
+                   (lambda () (not (mp:process-alive-p thread)))))
+
+(mark-supported)
diff --git a/deps/bordeaux-threads/src/pkgdcl.lisp b/deps/bordeaux-threads/src/pkgdcl.lisp
new file mode 100644 (file)
index 0000000..0dd4382
--- /dev/null
@@ -0,0 +1,62 @@
+;;;; -*- indent-tabs-mode: nil -*-
+
+(cl:defpackage bordeaux-threads
+  (:nicknames #:bt)
+  (:use #:cl #:alexandria)
+  #+abcl
+  (:import-from :java #:jnew #:jcall #:jmethod)
+  (:export #:thread #:make-thread #:current-thread #:threadp #:thread-name
+           #:start-multiprocessing
+           #:*default-special-bindings* #:*standard-io-bindings*
+           #:*supports-threads-p*
+
+           #:make-lock #:acquire-lock #:release-lock #:with-lock-held
+           #:make-recursive-lock #:acquire-recursive-lock
+           #:release-recursive-lock #:with-recursive-lock-held
+
+           #:make-condition-variable #:condition-wait #:condition-notify
+
+           #:with-timeout #:timeout
+
+           #:all-threads #:interrupt-thread #:destroy-thread #:thread-alive-p
+           #:join-thread #:thread-yield)
+  (:documentation "BORDEAUX-THREADS is a proposed standard for a minimal
+  MP/threading interface. It is similar to the CLIM-SYS threading and
+  lock support, but for the following broad differences:
+
+  1) Some behaviours are defined in additional detail: attention has
+     been given to special variable interaction, whether and when
+     cleanup forms are run. Some behaviours are defined in less
+     detail: an implementation that does not support multiple
+     threads is not required to use a new list (nil) for a lock, for
+     example.
+
+  2) Many functions which would be difficult, dangerous or inefficient
+     to provide on some implementations have been removed. Chiefly
+     these are functions such as thread-wait which expect for
+     efficiency that the thread scheduler is written in Lisp and
+     'hookable', which can't sensibly be done if the scheduler is
+     external to the Lisp image, or the system has more than one CPU.
+
+  3) Unbalanced ACQUIRE-LOCK and RELEASE-LOCK functions have been
+     added.
+
+  4) Posix-style condition variables have been added, as it's not
+     otherwise possible to implement them correctly using the other
+     operations that are specified.
+
+  Threads may be implemented using whatever applicable techniques are
+  provided by the operating system: user-space scheduling,
+  kernel-based LWPs or anything else that does the job.
+
+  Some parts of this specification can also be implemented in a Lisp
+  that does not support multiple threads. Thread creation and some
+  thread inspection operations will not work, but the locking
+  functions are still present (though they may do nothing) so that
+  thread-safe code can be compiled on both multithread and
+  single-thread implementations without need of conditionals.
+
+  To avoid conflict with existing MP/threading interfaces in
+  implementations, these symbols live in the BORDEAUX-THREADS package.
+  Implementations and/or users may also make them visible or exported
+  in other more traditionally named packages."))
diff --git a/deps/bordeaux-threads/test/bordeaux-threads-test.lisp b/deps/bordeaux-threads/test/bordeaux-threads-test.lisp
new file mode 100644 (file)
index 0000000..e400910
--- /dev/null
@@ -0,0 +1,202 @@
+#|
+Copyright 2006,2007 Greg Pfeil
+
+Distributed under the MIT license (see LICENSE file)
+|#
+
+(defpackage bordeaux-threads-test
+  (:use #:cl #:bordeaux-threads #:fiveam)
+  (:shadow #:with-timeout))
+
+(in-package #:bordeaux-threads-test)
+
+(def-suite :bordeaux-threads)
+(def-fixture using-lock () 
+  (let ((lock (make-lock)))
+    (&body)))
+(in-suite :bordeaux-threads)
+
+(test should-have-current-thread
+  (is (current-thread)))
+
+(test current-thread-identity
+  (let* ((box (list nil))
+         (thread (make-thread (lambda ()
+                                (setf (car box) (current-thread))))))
+    (join-thread thread)
+    (is (eql (car box) thread))))
+
+(test join-thread-return-value
+  (is (eql 0 (join-thread (make-thread (lambda () 0))))))
+
+(test should-identify-threads-correctly
+  (is (threadp (current-thread)))
+  (is (threadp (make-thread (lambda () t) :name "foo")))
+  (is (not (threadp (make-lock)))))
+
+(test should-retrieve-thread-name
+  (is (equal "foo" (thread-name (make-thread (lambda () t) :name "foo")))))
+
+(test interrupt-thread
+  (let* ((box (list nil))
+         (thread (make-thread (lambda ()
+                                (setf (car box)
+                                      (catch 'new-thread
+                                        (sleep 60)
+                                        'not-interrupted))))))
+    (sleep 1)
+    (interrupt-thread thread (lambda ()
+                               (throw 'new-thread 'interrupted)))
+    (join-thread thread)
+    (is (eql 'interrupted (car box)))))
+
+(test should-lock-without-contention
+  (with-fixture using-lock ()
+    (is (acquire-lock lock t))
+    (release-lock lock)
+    (is (acquire-lock lock nil))
+    (release-lock lock)))
+
+(defun set-equal (set-a set-b)
+  (and (null (set-difference set-a set-b))
+       (null (set-difference set-b set-a))))
+
+(test default-special-bindings
+  (locally (declare (special *a* *c*))
+    (let* ((the-as 50) (the-bs 150) (*b* 42)
+           some-a some-b some-other-a some-other-b
+           (*default-special-bindings*
+            `((*a* . (funcall ,(lambda () (incf the-as))))
+              (*b* . (funcall ,(lambda () (incf the-bs))))
+              ,@*default-special-bindings*))
+           (threads (list (make-thread
+                           (lambda ()
+                             (setf some-a *a* some-b *b*)))
+                          (make-thread
+                           (lambda ()
+                             (setf some-other-a *a*
+                                   some-other-b *b*))))))
+      (declare (special *b*))
+      (thread-yield)
+      (is (not (boundp '*a*)))
+      (loop while (some #'thread-alive-p threads)
+            do (thread-yield))
+      (is (set-equal (list some-a some-other-a) '(51 52)))
+      (is (set-equal (list some-b some-other-b) '(151 152)))
+      (is (not (boundp '*a*))))))
+
+
+(defparameter *shared* 0)
+(defparameter *lock* (make-lock))
+
+(test should-have-thread-interaction
+  ;; this simple test generates N process. Each process grabs and
+  ;; releases the lock until SHARED has some value, it then
+  ;; increments SHARED. the outer code first sets shared 1 which
+  ;; gets the thing running and then waits for SHARED to reach some
+  ;; value. this should, i think, stress test locks.
+  (setf *shared* 0)
+  (flet ((worker (i)
+           (loop
+             do (with-lock-held (*lock*)
+                  (when (= i *shared*)
+                    (incf *shared*)
+                    (return)))
+                (sleep 0.001))))
+    (let* ((procs (loop
+                    for i from 1 upto 2
+                    ;; create a new binding to protect against implementations that
+                    ;; mutate instead of binding the loop variable
+                    collect (let ((i i))
+                              (make-thread (lambda ()
+                                             (funcall #'worker i))
+                                           :name (format nil "Proc #~D" i))))))
+      (with-lock-held (*lock*)
+        (incf *shared*))
+      (block test
+        (loop
+          until (with-lock-held (*lock*)
+                  (= (1+ (length procs)) *shared*))
+          do (with-lock-held (*lock*)
+               (is (>= (1+ (length procs)) *shared*)))
+             (sleep 0.001))))))
+
+
+(defparameter *condition-variable* (make-condition-variable))
+
+(test condition-variable
+  (setf *shared* 0)
+  (flet ((worker (i)
+           (with-lock-held (*lock*)
+             (loop
+               until (= i *shared*)
+               do (condition-wait *condition-variable* *lock*))
+             (incf *shared*))
+           (condition-notify *condition-variable*)))
+    (let ((num-procs 100))
+      (dotimes (i num-procs)
+        ;; create a new binding to protect against implementations that
+        ;; mutate instead of binding the loop variable
+        (let ((i i))
+          (make-thread (lambda ()
+                         (funcall #'worker i))
+                       :name (format nil "Proc #~D" i))))
+      (with-lock-held (*lock*)
+        (loop
+          until (= num-procs *shared*)
+          do (condition-wait *condition-variable* *lock*)))
+      (is (equal num-procs *shared*)))))
+
+;; Generally safe sanity check for the locks and single-notify
+#+(and lispworks (not lispworks6))
+(test condition-variable-lw
+  (let ((condition-variable (make-condition-variable :name "Test"))
+        (test-lock (make-lock))
+        (completed nil))
+    (dotimes (id 6)
+      (let ((id id))
+        (make-thread (lambda ()
+                       (with-lock-held (test-lock)
+                         (condition-wait condition-variable test-lock)
+                         (push id completed)
+                         (condition-notify condition-variable))))))
+    (sleep 2)
+    (if completed
+        (print "Failed: Premature passage through condition-wait")
+        (print "Successfully waited on condition"))
+    (condition-notify condition-variable)
+    (sleep 2)
+    (if (and completed
+             (eql (length completed) 6)
+             (equal (sort completed #'<)
+                    (loop for id from 0 to 5 collect id)))
+        (print "Success: All elements notified")
+        (print (format nil "Failed: Of 6 expected elements, only ~A proceeded" completed)))
+    (bt::with-cv-access condition-variable
+      (if (and
+           (not (or (car wait-tlist) (cdr wait-tlist)))
+           (zerop (hash-table-count wait-hash))
+           (zerop (hash-table-count unconsumed-notifications)))
+          (print "Success: condition variable restored to initial state")
+          (print "Error: condition variable retains residue from completed waiters")))
+    (setq completed nil)
+    (dotimes (id 6)
+          (let ((id id))
+            (make-thread (lambda ()
+                           (with-lock-held (test-lock)
+                             (condition-wait condition-variable test-lock)
+                             (push id completed))))))
+    (sleep 2)
+    (condition-notify condition-variable)
+    (sleep 2)
+    (if (= (length completed) 1)
+        (print "Success: Notify-single only notified a single waiter to restart")
+        (format t "Failure: Notify-single restarted ~A items" (length completed)))
+    (condition-notify condition-variable)
+    (sleep 2)
+    (if (= (length completed) 2)
+        (print "Success: second Notify-single only notified a single waiter to restart")
+        (format t "Failure: Two Notify-singles restarted ~A items" (length completed)))
+    (loop for i from 0 to 5 do (condition-notify condition-variable))
+    (print "Note:  In the case of any failures, assume there are outstanding waiting threads")
+    (values)))
diff --git a/deps/bordeaux-threads/version.lisp-expr b/deps/bordeaux-threads/version.lisp-expr
new file mode 100644 (file)
index 0000000..f9514c4
--- /dev/null
@@ -0,0 +1,2 @@
+;; -*- lisp -*-
+"0.8.3"
diff --git a/deps/chunga/CHANGELOG b/deps/chunga/CHANGELOG
new file mode 100644 (file)
index 0000000..2821100
--- /dev/null
@@ -0,0 +1,8 @@
+Version 1.1.7
+2017-12-31
+Removed (safety 0)
+Version 1.1.6
+2014-11-28
+add CHANGELOG (Hans Huebner)
+update support info (Hans Huebner)
+
diff --git a/deps/chunga/CHANGELOG.txt b/deps/chunga/CHANGELOG.txt
new file mode 100644 (file)
index 0000000..c632b75
--- /dev/null
@@ -0,0 +1,91 @@
+Version 1.1.5
+2013-03-21
+Fixes to changed default for eof-error-p suggested by Edi Weitz
+
+Version 1.1.4
+2013-03-20
+Trivial documentation fix
+
+Version 1.1.3
+2013-03-20
+Change default eof-error-p in READ-CHAR* to T (reported by Xu Jingtao)
+
+Version 1.1.2
+2012-12-09
+Fix bug in READ-NAME-VALUE-PAIR for cookie reading in Drakma
+
+Version 1.1.1
+2010-05-19
+Read quoted cookie values (Red Daly)
+
+Version 1.1.0
+2009-12-01
+Exported TOKEN-CHAR-P
+Allowed START and END keyword arguments for TRIM-WHITESPACE
+Simplified cookie value parsing
+
+Version 1.0.0
+2009-02-19
+Switched to binary streams underneath and got rid of FLEXI-STREAMS dependency
+Added conditions
+Exported (an improved version of) AS-KEYWORD
+Added WITH-CHARACTER-STREAM-SEMANTICS
+
+Version 0.4.3
+2008-05-23
+Cleanup, reduce some consing
+
+Version 0.4.2
+2008-05-07
+Flush stream when switching chunking off (patch by Hans Hübner)
+
+Version 0.4.1
+2007-10-11
+Make Chunga work with AllegroCL's "modern" mode (patch by Ross Jekel)
+
+Version 0.4.0
+2007-09-18
+Added *TREAT-SEMICOLON-AS-CONTINUATION*
+
+Version 0.3.1
+2007-09-07
+Fixed bug in STREAM-LISTEN
+
+Version 0.3.0
+2007-05-08
+Added *ACCEPT-BOGUS-EOLS* (suggested by Sean Ross)
+
+Version 0.2.4
+2007-02-08
+Allow more characters in cookie names/values according to original Netscape spec
+Robustified READ-COOKIE-VALUE
+
+Version 0.2.3
+2007-01-17
+Guard against stray semicolons when reading name/value pairs (thanks to B?lent Murtezaoglu)
+
+Version 0.2.2
+2007-01-10
+Faster vesion of READ-LINE* (provided by Gabor Melis)
+
+Version 0.2.1
+2006-10-26
+Added explicit element types for CLISP to fix problems reported by Anton Vodonosov
+
+Version 0.2.0
+2006-10-06
+Only wrap inner stream with flexi stream if really needed
+
+Version 0.1.2
+2006-09-05
+Exported CHUNKED-STREAM-STREAM
+Mentioned Gentoo port in docs
+Added info about mailing lists
+
+Version 0.1.1
+2006-09-02
+Added missing CRLF for output chunking
+
+Version 0.1.0
+2006-09-01
+First public release
diff --git a/deps/chunga/chunga.asd b/deps/chunga/chunga.asd
new file mode 100644 (file)
index 0000000..be6b778
--- /dev/null
@@ -0,0 +1,42 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/chunga/chunga.asd,v 1.20 2008/05/24 18:38:30 edi Exp $
+
+;;; Copyright (c) 2006-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(asdf:defsystem :chunga
+  :serial t
+  :version "1.1.7"
+  :depends-on (:trivial-gray-streams)
+  :components ((:file "packages")
+               (:file "specials")
+               (:file "util")
+               (:file "known-words")
+               (:file "conditions")
+               (:file "read")
+               (:file "streams")
+               (:file "input")
+               (:file "output")))
diff --git a/deps/chunga/conditions.lisp b/deps/chunga/conditions.lisp
new file mode 100644 (file)
index 0000000..c04a53a
--- /dev/null
@@ -0,0 +1,84 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: ODD-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/odd-streams/conditions.lisp,v 1.5 2007/12/31 01:08:45 edi Exp $
+
+;;; Copyright (c) 2008-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :chunga)
+
+(define-condition chunga-condition (condition)
+  ()
+  (:documentation "Superclass for all conditions related to Chunga."))
+
+(define-condition chunga-error (chunga-condition stream-error)
+  ()
+  (:documentation "Superclass for all errors related to Chunga.  This
+is a subtype of STREAM-ERROR, so STREAM-ERROR-STREAM can be used to
+access the offending stream."))
+
+(define-condition chunga-simple-error (chunga-error simple-condition)
+  ()
+  (:documentation "Like CHUNGA-ERROR but with formatting capabilities."))
+
+(define-condition parameter-error (chunga-simple-error)
+  ()
+  (:documentation "Signalled if a function was called with
+inconsistent or illegal parameters."))
+
+(define-condition syntax-error (chunga-simple-error)
+  ()
+  (:documentation "Signalled if Chunga encounters wrong or unknown
+syntax when reading data."))
+
+(define-condition chunga-warning (chunga-condition warning)
+  ()
+  (:documentation "Superclass for all warnings related to Chunga."))
+
+(define-condition chunga-simple-warning (chunga-warning simple-condition)
+  ()
+  (:documentation "Like CHUNGA-WARNING but with formatting capabilities."))
+
+(define-condition input-chunking-unexpected-end-of-file (chunga-error)
+  ()
+  (:documentation "A condition of this type is signaled if we reach an
+unexpected EOF on a chunked stream with input chunking enabled."))
+
+(define-condition input-chunking-body-corrupted (chunga-error)
+  ((last-char :initarg :last-char
+              :documentation "The \(unexpected) character which was read.")
+   (expected-chars :initarg :expected-chars
+                   :documentation "The characters which were expected.
+A list of characters or one single character."))
+  (:report (lambda (condition stream)
+             (with-slots (last-char expected-chars)
+                 condition
+               (format stream "Chunked stream ~S seems to be corrupted.
+Read character ~S, but expected ~:[a member of ~S~;~S~]."
+                       (stream-error-stream condition)
+                       last-char (atom expected-chars) expected-chars))))
+  (:documentation "A condition of this type is signaled if an
+unexpected character \(octet) is read while reading from a chunked
+stream with input chunking enabled."))
diff --git a/deps/chunga/docs/index.html b/deps/chunga/docs/index.html
new file mode 100644 (file)
index 0000000..4d66ad3
--- /dev/null
@@ -0,0 +1,736 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+  <title>CHUNGA - Portable chunked streams for Common Lisp</title>
+  <style type="text/css">
+  pre { padding:5px; background-color:#e0e0e0 }
+  h3, h4 { text-decoration: underline; }
+  a { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:visited { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; }
+  a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; }
+  a.none { text-decoration: none; padding: 0; }
+  a.none:visited { text-decoration: none; padding: 0; }
+  a.none:hover { text-decoration: none; border: none; padding: 0; }
+  a.none:focus { text-decoration: none; border: none; padding: 0; }
+  a.noborder { text-decoration: none; padding: 0; }
+  a.noborder:visited { text-decoration: none; padding: 0; }
+  a.noborder:hover { text-decoration: none; border: none; padding: 0; }
+  a.noborder:focus { text-decoration: none; border: none; padding: 0; }
+  </style>
+</head>
+
+<body bgcolor=white>
+
+<h2>CHUNGA - Portable chunked streams for Common Lisp</h2>
+
+<blockquote>
+<br>&nbsp;<br><h3><a name=abstract class=none>Abstract</a></h3> Chunga
+implements streams capable of chunked encoding on demand as defined in
+RFC 2616.  For an example of how these streams can be used
+see <a href="https://edicl.github.io/drakma/">Drakma</a>.
+<p>
+The library needs a Common Lisp implementation that
+supports <a
+href="http://www.nhplace.com/kent/CL/Issues/stream-definition-by-user.html"><em>Gray
+streams</em></a> and relies on David
+Lichteblau's <a
+href="http://www.cliki.net/trivial-gray-streams">trivial-gray-streams</a> to offer portability between different Lisps.
+<p>
+Chunga is currently not optimized towards performance - it is
+rather intended to be easy to use and (if possible) to behave correctly.
+<p>
+The code comes with
+a <a
+href="http://www.opensource.org/licenses/bsd-license.php">BSD-style
+license</a> so you can basically do with it whatever you want.
+
+<p>
+  <a href="https://github.com/edicl/chunga/releases/latest">Download current version</a>
+  or visit the <a href="https://github.com/edicl/chunga/">project on Github</a>.
+</blockquote>
+
+<br>&nbsp;<br><h3><a class=none name="contents">Contents</a></h3>
+<ol>
+  <li><a href="#download">Download and installation</a>
+  <li><a href="#support">Support</a>
+  <li><a href="#dictionary">The Chunga dictionary</a>
+    <ol>
+      <li><a href="#streams">Chunked streams</a>
+        <ol>
+          <li><a href="#chunked-stream"><code>chunked-stream</code></a>
+          <li><a href="#chunked-input-stream"><code>chunked-input-stream</code></a>
+          <li><a href="#chunked-output-stream"><code>chunked-output-stream</code></a>
+          <li><a href="#chunked-io-stream"><code>chunked-io-stream</code></a>
+          <li><a href="#make-chunked-stream"><code>make-chunked-stream</code></a>
+          <li><a href="#chunked-stream-stream"><code>chunked-stream-stream</code></a>
+          <li><a href="#chunked-stream-input-chunking-p"><code>chunked-stream-input-chunking-p</code></a>
+          <li><a href="#chunked-stream-output-chunking-p"><code>chunked-stream-output-chunking-p</code></a>
+          <li><a href="#chunked-input-stream-extensions"><code>chunked-input-stream-extensions</code></a>
+          <li><a href="#chunked-input-stream-trailers"><code>chunked-input-stream-trailers</code></a>
+        </ol>
+      <li><a href="#conditions">Conditions</a>
+        <ol>
+          <li><a href="#chunga-condition"><code>chunga-condition</code></a>
+          <li><a href="#chunga-error"><code>chunga-error</code></a>
+          <li><a href="#chunga-warning"><code>chunga-warning</code></a>
+          <li><a href="#syntax-error"><code>syntax-error</code></a>
+          <li><a href="#parameter-error"><code>parameter-error</code></a>
+          <li><a href="#input-chunking-body-corrupted"><code>input-chunking-body-corrupted</code></a>
+          <li><a href="#input-chunking-unexpected-end-of-file"><code>input-chunking-unexpected-end-of-file</code></a>
+        </ol>
+      <li><a href="#parse">RFC 2616 parsing</a>
+        <ol>
+          <li><a href="#with-character-stream-semantics"><code>with-character-stream-semantics</code></a>
+          <li><a href="#read-line*"><code>read-line*</code></a>
+          <li><a href="#read-http-headers"><code>read-http-headers</code></a>
+          <li><a href="#token-char-p"><code>token-char-p</code></a>
+          <li><a href="#read-token"><code>read-token</code></a>
+          <li><a href="#read-name-value-pair"><code>read-name-value-pair</code></a>
+          <li><a href="#read-name-value-pairs"><code>read-name-value-pairs</code></a>
+          <li><a href="#assert-char"><code>assert-char</code></a>
+          <li><a href="#skip-whitespace"><code>skip-whitespace</code></a>
+          <li><a href="#read-char*"><code>read-char*</code></a>
+          <li><a href="#peek-char*"><code>peek-char*</code></a>
+          <li><a href="#trim-whitespace"><code>trim-whitespace</code></a>
+          <li><a href="#*current-error-message*"><code>*current-error-message*</code></a>
+          <li><a href="#*accept-bogus-eols*"><code>*accept-bogus-eols*</code></a>
+          <li><a href="#*treat-semicolon-as-continuation*"><code>*treat-semicolon-as-continuation*</code></a>
+          <li><a href="#as-keyword"><code>as-keyword</code></a>
+          <li><a href="#as-capitalized-string"><code>as-capitalized-string</code></a>
+        </ol>
+    </ol>
+  <li><a href="#ack">Acknowledgements</a>
+</ol>
+
+<br>&nbsp;<br><h3><a class=none name="download">Download and installation</a></h3>
+
+Chunga together with this documentation can be downloaded
+from <a href="https://github.com/edicl/chunga/releases/latest">Github</a>. The
+current version is 1.1.7. Chunga will only
+work with Lisps where
+the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#character_code">character
+codes</a> of
+all <a href="http://en.wikipedia.org/wiki/ISO_8859-1">Latin-1</a>
+characters coincide with their
+Unicode <a href="http://en.wikipedia.org/wiki/Code_point">code
+points</a> (which is the case for all current implementations I know).
+<p>
+The esieast way to install Chunga is with <a href="https://www.quicklisp.org/">Quicklisp</a>
+<p>
+The current development version of Chunga can be found
+at <a href="https://github.com/edicl/chunga">https://github.com/edicl/chunga</a>.
+
+<br>&nbsp;<br><h3><a name="support" class=none>Support</a></h3>
+
+The development version of chunga can be
+found <a href="https://github.com/edicl/chunga" target="_new">on
+github</a>.  Please use the github issue tracking system to submit bug
+reports.  Patches are welcome, please
+use <a href="https://github.com/edicl/chunga/pulls">GitHub pull
+requests</a>.
+
+<br>&nbsp;<br><h3><a class=none name="dictionary">The Chunga dictionary</a></h3>
+
+<h4><a name="streams" class=none>Chunked streams</a></h4>
+
+<em>Chunked streams</em> are the core of the <a href="http://globalia.net/donlope/fz/songs/Chunga's_Revenge.html">Chunga</a> library.  You
+create them using the
+function <a
+href="#make-chunked-stream"><code>MAKE-CHUNKED-STREAM</code></a> which
+takes an open binary stream (called the <em>underlying</em> stream) as its single argument.
+A <em>binary</em> stream in this context means that if it's an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_i.htm#input">input
+stream</a>, you can
+apply <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_seq.htm"><code>READ-SEQUENCE</code></a>
+to it where the sequence is an array of element
+type <a href="http://edicl.github.io/flexi-streams/#octet"><code>OCTET</code></a>, and similarly for <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_wr_seq.htm"><code>WRITE-SEQUENCE</code></a> and <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_o.htm#output">output streams</a>.  (Note that this specifically holds for <a href="http://www.lispworks.com/documentation/lw50/LWRM/html/lwref-91.htm"><em>bivalent</em> streams</a> like socket streams.)
+<p>
+A chunked stream behaves like an ordinary Lisp stream
+of <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_e.htm#element_type">element
+type</a> <a
+href="http://edicl.github.io/flexi-streams/#octet"><code>OCTET</code></a>
+with the addition that you can turn <em>chunking</em> on and off for
+input as well as for output.  With chunking turned on, data is read or
+written according to
+the <a href="http://www.rfc.net/rfc2616.html#s3.6.1">definition in RFC
+2616</a>.
+
+<!-- Entry for CHUNKED-STREAM -->
+
+<p><br>[Standard class]<br><a class=none name='chunked-stream'><b>chunked-stream</b></a>
+<blockquote><br>
+
+Every <a href="#stream">chunked stream</a> returned by
+<a href="#make-chunked-stream"><code>MAKE-CHUNKED-STREAM</code></a> is of this type which is a subtype of
+<a href="http://www.lispworks.com/documentation/HyperSpec/Body/t_stream.htm"><code>STREAM</code></a>.
+
+</blockquote>
+
+<!-- End of entry for CHUNKED-STREAM -->
+
+
+<!-- Entry for CHUNKED-INPUT-STREAM -->
+
+<p><br>[Standard class]<br><a class=none name='chunked-input-stream'><b>chunked-input-stream</b></a>
+<blockquote><br>
+
+A <a href="#stream">chunked stream</a> is of this type if its
+underlying stream is an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_i.htm#input">input
+stream</a>. This is a subtype of
+<a href="#chunked-stream"><code>CHUNKED-STREAM</code></a>.
+
+</blockquote>
+
+<!-- End of entry for CHUNKED-INPUT-STREAM -->
+
+
+
+
+<!-- Entry for CHUNKED-OUTPUT-STREAM -->
+
+<p><br>[Standard class]<br><a class=none name='chunked-output-stream'><b>chunked-output-stream</b></a>
+<blockquote><br>
+
+A <a href="#stream">chunked stream</a> is of this type if its
+underlying stream is an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_o.htm#output">output stream</a>. This is a subtype of
+<a href="#chunked-stream"><code>CHUNKED-STREAM</code></a>.
+
+</blockquote>
+
+<!-- End of entry for CHUNKED-OUTPUT-STREAM -->
+
+
+<!-- Entry for CHUNKED-IO-STREAM -->
+
+<p><br>[Standard class]<br><a class=none name='chunked-io-stream'><b>chunked-io-stream</b></a>
+<blockquote><br>
+
+A <a href="#stream">chunked stream</a> is of this type if it is both
+a <a href="#chunked-input-stream"><code>CHUNKED-INPUT-STREAM</code></a> as well as a <a href="#chunked-output-stream"><code>CHUNKED-OUTPUT-STREAM</code></a>.
+
+</blockquote>
+
+<!-- End of entry for CHUNKED-IO-STREAM -->
+
+<!-- Entry for MAKE-CHUNKED-STREAM -->
+
+<p><br>[Function]<br><a class=none name='make-chunked-stream'><b>make-chunked-stream</b> <i>stream</i> =&gt; <i>chunked-stream</i></a>
+<blockquote><br>
+
+Creates and returns a <a href="#stream">chunked stream</a> (a stream of type
+<a href="#chunked-stream"><code>CHUNKED-STREAM</code></a>) which wraps <code><i>stream</i></code>.  <code><i>stream</i></code> must be an open
+binary stream.
+
+</blockquote>
+
+<!-- End of entry for MAKE-CHUNKED-STREAM -->
+
+
+<!-- Entry for CHUNKED-STREAM-STREAM -->
+
+<p><br>[Specialized reader]<br><a class=none name='chunked-stream-stream'><b>chunked-stream-stream</b> <i>(stream chunked-stream)</i> =&gt; <i>underlying-stream</i></a>
+<blockquote><br>
+
+Returns the <a href="#stream">underlying stream</a> of the <a href="#chunked-stream">chunked stream</a> <code><i>stream</i></code>.
+
+</blockquote>
+
+<!-- End of entry for CHUNKED-STREAM-STREAM -->
+
+
+<!-- Entry for CHUNKED-STREAM-INPUT-CHUNKING-P -->
+
+<p><br>[Generic reader]<br><a class=none name='chunked-stream-input-chunking-p'><b>chunked-stream-input-chunking-p</b> <i>object</i> =&gt; <i>generalized-boolean</i></a>
+
+<blockquote><br>
+
+Returns a true value if <code><i>object</i></code> is of type <a href="#chunked-input-stream"><code>CHUNKED-INPUT-STREAM</code></a> and if input chunking is currently enabled.
+
+</blockquote>
+
+<p><br>[Specialized writer]<br><a class=none><tt>(setf (</tt><b>chunked-stream-input-chunking-p</b> <i>(stream chunked-input-stream)</i><tt>)</tt> <i>new-value</i><tt>)</tt></a>
+
+<blockquote><br>
+
+This function is used to switch input chunking
+on <code><i>stream</i></code> on or off.  Note that input chunking will
+usally be turned off automatically when the last chunk is read.
+
+</blockquote>
+
+<!-- End of entry for CHUNKED-STREAM-INPUT-CHUNKING-P -->
+
+
+
+<!-- Entry for CHUNKED-STREAM-OUTPUT-CHUNKING-P -->
+
+<p><br>[Generic reader]<br><a class=none name='chunked-stream-output-chunking-p'><b>chunked-stream-output-chunking-p</b> <i>object</i> =&gt; <i>generalized-boolean</i></a>
+
+<blockquote><br>
+
+Returns a true value if <code><i>object</i></code> is of type <a href="#chunked-output-stream"><code>CHUNKED-OUTPUT-STREAM</code></a> and if output chunking is currently enabled.
+
+</blockquote>
+
+<p><br>[Specialized writer]<br><a class=none><tt>(setf (</tt><b>chunked-stream-output-chunking-p</b> <i>(stream chunked-output-stream)</i><tt>)</tt> <i>new-value</i><tt>)</tt></a>
+
+<blockquote><br>
+
+This function is used to switch output chunking
+on <code><i>stream</i></code> on or off.
+
+</blockquote>
+
+<!-- End of entry for CHUNKED-STREAM-OUTPUT-CHUNKING-P -->
+
+
+
+
+<!-- Entry for CHUNKED-INPUT-STREAM-EXTENSIONS -->
+
+<p><br>[Specialized reader]<br><a class=none name='chunked-input-stream-extensions'><b>chunked-input-stream-extensions</b> <i>(stream chunked-input-stream)</i> =&gt; <i>extensions</i></a>
+<blockquote><br>
+
+Returns
+an <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#alist">alist</a>
+of <a href="http://www.rfc.net/rfc2616.html#s3.6">attribute/value pairs</a> corresponding to the optional <a href="http://www.rfc.net/rfc2616.html#s3.6.1">"chunk
+extensions"</a> which might have been encountered when reading
+from <code><i>stream</i></code>.
+
+</blockquote>
+
+<!-- End of entry for CHUNKED-INPUT-STREAM-EXTENSIONS -->
+
+
+<!-- Entry for CHUNKED-INPUT-STREAM-EXTENSIONS -->
+
+<p><br>[Specialized reader]<br><a class=none name='chunked-input-stream-trailers'><b>chunked-input-stream-trailers</b> <i>(stream chunked-input-stream)</i> =&gt; <i>trailers</i></a>
+<blockquote><br>
+
+Returns the
+optional <a href="http://www.rfc.net/rfc2616.html#s3.6.1">"trailer"
+HTTP headers</a> which might have been sent after the last chunk,
+i.e. directly before input chunking ended on <code><i>stream</i></code>.
+The format of <code><i>trailers</i></code> is identical to that returned
+by <a href="#read-http-headers"><code>READ-HTTP-HEADERS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for CHUNKED-INPUT-STREAM-EXTENSIONS -->
+
+
+
+
+<h4><a name="conditions" class=none>Conditions</a></h4>
+
+Here are conditions which might be signalled if something bad happens
+with a chunked stream.
+
+<!-- Entry for CHUNGA-CONDITION -->
+
+<p><br>[Condition]
+<br><a class=none name="chunga-condition"><b>chunga-condition</b></a>
+
+<blockquote><br>
+All conditions signalled by Chunga are of this type.  This is a subtype of <a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_cnd.htm"><code>CONDITION</code></a>.
+</blockquote>
+
+<!-- End of entry for CHUNGA-CONDITION -->
+
+<!-- Entry for CHUNGA-ERROR -->
+
+<p><br>[Error]
+<br><a class=none name="chunga-error"><b>chunga-error</b></a>
+
+<blockquote><br>
+All errors signalled by Chunga are of this type.  This is a subtype of <a href="#chunga-condition"><code>CHUNGA-CONDITION</code></a> and of
+<a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_stm_er.htm"><code>STREAM-ERROR</code></a>,
+so <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_stm_er.htm"><code>STREAM-ERROR-STREAM</code></a>
+can be used to access the offending stream.
+</blockquote>
+
+<!-- End of entry for CHUNGA-ERROR -->
+
+<!-- Entry for CHUNGA-WARNING -->
+
+<p><br>[Warning]
+<br><a class=none name="chunga-warning"><b>chunga-warning</b></a>
+
+<blockquote><br>
+All warnings signalled by Chunga are of this type.  This is a subtype of <a href="#chunga-condition"><code>CHUNGA-CONDITION</code></a> and of <a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_warnin.htm"><code>WARNING</code></a>.
+</blockquote>
+
+<!-- End of entry for CHUNGA-WARNING -->
+
+<!-- Entry for SYNTAX-ERROR -->
+
+<p><br>[Error]
+<br><a class=none name="syntax-error"><b>syntax-error</b></a>
+
+<blockquote><br> An error of this type is signalled if Chunga
+encounters wrong or unknown syntax when reading data.  This is a
+subtype of <a href="#chunga-error"><code>CHUNGA-ERROR</code></a>.
+</blockquote>
+
+<!-- End of entry for SYNTAX-ERROR -->
+
+<!-- Entry for PARAMETER-ERROR -->
+
+<p><br>[Error]
+<br><a class=none name="parameter-error"><b>parameter-error</b></a>
+
+<blockquote><br> An error of this type is signalled if a function was
+called with inconsistent or illegal parameters.  This is a subtype
+of <a href="#chunga-error"><code>CHUNGA-ERROR</code></a>.
+</blockquote>
+
+<!-- End of entry for PARAMETER-ERROR -->
+
+<!-- Entry for INPUT-CHUNKING-BODY-CORRUPTED -->
+
+<p><br>[Condition type]<br><a class=none name='input-chunking-body-corrupted'><b>input-chunking-body-corrupted</b></a>
+<blockquote><br>
+
+A condition of this type is signaled if an
+unexpected character (octet) is read while reading from a
+<a href="#stream">chunked stream</a> with input chunking enabled. This is a subtype of
+<a href="#chunga-error"><code>CHUNGA-ERROR</code></a>.
+
+</blockquote>
+
+<!-- End of entry for INPUT-CHUNKING-BODY-CORRUPTED -->
+
+
+<!-- Entry for INPUT-CHUNKING-UNEXPECTED-END-OF-FILE -->
+
+<p><br>[Condition type]<br><a class=none name='input-chunking-unexpected-end-of-file'><b>input-chunking-unexpected-end-of-file</b></a>
+<blockquote><br>
+
+A condition of this type is signaled if we
+reach an unexpected EOF on a <a href="#stream">chunked stream</a> with input chunking
+enabled.  This is a subtype of
+<a href="#chunga-error"><code>CHUNGA-ERROR</code></a>.
+
+</blockquote>
+
+<!-- End of entry for INPUT-CHUNKING-UNEXPECTED-END-OF-FILE -->
+
+
+
+
+<h4><a name="parse" class=none>RFC 2616 parsing</a></h4>
+
+Chunga needs to know a bit
+about <a href="http://www.rfc.net/rfc2616.html">RFC 2616 syntax</a> in
+order to cope
+with <a href="#chunked-input-stream-extensions">extensions</a>
+and <a href="#chunked-input-stream-trailers">trailers</a>.  As these
+functions are in there anyway, they're exported, so they can be used
+by other code like for
+example <a href="https://edicl.github.io/drakma/">Drakma</a>.
+<p>
+Note that all of these functions are designed to work
+on <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_b.htm#binary">binary</a>
+streams, specifically on streams with element
+type <code>(UNSIGNED-BYTE&nbsp;8)</code>.  They will <em>not</em> work
+with character streams.  (But the "bivalent" streams offered by many
+Lisp implementations will do.)  They <em>must</em> be called within the context
+of <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+<p><br>[Macro]
+<br><a class=none name="with-character-stream-semantics"><b>with-character-stream-semantics</b> <i>statement*</i> =&gt; <i>result*</i></a>
+<blockquote><br>
+
+Executes the <code><i>statement*</i></code> forms in such a way that
+functions within this section can read characters from binary streams
+(treating octets as the Latin-1 characters with the corresponding code
+points).  All the functions below <em>must</em> be wrapped with this
+macro.  If your code uses several of these functions which interact on
+the same stream, all of them must be wrapped with the same macro.  See
+the source code of <a href="https://edicl.github.io/drakma/">Drakma</a>
+or <a href="https://edicl.github.io/hunchnetoot/">Hunchentoot</a> for examples
+of how to use this macro.
+
+</blockquote>
+
+<!-- Entry for READ-LINE* -->
+
+<p><br>[Function]<br><a class=none name='read-line*'><b>read-line*</b> <i>stream <tt>&amp;optional</tt> log-stream</i> =&gt; <i>line</i></a>
+<blockquote><br>
+
+Reads and assembles characters from the binary stream <code><i>stream</i></code> until a <a href="http://en.wikipedia.org/wiki/Carriage_return">carriage
+return</a>
+is read.  Makes sure that the following character is a <a href="http://en.wikipedia.org/wiki/Line_feed">linefeed</a>.  If
+<a href="#*accept-bogus-eols*"><code>*ACCEPT-BOGUS-EOLS*</code></a> is not <code>NIL</code>, then the function will also accept a
+lone carriage return or linefeed as a line break.  Returns
+the string of characters read excluding the line break.  Additionally
+logs this string to <code><i>log-stream</i></code> if it is not <code>NIL</code>.
+<p>
+See <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for READ-LINE* -->
+
+
+<!-- Entry for READ-HTTP-HEADERS -->
+
+<p><br>[Function]<br><a class=none name='read-http-headers'><b>read-http-headers</b> <i>stream <tt>&amp;optional</tt> log-stream</i> =&gt; <i>headers</i></a>
+<blockquote><br>
+
+Reads HTTP header lines from the binary stream <code><i>stream</i></code>
+(except for the initial status line which is supposed to be read
+already) and returns a
+corresponding <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#alist">alist</a>
+of names and values where the names are keywords and the values are
+strings.  Multiple lines with the same name are combined into one
+value, the individual values separated by commas.  Header lines which
+are spread across multiple lines are recognized and treated correctly.  (But see <a href="#*treat-semicolon-as-continuation*"><code>*TREAT-SEMICOLON-AS-CONTINUATION*</code></a>.)
+Additonally logs the header lines to
+<code><i>log-stream</i></code> if it is not <code>NIL</code>.
+<p>
+See <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for READ-HTTP-HEADERS -->
+
+<!-- Entry for READ-TOKEN -->
+
+<p><br>[Function]<br><a class=none name='read-token'><b>read-token</b> <i>stream</i> =&gt; <i>token</i></a>
+<blockquote><br>
+
+Read characters from the binary stream <code><i>stream</i></code> while they
+are <em>token</em> constituents (according
+to <a href="http://www.rfc.net/rfc2616.html">RFC 2616</a>).  It is
+assumed that there's a token character at the current position.  The
+token read is returned as a string.  Doesn't signal an error (but
+simply stops reading)
+if <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/e_end_of.htm"><code>END-OF-FILE</code></a>
+is encountered after the first character.
+<p>
+See <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for READ-TOKEN -->
+
+<!-- Entry for TOKEN-CHAR-P -->
+
+<p><br>[Function]<br><a class=none name='token-char-p'><b>token-char-p</b> <i>char</i> =&gt; <i>generalized-boolean</i></a>
+<blockquote><br>
+
+Returns a true value if the Lisp character <code><i>char</i></code> is a token constituent
+according to
+<a href="http://www.rfc.net/rfc2616.html">RFC 2616</a>.
+
+</blockquote>
+
+<!-- End of entry for TOKEN-CHAR-P -->
+
+<!-- Entry for READ-NAME-VALUE-PAIR -->
+
+<p><br>[Function]<br><a class=none name='read-name-value-pair'><b>read-name-value-pair</b> <i>stream <tt>&amp;key</tt> value-required-p cookie-syntax</i> =&gt; <i>pair</i></a>
+<blockquote><br>
+
+Reads a typical (in <a href="http://www.rfc.net/rfc2616.html">RFC
+2616</a>) <a href="http://www.rfc.net/rfc2616.html#s3.6">name/value or
+attribute/value combination</a> from the
+binary stream <code><i>stream</i></code> - a <em>token</em> followed by
+a <code>#\=</code> character and another token or a <em>quoted
+string</em>.  Returns
+a <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#cons">cons</a>
+of the name and the value, both as strings.
+If <code><i>value-required-p</i></code> is <code>NIL</code> (the
+default is <code>T</code>), the <code>#\=</code> sign and the value
+are optional.  If <code><i>cookie-syntax</i></code> is true (the
+default is <code>NIL</code>), the value is read like the value of
+a <a href="https://edicl.github.io/drakma/#cookies">cookie</a> header.
+<p>
+See <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for READ-NAME-VALUE-PAIR -->
+
+
+<!-- Entry for READ-NAME-VALUE-PAIRS -->
+
+<p><br>[Function]<br><a class=none name='read-name-value-pairs'><b>read-name-value-pairs</b> <i>stream <tt>&amp;key</tt> value-required-p cookie-syntax</i> =&gt; <i>pairs</i></a>
+<blockquote><br>
+
+Uses <a href="#read-name-value-pair"><code>READ-NAME-VALUE-PAIR</code></a> to read and return an <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#alist">alist</a> of
+name/value pairs from the binary stream <code><i>stream</i></code>.  It is assumed that the pairs are
+separated by semicolons and that the first char read (except for
+whitespace) will be a semicolon.  The parameters are used as in
+<a href="#read-name-value-pair"><code>READ-NAME-VALUE-PAIR</code></a>.
+Stops reading in case
+of <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/e_end_of.htm"><code>END-OF-FILE</code></a>
+(instead of signaling an error).
+<p>
+See <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for READ-NAME-VALUE-PAIRS -->
+
+
+
+<!-- Entry for ASSERT-CHAR -->
+
+<p><br>[Function]<br><a class=none name='assert-char'><b>assert-char</b> <i>stream expected-char</i> =&gt; <i>char</i></a>
+<blockquote><br>
+
+Reads the next character from the binary stream <code><i>stream</i></code> and checks if it is the
+character <code><i>expected-char</i></code>.  Signals an error otherwise.
+<p>
+See <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for ASSERT-CHAR -->
+
+
+<!-- Entry for SKIP-WHITESPACE -->
+
+<p><br>[Function]<br><a class=none name='skip-whitespace'><b>skip-whitespace</b> <i>stream</i> =&gt; <i>char-or-nil</i></a>
+<blockquote><br>
+
+Consume characters from the binary stream <code><i>stream</i></code> until an <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/e_end_of.htm"><code>END-OF-FILE</code></a> is
+encountered or a non-whitespace (according to <a href="http://www.rfc.net/rfc2616.html">RFC 2616</a>)
+characters is seen.  This character is returned (or <code>NIL</code> in case
+of <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/e_end_of.htm"><code>END-OF-FILE</code></a>).
+<p>
+See <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for SKIP-WHITESPACE -->
+
+
+<!-- Entry for READ-CHAR* -->
+
+<p><br>[Function]<br><a class=none name='read-char*'><b>read-char*</b> <i>stream</i> =&gt; <i>char</i></a>
+<blockquote><br>
+
+Reads and returns the next character from the binary stream <code><i>stream</i></code>.
+<p>
+See <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for READ-CHAR* -->
+
+
+<!-- Entry for PEEK-CHAR* -->
+
+<p><br>[Function]<br><a class=none name='peek-char*'><b>peek-char*</b> <i>stream <tt>&amp;optional</tt> eof-error-p eof-value</i>  =&gt; <i>boolean</i></a>
+<blockquote><br>
+
+Returns a true value if a character can be read from the binary
+stream <code><i>stream</i></code>.  If <code><i>eof-error-p</i></code>
+has a true value, an error is signalled if no character remains to be
+read.  <code><i>eof-value</i></code> specifies the value to return
+if <code><i>eof-error-p</i></code> is false and the end of the file
+has been reached.
+<p>
+See <a href="#with-character-stream-semantics"><code>WITH-CHARACTER-STREAM-SEMANTICS</code></a>.
+
+</blockquote>
+
+<!-- End of entry for PEEK-CHAR* -->
+
+
+<!-- Entry for TRIM-WHITESPACE -->
+
+<p><br>[Function]<br><a class=none name='trim-whitespace'><b>trim-whitespace</b> <i>string <tt>&amp;key</tt> start end</i> =&gt; <i>string'</i></a>
+<blockquote><br>
+
+Returns a version of the string <code><i>string</i></code> (between <code><i>start</i></code> and <code><i>end</i></code>) where spaces and tab
+characters are trimmed from the start and the end.
+
+</blockquote>
+
+<!-- End of entry for TRIM-WHITESPACE -->
+
+
+<!-- Entry for *CURRENT-ERROR-MESSAGE* -->
+
+<p><br>[Special variable]<br><a class=none name='*current-error-message*'><b>*current-error-message*</b></a>
+<blockquote><br>
+
+Used by the parsing functions in <a href="#parse">this section</a> as
+an introduction to a standardized error message.  Should be bound to a
+string or <code>NIL</code> if one of these functions is called.
+
+</blockquote>
+
+<!-- End of entry for *CURRENT-ERROR-MESSAGE* -->
+
+<!-- Entry for *ACCEPT-BOGUS-EOLS* -->
+
+<p><br>[Special variable]<br><a class=none name='*accept-bogus-eols*'><b>*accept-bogus-eols*</b></a>
+<blockquote><br>
+
+Some web servers do not respond with a correct CRLF line ending for
+HTTP headers but with a lone linefeed or carriage return instead.  If
+this variable is bound to a true
+value, <a href="#read-line*"><code>READ-LINE*</code></a> will treat a
+lone LF or CR character as an acceptable end of line.  The initial
+value is <code>NIL</code>.
+
+</blockquote>
+<!-- End of entry for *ACCEPT-BOGUS-EOLS* -->
+
+<!-- Entry for *TREAT-SEMICOLON-AS-CONTINUATION* -->
+
+<p><br>[Special variable]<br><a class=none name='*treat-semicolon-as-continuation*'><b>*treat-semicolon-as-continuation*</b></a>
+<blockquote><br>
+
+According to John Foderaro, Netscape v3 web servers bogusly split
+<code>Set-Cookie</code> headers over multiple lines which means that we'd have to
+treat <code>Set-Cookie</code> headers ending with a semicolon as incomplete and
+combine them with the next header.  This will only be done if this
+variable has a true value, though.  Its default value is <code>NIL</code>.
+</blockquote>
+
+<!-- End of entry for *TREAT-SEMICOLON-AS-CONTINUATION* -->
+
+<p><br>[Function]<br><a class=none name='as-keyword'><b>as-keyword</b> <i>string <tt>&amp;key</tt> destructivep</i> =&gt; <i>keyword</i></a>
+<blockquote><br>
+Converts the string <code><i>string</i></code> to a keyword where all characters are
+uppercase or lowercase, taking into account the current readtable
+case.  Might destructively modify <code><i>string</i></code> if <code><i>destructivep</i></code> is true which
+is the default.  "Knows" several HTTP header names and methods and
+is optimized to not call <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_intern.htm"><code>INTERN</code></a> for these.
+</blockquote>
+
+<p><br>[Function]<br><a class=none name='as-capitalized-string'><b>as-capitalized-string</b> <i>keyword</i> =&gt; <i>capitalized-string</i></a>
+<blockquote><br>
+Kind of the inverse of <a href="#as-keyword"><code>AS-KEYWORD</code></a>.  Has essentially the same effect
+as <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_stg_up.htm"><code>STRING-CAPITALIZE</code></a> but is optimized for "known" keywords like
+<code>:CONTENT-LENGTH</code> or <code>:GET</code>.
+</blockquote>
+
+
+<br>&nbsp;<br><h3><a class=none name="ack">Acknowledgements</a></h3>
+
+<p>
+Thanks to Jochen Schmidt's chunking code in <a href="http://www.cliki.net/ACL-COMPAT">ACL-COMPAT</a> for inspiration.
+This documentation was prepared with <a href="https://edicl.github.io/documentation-template/">DOCUMENTATION-TEMPLATE</a>.
+</p>
+
+</body>
+</html>
diff --git a/deps/chunga/input.lisp b/deps/chunga/input.lisp
new file mode 100644 (file)
index 0000000..bed1b13
--- /dev/null
@@ -0,0 +1,185 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CHUNGA; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/chunga/input.lisp,v 1.18 2008/05/24 03:06:22 edi Exp $
+
+;;; Copyright (c) 2006-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :chunga)
+
+(defmethod chunked-input-stream-extensions ((object t))
+  "The default method which always returns the empty list."
+  nil)
+
+(defmethod chunked-input-stream-trailers ((object t))
+  "The default method which always returns the empty list."
+  nil)
+
+(defmethod chunked-stream-input-chunking-p ((object t))
+  "The default method for all objects which are not of type
+CHUNKED-INPUT-STREAM."
+  nil)
+
+(defmethod (setf chunked-stream-input-chunking-p) (new-value (stream chunked-input-stream))
+  "Switches input chunking for STREAM on or off."
+  (unless (eq (not new-value) (not (chunked-stream-input-chunking-p stream)))
+    (with-slots (input-limit input-index expecting-crlf-p chunk-extensions chunk-trailers)
+        stream
+      (cond (new-value
+             (setq expecting-crlf-p nil
+                   input-limit 0
+                   input-index 0
+                   chunk-extensions nil
+                   chunk-trailers nil))
+            (t (when (< input-index input-limit)
+                 (error 'parameter-error
+                        :stream stream
+                        :format-control "Not all chunks from ~S have been read completely."
+                        :format-arguments (list stream)))))))
+  (setf (slot-value stream 'input-chunking-p) new-value))
+
+(defmethod stream-clear-input ((stream chunked-input-stream))
+  "Implements CLEAR-INPUT by resetting the internal chunk buffer."
+  (when (chunked-stream-input-chunking-p stream)
+    (setf (chunked-stream-input-index stream) 0
+          (chunked-stream-input-limit stream) 0))
+  ;; clear input on inner stream
+  (clear-input (chunked-stream-stream stream))
+  nil)
+
+(defmethod chunked-input-available-p ((stream chunked-input-stream))
+  "Whether there's unread input waiting in the chunk buffer."
+  (< (chunked-stream-input-index stream)
+     (chunked-stream-input-limit stream)))
+
+(defmethod stream-listen ((stream chunked-input-stream))
+  "We first check if input chunking is enabled and if there's
+something in the buffer.  Otherwise we poll the underlying stream."
+  (cond ((chunked-stream-input-chunking-p stream)
+         (or (chunked-input-available-p stream)
+             (fill-buffer stream)))
+        (t (listen (chunked-stream-stream stream)))))
+
+(defmethod fill-buffer ((stream chunked-input-stream))
+  "Re-fills the chunk buffer.  Returns NIL if chunking has ended."
+  (let ((inner-stream (chunked-stream-stream stream))
+        ;; set up error function for the functions in `read.lisp'
+        (*current-error-function*
+         (lambda (last-char expected-chars)
+             "The function which is called when an unexpected
+character is seen.  Signals INPUT-CHUNKING-BODY-CORRUPTED."
+             (error 'input-chunking-body-corrupted
+                    :stream stream
+                    :last-char last-char
+                    :expected-chars expected-chars))))
+    (labels ((add-extensions ()
+               "Reads chunk extensions \(if there are any) and stores
+them into the corresponding slot of the stream."
+               (when-let (extensions (read-name-value-pairs inner-stream))
+                 (warn 'chunga-warning
+                       :stream stream
+                       :format-control "Adding uninterpreted extensions to stream ~S."
+                       :format-arguments (list stream))
+                 (setf (slot-value stream 'chunk-extensions)
+                       (append (chunked-input-stream-extensions stream) extensions)))
+               (assert-crlf inner-stream))
+             (get-chunk-size ()
+               "Reads chunk size header \(including optional
+extensions) and returns the size."
+               (with-character-stream-semantics
+                 (when (expecting-crlf-p stream)
+                   (assert-crlf inner-stream))
+                 (setf (expecting-crlf-p stream) t)
+                 ;; read hexadecimal number
+                 (let (last-char)
+                   (prog1 (loop for weight = (digit-char-p (setq last-char (read-char* inner-stream))
+                                                           16)
+                                for result = (if weight
+                                               (+ weight (* 16 (or result 0)))
+                                               (return (or result
+                                                           (error 'input-chunking-body-corrupted
+                                                                  :stream stream
+                                                                  :last-char last-char
+                                                                  :expected-chars +hex-digits+)))))
+                     ;; unread first octet which wasn't a digit
+                     (unread-char* last-char)
+                     (add-extensions))))))
+      (let ((chunk-size (get-chunk-size)))
+        (with-slots (input-buffer input-limit input-index)
+            stream
+          (setq input-index 0
+                input-limit chunk-size)
+          (cond ((zerop chunk-size)
+                 ;; turn chunking off
+                 (setf (chunked-stream-input-chunking-p stream) nil
+                       (slot-value stream 'chunk-trailers) (with-character-stream-semantics
+                                                             (read-http-headers inner-stream))
+                       input-limit 0)
+                 ;; return NIL
+                 (return-from fill-buffer))
+                ((> chunk-size (length input-buffer))
+                 ;; replace buffer if it isn't big enough for the next chunk
+                 (setq input-buffer (make-array chunk-size :element-type '(unsigned-byte 8)))))
+          (unless (= (read-sequence input-buffer inner-stream :start 0 :end chunk-size)
+                     chunk-size)
+            (error 'input-chunking-unexpected-end-of-file
+                   :stream stream))
+          chunk-size)))))
+
+(defmethod stream-read-byte ((stream chunked-input-stream))
+  "Reads one byte from STREAM.  Checks the chunk buffer first, if
+input chunking is enabled.  Re-fills buffer is necessary."
+  (unless (chunked-stream-input-chunking-p stream)
+    (return-from stream-read-byte (read-byte (chunked-stream-stream stream) nil :eof)))
+  (unless (chunked-input-available-p stream)
+    (unless (fill-buffer stream)
+      (return-from stream-read-byte :eof)))
+  (with-slots (input-buffer input-index)
+      stream
+    (prog1 (aref input-buffer input-index)
+      (incf input-index))))
+
+(defmethod stream-read-sequence ((stream chunked-input-stream) sequence start end &key)
+  "Fills SEQUENCE by adding data from the chunk buffer and re-filling
+it until enough data was read.  Works directly on the underlying
+stream if input chunking is off."
+  (unless (chunked-stream-input-chunking-p stream)
+    (return-from stream-read-sequence
+      (read-sequence sequence (chunked-stream-stream stream) :start start :end end)))
+  (loop
+   (when (>= start end)
+     (return-from stream-read-sequence start))   
+   (unless (chunked-input-available-p stream)
+     (unless (fill-buffer stream)
+       (return-from stream-read-sequence start)))
+   (with-slots (input-buffer input-limit input-index)
+       stream
+     (replace sequence input-buffer
+              :start1 start :end1 end
+              :start2 input-index :end2 input-limit)
+     (let ((length (min (- input-limit input-index)
+                        (- end start))))
+       (incf start length)
+       (incf input-index length)))))
diff --git a/deps/chunga/known-words.lisp b/deps/chunga/known-words.lisp
new file mode 100644 (file)
index 0000000..92b391d
--- /dev/null
@@ -0,0 +1,152 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CHUNGA; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/chunga/known-words.lisp,v 1.3 2008/05/29 22:21:09 edi Exp $
+
+;;; Copyright (c) 2006-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :chunga)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (define-constant +known-words+
+    '(;; headers including WebDAV and some de facto standard headers
+      "Accept"
+      "Accept-Charset"
+      "Accept-Encoding"
+      "Accept-Language"
+      "Accept-Ranges"
+      "Age"
+      "Allow"
+      "Authorization"
+      "Cache-Control"
+      "Connection"
+      "Content-Encoding"
+      "Content-Language"
+      "Content-Length"
+      "Content-Location"
+      "Content-MD5"
+      "Content-Range"
+      "Content-Type"
+      "DAV"
+      "Date"
+      "Depth"
+      "Destination"
+      "ETag"
+      "Expect"
+      "Expires"
+      "From"
+      "Host"
+      "If"
+      "If-Match"
+      "If-Modified-Since"
+      "If-None-Match"
+      "If-Range"
+      "If-Unmodified-Since"
+      "Last-Modified"
+      "Location"
+      "Lock-Token"
+      "Max-Forwards"
+      "Overwrite"
+      "Pragma"
+      "Proxy-Authenticate"
+      "Proxy-Authorization"
+      "Range"
+      "Referer"
+      "Retry-After"
+      "Server"
+      "TE"
+      "TimeOut"
+      "Trailer"
+      "Transfer-Encoding"
+      "Upgrade"
+      "User-Agent"
+      "Vary"
+      "Via"
+      "WWW-Authenticate"
+      "Warning"
+      ;; methods including WebDAV
+      "CONNECT"
+      "COPY"
+      "DELETE"
+      "GET"
+      "HEAD"
+      "LOCK"
+      "MKCOL"
+      "MOVE"
+      "OPTIONS"
+      "POST"
+      "PROPFIND"
+      "PROPPATCH"
+      "PUT"
+      "TRACE"
+      "UNLOCK"
+      ;; protocols
+      "HTTP/1.1"
+      "HTTP/1.0"
+      ;; only a few and only the "preferred MIME names" - see
+      ;; <http://www.iana.org/assignments/character-sets> for a
+      ;; complete list
+      "US-ASCII"
+      "ISO-8859-1"
+      "UTF-8"
+      "UTF-16"
+      "UTF-32BE"
+      "UTF-32LE")
+    "A list of words \(headers, methods, protocols, character sets)
+that are typically seen in HTTP communication.  Mostly from RFC 2616,
+but includes WebDAV stuff and other things as well."))
+
+(define-constant +string-to-keyword-hash+
+  (let ((hash (make-hash-table :test 'equal :size (length +known-words+))))
+    (loop for word in +known-words+
+          do (setf (gethash word hash) (make-keyword word nil)))
+    hash)
+  "A hash table which case-insensitively maps the strings from
++KNOWN-WORDS+ to keywords.")
+
+(define-constant +keyword-to-string-hash+
+  (let ((hash (make-hash-table :test 'eq :size (length +known-words+))))
+    (loop for word in +known-words+
+          do (setf (gethash (make-keyword word nil) hash)
+                   (string-capitalize word)))
+    hash)
+  "A hash table which maps keywords derived from +KNOWN-WORDS+ to
+capitalized strings.")
+
+(defun as-keyword (string &key (destructivep t))
+  "Converts the string STRING to a keyword where all characters are
+uppercase or lowercase, taking into account the current readtable
+case.  Might destructively modify STRING if DESTRUCTIVEP is true which
+is the default.  \"Knows\" several HTTP header names and methods and
+is optimized to not call INTERN for these."
+  (or (gethash string +string-to-keyword-hash+)
+      (make-keyword string destructivep)))
+
+(defun as-capitalized-string (keyword)
+  "Kind of the inverse of AS-KEYWORD.  Has essentially the same effect
+as STRING-CAPITALIZE but is optimized for \"known\" keywords like
+:CONTENT-LENGTH or :GET."
+  (or (gethash keyword +keyword-to-string-hash+)
+      (string-capitalize keyword)))
diff --git a/deps/chunga/output.lisp b/deps/chunga/output.lisp
new file mode 100644 (file)
index 0000000..6029aa6
--- /dev/null
@@ -0,0 +1,137 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CHUNGA; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/chunga/output.lisp,v 1.14 2008/05/24 03:06:22 edi Exp $
+
+;;; Copyright (c) 2006-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :chunga)
+
+(defmethod chunked-stream-output-chunking-p ((object t))
+  "The default method for all objects which are not of type
+CHUNKED-OUTPUT-STREAM."
+  nil)
+
+(defmethod write-chunk ((stream chunked-output-stream) sequence
+                        &key (start 0)
+                             (end (length sequence)))
+  "Writes the contents of SEQUENCE from START to END to the
+underlying stream of STREAM as one chunk."
+  (let ((output-stream (chunked-stream-stream stream)))
+    ;; chunk size
+    (loop for char across (format nil "~X" (- end start))
+          do (write-byte (char-code char) output-stream))
+    (write-sequence +crlf+ output-stream)
+    ;; data
+    (write-sequence sequence output-stream :start start :end end)
+    (write-sequence +crlf+ output-stream)))
+
+(defmethod flush-buffer ((stream chunked-output-stream))
+  "Uses WRITE-CHUNK to empty the output buffer unless it is
+already empty."
+  (with-slots (output-buffer output-index)
+      stream
+    (when (plusp output-index)
+      (write-chunk stream output-buffer :end output-index)
+      (setq output-index 0))))
+
+(defmethod (setf chunked-stream-output-chunking-p) (new-value (stream chunked-output-stream))
+  "Switches output chunking for STREAM on or off."
+  (unless (eq (not new-value) (not (chunked-stream-output-chunking-p stream)))
+    (with-slots (real-stream output-index)
+        stream
+      (cond (new-value
+             ;; get rid of "old" data
+             (force-output real-stream)
+             ;; initialize output buffer as being empty
+             (setq output-index 0))
+            (t (flush-buffer stream)
+               ;; last chunk to signal end of chunking
+               (write-byte #.(char-code #\0) real-stream)
+               (write-sequence +crlf+ real-stream)
+               (write-sequence +crlf+ real-stream)
+               (force-output real-stream)))))
+  (setf (slot-value stream 'output-chunking-p) new-value))
+
+(defmethod stream-clear-output ((stream chunked-output-stream))
+  "We clear output by resetting the output buffer and clearing
+the underlying stream."
+  (when (chunked-stream-output-chunking-p stream)
+    (setf (slot-value stream 'output-index) 0))
+  (clear-output (chunked-stream-stream stream)))
+
+(defmethod stream-finish-output ((stream chunked-output-stream))
+  "Flush the output buffer if output chunking is on, then operate
+on the underlying stream."
+  (when (chunked-stream-output-chunking-p stream)
+    (flush-buffer stream))
+  (finish-output (chunked-stream-stream stream)))
+
+(defmethod stream-force-output ((stream chunked-output-stream))
+  "Flush the output buffer if output chunking is on, then operate
+on the underlying stream."
+  (when (chunked-stream-output-chunking-p stream)
+    (flush-buffer stream))
+  (force-output (chunked-stream-stream stream)))
+
+(defmethod stream-write-byte ((stream chunked-output-stream) byte)
+  "Writes one byte by simply adding it to the end of the output
+buffer \(if output chunking is enabled).  The buffer is flushed
+if necessary."
+  (unless (chunked-stream-output-chunking-p stream)
+    (return-from stream-write-byte
+      (write-byte byte (chunked-stream-stream stream))))
+  (with-slots (output-index output-buffer)
+      stream
+    (when (>= output-index +output-buffer-size+)
+      (flush-buffer stream))
+    (setf (aref output-buffer output-index) byte)
+    (incf output-index)
+    byte))
+
+(defmethod stream-write-sequence ((stream chunked-output-stream) sequence start end &key)
+  "Outputs SEQUENCE by appending it to the output buffer if it's
+small enough.  Large sequences are written directly using
+WRITE-CHUNK."
+  (unless (chunked-stream-output-chunking-p stream)
+    (return-from stream-write-sequence
+      (write-sequence sequence (chunked-stream-stream stream) :start start :end end)))
+  (with-slots (output-buffer output-index)
+      stream
+    (let ((length (- end start)))
+      (cond ((<= length (- +output-buffer-size+ output-index))
+             (replace output-buffer sequence :start1 output-index
+                      :start2 start :end2 end)
+             (incf output-index length))
+            (t (flush-buffer stream)
+               (write-chunk stream sequence :start start :end end)))))
+  sequence)
+
+(defmethod close ((stream chunked-output-stream) &key abort)
+  "When a stream is closed and ABORT isn't true we have to make
+sure to send the last chunk."
+  (unless abort
+    (setf (chunked-stream-output-chunking-p stream) nil))
+  (call-next-method))
diff --git a/deps/chunga/packages.lisp b/deps/chunga/packages.lisp
new file mode 100644 (file)
index 0000000..b2afd3d
--- /dev/null
@@ -0,0 +1,68 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/chunga/packages.lisp,v 1.19 2008/05/24 18:38:30 edi Exp $
+
+;;; Copyright (c) 2006-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(defpackage :chunga
+  (:use :cl :trivial-gray-streams)
+  #+:lispworks
+  (:import-from :lw :when-let)
+  (:export :*accept-bogus-eols*
+           :*current-error-message*
+           :*treat-semicolon-as-continuation*
+           :assert-char
+           :as-keyword
+           :as-capitalized-string
+           :chunga-error
+           :chunga-warning
+           :chunked-input-stream
+           :chunked-input-stream-extensions
+           :chunked-input-stream-trailers
+           :chunked-io-stream
+           :chunked-output-stream
+           :chunked-stream
+           :chunked-stream-input-chunking-p
+           :chunked-stream-output-chunking-p
+           :chunked-stream-stream
+           :input-chunking-body-corrupted
+           :input-chunking-unexpected-end-of-file
+           :make-chunked-stream
+           :read-http-headers
+           :peek-char*
+           :read-char*
+           :read-line*
+           :read-name-value-pair
+           :read-name-value-pairs
+           :read-token
+           :skip-whitespace
+           :syntax-error
+           :token-char-p
+           :trim-whitespace
+           :with-character-stream-semantics))
+           
diff --git a/deps/chunga/read.lisp b/deps/chunga/read.lisp
new file mode 100644 (file)
index 0000000..83c63bc
--- /dev/null
@@ -0,0 +1,293 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CHUNGA; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/chunga/read.lisp,v 1.22 2008/05/26 08:18:00 edi Exp $
+
+;;; Copyright (c) 2006-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :chunga)
+
+(defun signal-unexpected-chars (stream last-char expected-chars)
+  "Signals an error that LAST-CHAR was read although one of
+EXPECTED-CHARS was expected.  \(Note that EXPECTED-CHARS, despite its
+name, can also be a single character instead of a list).  Calls
+*CURRENT-ERROR-FUNCTION* if it's not NIL, or uses
+*CURRENT-ERROR-MESSAGE* otherwise."
+  (cond (*current-error-function*
+         (funcall *current-error-function* last-char expected-chars))
+        (t
+         (error 'syntax-error
+                :stream stream
+                :format-control "~@[~A~%~]~:[End of file~;Read character ~:*~S~], ~
+but expected ~:[a member of ~S~;~S~]."
+                :format-arguments (list *current-error-message*
+                                        last-char
+                                        (atom expected-chars)
+                                        expected-chars)))))
+
+(defun charp (char)
+  "Returns true if the Lisp character CHAR is a CHAR according to RFC 2616."
+  (<= 0 (char-code char) 127))
+
+(defun controlp (char)
+  "Returns true if the Lisp character CHAR is a CTL according to RFC 2616."
+  (or (<= 0 (char-code char) 31)
+      (= (char-code char) 127)))
+
+(defun separatorp (char)
+  "Returns true if the Lisp character CHAR is a separator
+according to RFC 2616."
+  (find char #.(format nil " ()<>@,;:\\\"/[]?={}~C" #\Tab)
+        :test #'char=))
+
+(defun whitespacep (char)
+  "Returns true if the Lisp character CHAR is whitespace
+according to RFC 2616."
+  (member char '(#\Space #\Tab) :test #'char=))
+
+(defun token-char-p (char)
+  "Returns true if the Lisp character CHAR is a token constituent
+according to RFC 2616."
+  (and (charp char)
+       (not (or (controlp char)
+                (separatorp char)))))
+
+(defun assert-char (stream expected-char)
+  "Reads the next character from STREAM and checks if it is the
+character EXPECTED-CHAR.  Signals an error otherwise."
+  (let ((char (read-char* stream)))
+    (unless (char= char expected-char)
+      (signal-unexpected-chars stream char expected-char))
+    char))
+
+(defun assert-crlf (stream)
+  "Reads the next two characters from STREAM and checks if these
+are a carriage return and a linefeed.  Signals an error
+otherwise."
+  (assert-char stream #\Return)
+  (assert-char stream #\Linefeed))
+
+(defun read-line* (stream &optional log-stream)
+  "Reads and assembles characters from the binary stream STREAM until
+a carriage return is read.  Makes sure that the following character is
+a linefeed.  If *ACCEPT-BOGUS-EOLS* is not NIL, then the function will
+also accept a lone carriage return or linefeed as an acceptable line
+break.  Returns the string of characters read excluding the line
+break.  Returns NIL if input ends before one character was read.
+Additionally logs this string to LOG-STREAM if it is not NIL."
+  (let ((result
+         (with-output-to-string (line)
+           (loop for char-seen-p = nil then t
+                 for char = (read-char* stream nil)
+                 for is-cr-p = (and char (char= char #\Return))
+                 until (or (null char)
+                           is-cr-p
+                           (and *accept-bogus-eols*
+                                (char= char #\Linefeed)))
+                 do (write-char char line)
+                 finally (cond ((and (not char-seen-p)
+                                     (null char))
+                                (return-from read-line* nil))
+                               ((not *accept-bogus-eols*)
+                                (assert-char stream #\Linefeed))
+                               (is-cr-p
+                                (when (eql (peek-char* stream) #\Linefeed)
+                                  (read-char* stream))))))))
+    (when log-stream
+      (write-line result log-stream)
+      (finish-output log-stream))
+    result))
+
+(defun trim-whitespace (string &key (start 0) (end (length string)))
+  "Returns a version of the string STRING \(between START and END)
+where spaces and tab characters are trimmed from the start and the
+end.  Might return STRING."
+  ;; optimized version to replace STRING-TRIM, suggested by Jason Kantz
+  (declare (optimize
+            speed
+            (space 0)
+            (debug 1)
+            (compilation-speed 0)
+            #+:lispworks (hcl:fixnum-safety 0)))
+  (declare (string string))
+  (let* ((start% (loop for i of-type fixnum from start below end
+                       while (or (char= #\space (char string i))
+                                 (char= #\tab (char string i)))
+                       finally (return i)))
+         (end% (loop for i of-type fixnum downfrom (1- end) to start
+                     while (or (char= #\space (char string i))
+                               (char= #\tab (char string i)))
+                     finally (return (1+ i)))))
+    (declare (fixnum start% end%))
+    (cond ((and (zerop start%) (= end% (length string))) string)
+          ((> start% end%) "")
+          (t (subseq string start% end%)))))
+
+(defun read-http-headers (stream &optional log-stream)
+  "Reads HTTP header lines from STREAM \(except for the initial
+status line which is supposed to be read already) and returns a
+corresponding alist of names and values where the names are
+keywords and the values are strings.  Multiple lines with the
+same name are combined into one value, the individual values
+separated by commas.  Header lines which are spread across
+multiple lines are recognized and treated correctly.  Additonally
+logs the header lines to LOG-STREAM if it is not NIL."
+  (let (headers
+        (*current-error-message* "While reading HTTP headers:"))
+    (labels ((read-header-line ()
+               "Reads one header line, considering continuations."
+               (with-output-to-string (header-line)
+                 (loop
+                  (let ((line (trim-whitespace (read-line* stream log-stream))))
+                    (when (zerop (length line))
+                      (return))
+                    (write-sequence line header-line)
+                    (let ((next (peek-char* stream)))
+                      (unless (whitespacep next)
+                        (return)))
+                    ;; we've seen whitespace starting a continutation,
+                    ;; so we loop
+                    (write-char #\Space header-line)))))
+             (split-header (line)
+               "Splits line at colon and converts it into a cons.
+Returns NIL if LINE consists solely of whitespace."
+               (unless (zerop (length (trim-whitespace line)))
+                 (let ((colon-pos (or (position #\: line :test #'char=)
+                                      (error 'syntax-error
+                                             :stream stream
+                                             :format-control "Couldn't find colon in header line ~S."
+                                             :format-arguments (list line)))))
+                   (cons (as-keyword (subseq line 0 colon-pos))
+                         (trim-whitespace (subseq line (1+ colon-pos)))))))
+             (add-header (pair)
+               "Adds the name/value cons PAIR to HEADERS.  Takes
+care of multiple headers with the same name."
+               (let* ((name (car pair))
+                      (existing-header (assoc name headers :test #'eq))
+                      (existing-value (cdr existing-header)))
+                 (cond (existing-header
+                        (setf (cdr existing-header)
+                              (format nil "~A~:[,~;~]~A"
+                                      existing-value
+                                      (and *treat-semicolon-as-continuation*
+                                           (eq name :set-cookie)
+                                           (ends-with-p (trim-whitespace existing-value) ";"))
+                                      (cdr pair))))
+                       (t (push pair headers))))))
+      (loop for header-pair = (split-header (read-header-line))
+            while header-pair
+            do (add-header header-pair)))
+    (nreverse headers)))
+
+(defun skip-whitespace (stream)
+  "Consume characters from STREAM until an END-OF-FILE is
+encountered or a non-whitespace \(according to RFC 2616)
+characters is seen.  This character is returned \(or NIL in case
+of END-OF-FILE)."
+  (loop for char = (peek-char* stream nil)
+        while (and char (whitespacep char))
+        do (read-char* stream)
+        finally (return char)))
+
+(defun read-token (stream)
+  "Read characters from STREAM while they are token constituents
+\(according to RFC 2616).  It is assumed that there's a token
+character at the current position.  The token read is returned as
+a string.  Doesn't signal an error \(but simply stops reading) if
+END-OF-FILE is encountered after the first character."
+  (with-output-to-string (out)
+    (loop for first = t then nil
+          for char = (if first
+                       (peek-char* stream)
+                       (or (peek-char* stream nil) (return)))
+          while (token-char-p char)
+          do (write-char (read-char* stream) out))))
+
+(defun read-quoted-string (stream)
+  "Reads a quoted string \(according to RFC 2616).  It is assumed
+that the character at the current position is the opening quote
+character.  Returns the string read without quotes and escape
+characters."
+  (read-char* stream)
+  (with-output-to-string (out)
+    (loop for char = (read-char* stream)
+          until (char= char #\")
+          do (case char
+               (#\\ (write-char (read-char* stream) out))
+               (#\Return (assert-char stream #\Linefeed)
+                         (let ((char (read-char* stream)))
+                           (unless (whitespacep char)
+                             (signal-unexpected-chars stream char '(#\Space #\Tab)))))
+               (otherwise (write-char char out))))))
+
+(defun read-cookie-value (stream &key (separators ";"))
+  "Reads a cookie parameter value from STREAM which is returned as a
+string.  Simply reads until a semicolon is seen \(or an element of
+SEPARATORS).  Also reads quoted strings if the first non-whitespace
+character is a quotation mark \(as in RFC 2109)."
+  (if (char= #\" (peek-char* stream))
+      (read-quoted-string stream)
+      (trim-whitespace
+       (with-output-to-string (out)
+         (loop for char = (peek-char* stream nil)
+               until (or (null char) (find char separators :test #'char=))
+               do (write-char (read-char* stream) out))))))
+
+(defun read-name-value-pair (stream &key (value-required-p t) cookie-syntax)
+  "Reads a typical \(in RFC 2616) name/value or attribute/value
+combination from STREAM - a token followed by a #\\= character and
+another token or a quoted string.  Returns a cons of name and value,
+both as strings.  If VALUE-REQUIRED-P is NIL, the #\\= sign and the
+value are optional.  If COOKIE-SYNTAX is true, uses READ-COOKIE-VALUE
+internally."
+  (skip-whitespace stream)
+  (let ((name (if cookie-syntax
+                (read-cookie-value stream :separators "=;")
+                (read-token stream))))
+    (skip-whitespace stream)
+    (cons name
+          (when (or value-required-p
+                    (eql (peek-char* stream nil) #\=))
+            (assert-char stream #\=)
+            (skip-whitespace stream)
+            (cond (cookie-syntax (read-cookie-value stream))
+                  ((char= (peek-char* stream) #\") (read-quoted-string stream))
+                  (t (read-token stream)))))))
+
+(defun read-name-value-pairs (stream &key (value-required-p t) cookie-syntax)
+  "Uses READ-NAME-VALUE-PAIR to read and return an alist of
+name/value pairs from STREAM.  It is assumed that the pairs are
+separated by semicolons and that the first char read \(except for
+whitespace) will be a semicolon.  The parameters are used as in
+READ-NAME-VALUE-PAIR.  Stops reading in case of END-OF-FILE
+\(instead of signaling an error)."
+  (loop for char = (skip-whitespace stream)
+        while (and char (char= char #\;))
+        do (read-char* stream)
+        ;; guard against a stray semicolon at the end
+        when (skip-whitespace stream)
+        collect (read-name-value-pair stream
+                                      :value-required-p value-required-p
+                                      :cookie-syntax cookie-syntax)))
diff --git a/deps/chunga/specials.lisp b/deps/chunga/specials.lisp
new file mode 100644 (file)
index 0000000..02c75d6
--- /dev/null
@@ -0,0 +1,100 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CHUNGA; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/chunga/specials.lisp,v 1.12 2008/05/24 03:06:22 edi Exp $
+
+;;; Copyright (c) 2006-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :chunga)
+
+(defmacro define-constant (name value &optional doc)
+  "A version of DEFCONSTANT for, cough, /strict/ CL implementations."
+  ;; See <http://www.sbcl.org/manual/Defining-Constants.html>
+  `(defconstant ,name (if (boundp ',name) (symbol-value ',name) ,value)
+     ,@(when doc (list doc))))
+
+#+:lispworks
+(editor:setup-indent "define-constant" 1 2 4)
+
+(defconstant +output-buffer-size+ 8192
+  "Size of the initial output buffer for chunked output.")
+
+(define-constant +crlf+
+  (make-array 2 :element-type '(unsigned-byte 8)
+              :initial-contents (mapcar 'char-code '(#\Return #\Linefeed)))
+  "A 2-element array consisting of the character codes for a CRLF
+sequence.")
+
+(define-constant +hex-digits+ '#.(coerce "0123456789ABCDEF" 'list)
+  "The hexadecimal digits.")
+
+(defvar *current-error-message* nil
+  "Used by the parsing functions in `read.lisp' as an
+introduction to a standardized error message about unexpected
+characters unless it is NIL.")
+
+(defvar *current-error-function* nil
+  "Used by the functions in `read.lisp' as a function to signal
+errors about unexpected characters when *CURRENT-ERROR-MESSAGE*
+is NIL.")
+
+(defvar *accept-bogus-eols* nil
+  "Some web servers do not respond with a correct CRLF line ending for
+HTTP headers but with a lone linefeed or carriage return instead.  If
+this variable is bound to a true value, READ-LINE* will treat a lone
+LF or CR character as an acceptable end of line.  The initial value is
+NIL.")
+
+(defvar *treat-semicolon-as-continuation* nil
+  "According to John Foderaro, Netscape v3 web servers bogusly split
+Set-Cookie headers over multiple lines which means that we'd have to
+treat Set-Cookie headers ending with a semicolon as incomplete and
+combine them with the next header.  This will only be done if this
+variable has a true value, though.")
+
+(defvar *char-buffer* nil
+  "A `buffer' for one character.  Used by PEEK-CHAR* and
+UNREAD-CHAR*.")
+
+(pushnew :chunga *features*)
+
+;; stuff for Nikodemus Siivola's HYPERDOC
+;; see <http://common-lisp.net/project/hyperdoc/>
+;; and <http://www.cliki.net/hyperdoc>
+;; also used by LW-ADD-ONS
+
+(defvar *hyperdoc-base-uri* "http://weitz.de/chunga/")
+
+(let ((exported-symbols-alist
+       (loop for symbol being the external-symbols of :chunga
+             collect (cons symbol
+                           (concatenate 'string
+                                        "#"
+                                        (string-downcase symbol))))))
+  (defun hyperdoc-lookup (symbol type)
+    (declare (ignore type))
+    (cdr (assoc symbol
+                exported-symbols-alist
+                :test #'eq))))
diff --git a/deps/chunga/streams.lisp b/deps/chunga/streams.lisp
new file mode 100644 (file)
index 0000000..280867d
--- /dev/null
@@ -0,0 +1,131 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CHUNGA; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/chunga/streams.lisp,v 1.10 2008/05/24 03:06:22 edi Exp $
+
+;;; Copyright (c) 2006-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :chunga)
+
+(defclass chunked-stream (trivial-gray-stream-mixin)
+  ((real-stream :initarg :real-stream
+                :reader chunked-stream-stream
+                :documentation "The actual stream that's used for
+input and/or output."))
+  (:documentation "Every chunked stream returned by
+MAKE-CHUNKED-STREAM is of this type which is a subtype of
+STREAM."))
+
+(defclass chunked-input-stream (chunked-stream fundamental-binary-input-stream)
+  ((input-chunking-p :initform nil
+                     :reader chunked-stream-input-chunking-p
+                     :documentation "Whether input chunking is currently enabled.")
+   (input-buffer :initform nil
+                 :documentation "A vector containing the binary
+data from the most recent chunk that was read.")
+   (input-index :initform 0
+                :accessor chunked-stream-input-index
+                :documentation "The current position within INPUT-BUFFER.")
+   (input-limit :initform 0
+                :accessor chunked-stream-input-limit
+                :documentation "Only the content in INPUT-BUFFER
+up to INPUT-LIMIT belongs to the current chunk.")
+   (chunk-extensions :initform nil
+                     :reader chunked-input-stream-extensions
+                     :documentation "An alist of attribute/value
+pairs corresponding to the optional `chunk extensions' which
+might be encountered when reading from a chunked stream.")
+   (chunk-trailers :initform nil
+                   :reader chunked-input-stream-trailers
+                   :documentation "An alist of attribute/value
+pairs corresponding to the optional `trailer' HTTP headers which
+might be encountered at the end of a chunked stream.")
+   (expecting-crlf-p :initform nil
+                     :accessor expecting-crlf-p
+                     :documentation "Whether we expect to see
+CRLF before we can read the next chunk-size header part from the
+stream.  \(This will actually be the CRLF from the end of the
+last chunk-data part.)"))
+  (:documentation "A chunked stream is of this type if its
+underlying stream is an input stream. This is a subtype of
+CHUNKED-STREAM."))
+
+(defclass chunked-output-stream (chunked-stream fundamental-binary-output-stream)
+  ((output-chunking-p :initform nil
+                      :reader chunked-stream-output-chunking-p
+                      :documentation "Whether output chunking is
+currently enabled.")
+   (output-buffer :initform (make-array +output-buffer-size+ :element-type '(unsigned-byte 8))
+                  :accessor output-buffer
+                  :documentation "A vector used to temporarily
+store data which will output in one chunk.")
+   (output-index :initform 0
+                 :accessor output-index
+                 :documentation "The current end of OUTPUT-BUFFER."))
+  (:documentation "A chunked stream is of this type if its
+underlying stream is an output stream. This is a subtype of
+CHUNKED-STREAM."))
+
+(defclass chunked-io-stream (chunked-input-stream chunked-output-stream)
+  ()
+  (:documentation "A chunked stream is of this type if it is both
+a CHUNKED-INPUT-STREAM as well as a CHUNKED-OUTPUT-STREAM."))
+
+(defmethod stream-element-type ((stream chunked-stream))
+  "Chunked streams are always binary streams.  Wrap them with
+flexi streams if you need a character stream."
+  '(unsigned-byte 8))
+
+(defmethod open-stream-p ((stream chunked-stream))
+  "A chunked stream is open if its underlying stream is open."
+  (open-stream-p (chunked-stream-stream stream)))
+
+(defmethod close ((stream chunked-stream) &key abort)
+  "If a chunked stream is closed, we close the underlying stream as well."
+  (with-slots (real-stream)
+      stream
+    (cond ((open-stream-p real-stream)
+           (close real-stream :abort abort))
+          (t nil))))
+
+(defun make-chunked-stream (stream)
+  "Creates and returns a chunked stream \(a stream of type
+CHUNKED-STREAM) which wraps STREAM.  STREAM must be an open
+binary stream."
+  (unless (and (streamp stream)
+               (open-stream-p stream))
+    (error 'parameter-error
+           :stream stream
+           :format-control "~S should have been an open stream."
+           :format-arguments (list stream)))
+  (make-instance ;; actual type depends on STREAM
+                 (cond ((and (input-stream-p stream)
+                             (output-stream-p stream))
+                        'chunked-io-stream)
+                       ((input-stream-p stream)
+                        'chunked-input-stream)
+                       ((output-stream-p stream)
+                        'chunked-output-stream))
+                 :real-stream stream))
\ No newline at end of file
diff --git a/deps/chunga/util.lisp b/deps/chunga/util.lisp
new file mode 100644 (file)
index 0000000..50e789d
--- /dev/null
@@ -0,0 +1,93 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CHUNGA; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/chunga/util.lisp,v 1.12 2008/05/25 10:53:48 edi Exp $
+
+;;; Copyright (c) 2006-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :chunga)
+
+#-:lispworks
+(defmacro when-let ((var expr) &body body)
+  "Evaluates EXPR, binds it to VAR, and executes BODY if VAR has
+a true value."
+  `(let ((,var ,expr))
+     (when ,var ,@body)))
+
+(defun ends-with-p (seq suffix &key (test #'char-equal))
+  "Returns true if the sequence SEQ ends with the sequence
+SUFFIX.  Individual elements are compared with TEST."
+  (let ((mismatch (mismatch seq suffix :from-end t :test test)))
+    (or (null mismatch)
+        (= mismatch (- (length seq) (length suffix))))))
+
+(defun make-keyword (string destructivep)
+  "Converts the string STRING to a keyword where all characters are
+uppercase or lowercase, taking into account the current readtable
+case.  Destructively modifies STRING if DESTRUCTIVEP is true."
+  (intern (funcall
+           (if destructivep
+             (if (eq (readtable-case *readtable*) :upcase)
+               #'nstring-upcase
+               #'nstring-downcase)
+             (if (eq (readtable-case *readtable*) :upcase)
+               #'string-upcase
+               #'string-downcase))
+           string)
+          :keyword))
+
+(defun read-char* (stream &optional (eof-error-p t) eof-value)
+  "The streams we're dealing with are all binary with element type
+\(UNSIGNED-BYTE 8) and we're only interested in ISO-8859-1, so we use
+this to `simulate' READ-CHAR."
+  (cond (*char-buffer*
+         (prog1 *char-buffer*
+           (setq *char-buffer* nil)))
+        (t
+         ;; this assumes that character codes are identical to Unicode code
+         ;; points, at least for Latin1
+         (let ((char-code (read-byte stream eof-error-p eof-value)))
+           (and char-code
+                (code-char char-code))))))
+
+(defun unread-char* (char)
+  "Were simulating UNREAD-CHAR by putting the character into
+*CHAR-BUFFER*."
+  ;; no error checking, only used internally
+  (setq *char-buffer* char)
+  nil)
+  
+(defun peek-char* (stream &optional eof-error-p eof-value)
+  "We're simulating PEEK-CHAR by reading a character and putting it
+into *CHAR-BUFFER*."
+  ;; no error checking, only used internally  
+  (setq *char-buffer* (read-char* stream eof-error-p eof-value)))
+
+(defmacro with-character-stream-semantics (&body body)
+  "Binds *CHAR-BUFFER* around BODY so that within BODY we can use
+READ-CHAR* and friends \(see above) to simulate a character stream
+although we're reading from a binary stream."
+  `(let ((*char-buffer* nil))
+     ,@body))
diff --git a/deps/cl-base64/.gitignore b/deps/cl-base64/.gitignore
new file mode 100644 (file)
index 0000000..3413a58
--- /dev/null
@@ -0,0 +1,10 @@
+.bin
+*.fasl*
+*.dfsl
+*.pfsl
+*.ufsl
+*.fas
+*.x86f
+*.sparcf
+*.cfsl
+*.fsl
diff --git a/deps/cl-base64/COPYING b/deps/cl-base64/COPYING
new file mode 100644 (file)
index 0000000..acae269
--- /dev/null
@@ -0,0 +1,26 @@
+Copyright (c) 2002-2003 by Kevin Rosenberg
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the Authors may not be used to endorse or promote products 
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/deps/cl-base64/cl-base64.asd b/deps/cl-base64/cl-base64.asd
new file mode 100644 (file)
index 0000000..252389d
--- /dev/null
@@ -0,0 +1,44 @@
+;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:          cl-base64.asd
+;;;; Purpose:       ASDF definition file for Cl-Base64
+;;;; Programmer:    Kevin M. Rosenberg
+;;;; Date Started:  Dec 2002
+;;;;
+;;;; $Id$
+;;;; *************************************************************************
+
+(in-package #:cl-user)
+(defpackage #:cl-base64-system (:use #:asdf #:cl))
+(in-package #:cl-base64-system)
+
+
+(defsystem cl-base64
+  :name "cl-base64"
+  :author "Kevin M. Rosenberg based on initial code by Juri Pakaste"
+  :version "3.1"
+  :maintainer "Kevin M. Rosenberg <kmr@debian.org>"
+  :licence "BSD-style"
+  :description "Base64 encoding and decoding with URI support."
+  :components
+  ((:file "package")
+   (:file "encode" :depends-on ("package"))
+   (:file "decode" :depends-on ("package"))
+   ))
+
+(defmethod perform ((o test-op) (c (eql (find-system 'cl-base64))))
+  (operate 'load-op 'cl-base64-tests)
+  (operate 'test-op 'cl-base64-tests :force t))
+
+(defsystem cl-base64-tests
+    :depends-on (cl-base64 ptester kmrcl)
+    :components
+    ((:file "tests")))
+
+(defmethod perform ((o test-op) (c (eql (find-system 'cl-base64-tests))))
+  (operate 'load-op 'cl-base64-tests)
+  (or (funcall (intern (symbol-name '#:do-tests)
+                      (find-package '#:cl-base64-tests)))
+      (error "test-op failed")))
diff --git a/deps/cl-base64/debian/changelog b/deps/cl-base64/debian/changelog
new file mode 100644 (file)
index 0000000..7005d25
--- /dev/null
@@ -0,0 +1,140 @@
+cl-base64 (3.3.3-2) unstable; urgency=low
+
+  * Convert installation to dh-lisp
+  * control: Add Vcs-Browser field
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Mon, 03 Aug 2009 10:31:33 -0600
+
+cl-base64 (3.3.3-1) unstable; urgency=low
+
+  * New upstream
+  * Convert to debhelper version 7
+  * debian/watch: New file
+  * debian/control: Change section to new lisp section. Add Vcs-Git
+  and Homepage fields.
+  * debian/rules: Change to just architecture independent rules and DH7
+  * debian/{prerm,postinst}: Remove paths from binary function
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sat, 01 Aug 2009 23:19:43 -0600
+
+cl-base64 (3.3.2-1) unstable; urgency=low
+
+  * Depend on kmrcl only for test package
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sun, 27 Aug 2006 12:23:37 -0600
+
+cl-base64 (3.3.1-5) unstable; urgency=low
+
+  * Fix spelling mistake in package description (closes:363204)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Mon, 15 May 2006 17:41:36 -0600
+
+cl-base64 (3.3.1-4) unstable; urgency=low
+
+  * New upstream URI
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sat, 17 Sep 2005 15:34:55 -0600
+
+cl-base64 (3.3.1-3) unstable; urgency=low
+
+  * Fix package name in postinst/prerm
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Thu, 22 Apr 2004 08:53:34 -0600
+
+cl-base64 (3.3.1-2) unstable; urgency=low
+
+  * Rename package rules file (closes:244687)
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Mon, 19 Apr 2004 08:59:58 -0600
+
+cl-base64 (3.3.1-1) unstable; urgency=low
+
+  * Rename ASDF system to cl-base64
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sun, 18 Apr 2004 10:39:51 -0600
+
+cl-base64 (3.3-1) unstable; urgency=low
+
+  * Rework test loading
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sun, 24 Aug 2003 13:40:03 -0600
+
+cl-base64 (3.2.1-1) unstable; urgency=low
+
+  * New upstream
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Thu, 12 Jun 2003 08:04:55 -0600
+
+cl-base64 (3.2-1) unstable; urgency=low
+
+  * Improve .asd file
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue,  6 May 2003 10:19:22 -0600
+
+cl-base64 (3.1-1) unstable; urgency=low
+
+  * Implement asdf:test-op. Remove old base64-test.asd file.
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue, 15 Apr 2003 09:33:01 -0600
+
+cl-base64 (3.0.2-1) unstable; urgency=low
+
+  * Change declarations from array to simple-array where feasible
+  * add more fixnum declarations where helpful
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue, 14 Jan 2003 04:55:48 -0700
+
+cl-base64 (3.0.1-1) unstable; urgency=low
+
+  * Fix output of base64-string-to-usb8-array
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Tue, 14 Jan 2003 04:35:05 -0700
+
+cl-base64 (3.0.0-1) unstable; urgency=low
+
+  * Remove src.lisp and add package.lisp, decode.lisp, encode.lisp     
+  * Add support for usb8-arrays        
+  * Rewrite routines as macros to create efficient functions for
+     converting to and from streams, strings, and usb8-arrays.
+  * Fix error in integer-to-base64 when using columns
+  * Add base64-test.asd and test.lisp regression suite
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Mon, 13 Jan 2003 14:41:52 -0700
+
+cl-base64 (2.1.0-1) unstable; urgency=low
+
+  * Fix broken string-to-base64
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sat,  4 Jan 2003 06:40:32 -0700
+
+cl-base64 (2.0-1) unstable; urgency=low
+
+  * Ignore whitespace in base64 strings
+  * Add column breaking and stream output to base64 conversion
+  * Rework string-to-base64 to handle columns and streams
+       
+ -- Kevin M. Rosenberg <kmr@debian.org>  Fri,  3 Jan 2003 23:14:13 -0700
+
+cl-base64 (1.2-1) unstable; urgency=low
+
+  * Bug fix in base64-to-integer
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sun, 29 Dec 2002 00:03:11 -0700
+
+cl-base64 (1.1-1) unstable; urgency=low
+
+  * Rewritten version, significant optimizations
+  * BSD-style license
+  * Adds conversion to and from integers
+  * Renamed functions
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Sat, 28 Dec 2002 21:28:42 -0700
+
+cl-base64 (1.0-1) unstable; urgency=low
+
+  * Initial upload
+  * Changes compared to upstream: 
+     - Added .asd file for use with Common Lisp Controller
+     - Changes for Allegro's case sensitive mode
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Thu, 26 Dec 2002 19:17:51 -0700
diff --git a/deps/cl-base64/debian/compat b/deps/cl-base64/debian/compat
new file mode 100644 (file)
index 0000000..7f8f011
--- /dev/null
@@ -0,0 +1 @@
+7
diff --git a/deps/cl-base64/debian/control b/deps/cl-base64/debian/control
new file mode 100644 (file)
index 0000000..5fdd6e3
--- /dev/null
@@ -0,0 +1,19 @@
+Source: cl-base64
+Section: lisp
+Priority: optional
+Maintainer: Kevin M. Rosenberg <kmr@debian.org>
+Build-Depends-Indep: dh-lisp
+Build-Depends: debhelper (>= 7.0.0)
+Standards-Version: 3.8.2.0
+Homepage: http://files.b9.com/cl-base64/
+Vcs-Git: git://git.b9.com/cl-base64.git
+Vcs-Browser: http://git.b9.com/?p=cl-base64.git
+
+Package: cl-base64
+Architecture: all
+Depends: ${misc:Depends}, cl-kmrcl
+Description: Common Lisp package to encode and decode base64 with URI support
+ This package provides highly optimized base64 encoding and decoding.
+ Besides conversion to and from strings, integer conversions are supported.
+ Encoding with Uniform Resource Identifiers is supported by using
+ a modified encoding table that uses only URI-compatible characters.
diff --git a/deps/cl-base64/debian/copyright b/deps/cl-base64/debian/copyright
new file mode 100644 (file)
index 0000000..4a44dc0
--- /dev/null
@@ -0,0 +1,39 @@
+This package was debianized by Kevin M. Rosenberg <kmr@debian.org> in
+Dec 2002.
+
+It was downloaded from http://files.b9.com/base64/
+
+Upstream Author: Kevin M. Rosenberg <kevin@rosenberg.net>
+  This code is based on code placed in the public domain by Juri Pakaste
+  <juri@iki.fr> and available for download at
+  http://www.helsinki.fi/~pakaste/store/dl/base64.lisp
+
+Copyright:
+
+Copyright (c) 2002-2003 by Kevin Rosenberg
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the Authors may not be used to endorse or promote products 
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/deps/cl-base64/debian/rules b/deps/cl-base64/debian/rules
new file mode 100755 (executable)
index 0000000..dba8214
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/make -f
+
+pkg            := cl-base64
+debpkg         := cl-base64
+
+clc-source     := usr/share/common-lisp/source
+clc-systems    := usr/share/common-lisp/systems
+clc-files      := $(clc-source)/$(pkg)
+doc-dir                := usr/share/doc/$(debpkg)
+
+build:
+
+clean:
+       dh_testdir
+       dh_testroot
+       dh_clean
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_prep
+       dh_installdirs
+       dh_install $(pkg).asd $(clc-files)
+       dh_install *.lisp $(clc-files)
+
+binary-indep: install
+       dh_testdir
+       dh_testroot
+       dh_installchangelogs
+       dh_installdocs
+       dh_lisp
+       dh_compress
+       dh_fixperms
+       dh_installdeb
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary-arch:
+
+binary: binary-indep
+
+
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/deps/cl-base64/debian/upload.sh b/deps/cl-base64/debian/upload.sh
new file mode 100755 (executable)
index 0000000..ad50ce2
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash -e
+
+dup cl-base64 -Ufiles.b9.com -D/home/ftp/cl-base64 -C"(umask 022; /home/kevin/bin/remove-old-versions cl-base64 latest)" -su $*
diff --git a/deps/cl-base64/debian/watch b/deps/cl-base64/debian/watch
new file mode 100644 (file)
index 0000000..7d41af4
--- /dev/null
@@ -0,0 +1,2 @@
+version=3
+http://files.b9.com/cl-base64/cl-base64-(\d+.*)\.tar\.gz
diff --git a/deps/cl-base64/decode.lisp b/deps/cl-base64/decode.lisp
new file mode 100644 (file)
index 0000000..1649daa
--- /dev/null
@@ -0,0 +1,256 @@
+;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:          encode.lisp
+;;;; Purpose:       cl-base64 encoding routines
+;;;; Programmer:    Kevin M. Rosenberg
+;;;; Date Started:  Dec 2002
+;;;;
+;;;; $Id$
+;;;;
+;;;; This file implements the Base64 transfer encoding algorithm as
+;;;; defined in RFC 1521 by Borensten & Freed, September 1993.
+;;;; See: http://www.ietf.org/rfc/rfc1521.txt
+;;;;
+;;;; Based on initial public domain code by Juri Pakaste <juri@iki.fi>
+;;;;
+;;;; Copyright 2002-2003 Kevin M. Rosenberg
+;;;; Permission to use with BSD-style license included in the COPYING file
+;;;; *************************************************************************
+
+(in-package #:cl-base64)
+
+(declaim (inline whitespace-p))
+(defun whitespace-p (c)
+  "Returns T for a whitespace character."
+  (or (char= c #\Newline) (char= c #\Linefeed)
+      (char= c #\Return) (char= c #\Space)
+      (char= c #\Tab)))
+
+
+;;; Decoding
+
+#+ignore
+(defmacro def-base64-stream-to-* (output-type)
+  `(defun ,(intern (concatenate 'string (symbol-name :base64-stream-to-)
+                                (symbol-name output-type)))
+    (input &key (uri nil)
+        ,@(when (eq output-type :stream)
+                '(stream)))
+     ,(concatenate 'string "Decode base64 stream to " (string-downcase
+                                                       (symbol-name output-type)))
+     (declare (stream input)
+              (optimize (speed 3) (space 0) (safety 0)))
+     (let ((pad (if uri *uri-pad-char* *pad-char*))
+           (decode-table (if uri *uri-decode-table* *decode-table*)))
+       (declare (type decode-table decode-table)
+                (type character pad))
+       (let (,@(case output-type
+                     (:string
+                      '((result (make-string (* 3 (truncate (length string) 4))))))
+                     (:usb8-array
+                      '((result (make-array (* 3 (truncate (length string) 4))
+                                 :element-type '(unsigned-byte 8)
+                                 :fill-pointer nil
+                                 :adjustable nil)))))
+               (ridx 0))
+         (declare ,@(case output-type
+                          (:string
+                           '((simple-string result)))
+                          (:usb8-array
+                           '((type (simple-array (usigned-byte 8) (*)) result))))
+                  (fixnum ridx))
+         (do* ((bitstore 0)
+               (bitcount 0)
+               (char (read-char stream nil #\null)
+                     (read-char stream nil #\null)))
+              ((eq char #\null)
+               ,(case output-type
+                      (:stream
+                       'stream)
+                      ((:string :usb8-array)
+                       'result)
+                      ;; ((:stream :string)
+                      ;; '(subseq result 0 ridx))))
+                      ))
+           (declare (fixnum bitstore bitcount)
+                    (character char))
+           (let ((svalue (aref decode-table (the fixnum (char-code char)))))
+             (declare (fixnum svalue))
+             (cond
+               ((>= svalue 0)
+                (setf bitstore (logior
+                                (the fixnum (ash bitstore 6))
+                                svalue))
+                (incf bitcount 6)
+                (when (>= bitcount 8)
+                  (decf bitcount 8)
+                  (let ((ovalue (the fixnum
+                                  (logand
+                                   (the fixnum
+                                     (ash bitstore
+                                          (the fixnum (- bitcount))))
+                                   #xFF))))
+                    (declare (fixnum ovalue))
+                    ,(case output-type
+                           (:string
+                            '(setf (char result ridx) (code-char ovalue)))
+                           (:usb8-array
+                            '(setf (aref result ridx) ovalue))
+                           (:stream
+                            '(write-char (code-char ovalue) stream)))
+                    (incf ridx)
+                    (setf bitstore (the fixnum (logand bitstore #xFF))))))
+               ((char= char pad)
+                ;; Could add checks to make sure padding is correct
+                ;; Currently, padding is ignored
+                )
+               ((whitespace-p char)
+                ;; Ignore whitespace
+                )
+               ((minusp svalue)
+                (warn "Bad character ~W in base64 decode" char))
+               )))))))
+
+;;(def-base64-stream-to-* :string)
+;;(def-base64-stream-to-* :stream)
+;;(def-base64-stream-to-* :usb8-array)
+
+(defmacro def-base64-string-to-* (output-type)
+  `(defun ,(intern (concatenate 'string (symbol-name :base64-string-to-)
+                                (symbol-name output-type)))
+    (input &key (uri nil)
+        ,@(when (eq output-type :stream)
+                '(stream)))
+     ,(concatenate 'string "Decode base64 string to " (string-downcase
+                                                       (symbol-name output-type)))
+     (declare (string input)
+              (optimize (speed 3) (safety 0) (space 0)))
+     (let ((pad (if uri *uri-pad-char* *pad-char*))
+           (decode-table (if uri *uri-decode-table* *decode-table*)))
+       (declare (type decode-table decode-table)
+                (type character pad))
+       (let (,@(case output-type
+                     (:string
+                      '((result (make-string (* 3 (truncate (length input) 4))))))
+                     (:usb8-array
+                      '((result (make-array (* 3 (truncate (length input) 4))
+                                 :element-type '(unsigned-byte 8)
+                                 :fill-pointer nil
+                                 :adjustable nil)))))
+               (ridx 0))
+         (declare ,@(case output-type
+                          (:string
+                           '((simple-string result)))
+                          (:usb8-array
+                           '((type (simple-array (unsigned-byte 8) (*)) result))))
+                  (fixnum ridx))
+         (loop
+            for char of-type character across input
+            for svalue of-type fixnum = (aref decode-table
+                                              (the fixnum (char-code char)))
+            with bitstore of-type fixnum = 0
+            with bitcount of-type fixnum = 0
+            do
+              (cond
+                ((>= svalue 0)
+                 (setf bitstore (logior
+                                 (the fixnum (ash bitstore 6))
+                                 svalue))
+                 (incf bitcount 6)
+                 (when (>= bitcount 8)
+                   (decf bitcount 8)
+                   (let ((ovalue (the fixnum
+                                   (logand
+                                    (the fixnum
+                                      (ash bitstore
+                                           (the fixnum (- bitcount))))
+                                    #xFF))))
+                     (declare (fixnum ovalue))
+                     ,(case output-type
+                            (:string
+                             '(setf (char result ridx) (code-char ovalue)))
+                            (:usb8-array
+                             '(setf (aref result ridx) ovalue))
+                            (:stream
+                             '(write-char (code-char ovalue) stream)))
+                     (incf ridx)
+                     (setf bitstore (the fixnum (logand bitstore #xFF))))))
+                 ((char= char pad)
+                  ;; Could add checks to make sure padding is correct
+                  ;; Currently, padding is ignored
+                  )
+                 ((whitespace-p char)
+                  ;; Ignore whitespace
+                  )
+                 ((minusp svalue)
+                  (warn "Bad character ~W in base64 decode" char))
+                 ))
+         ,(case output-type
+                (:stream
+                 'stream)
+                ((:usb8-array :string)
+                 '(subseq result 0 ridx)))))))
+
+(def-base64-string-to-* :string)
+(def-base64-string-to-* :stream)
+(def-base64-string-to-* :usb8-array)
+
+;; input-mode can be :string or :stream
+;; input-format can be :character or :usb8
+
+(defun base64-string-to-integer (string &key (uri nil))
+  "Decodes a base64 string to an integer"
+  (declare (string string)
+           (optimize (speed 3) (safety 0) (space 0)))
+  (let ((pad (if uri *uri-pad-char* *pad-char*))
+        (decode-table (if uri *uri-decode-table* *decode-table*)))
+    (declare (type decode-table decode-table)
+             (character pad))
+    (let ((value 0))
+      (declare (integer value))
+      (loop
+         for char of-type character across string
+         for svalue of-type fixnum =
+           (aref decode-table (the fixnum (char-code char)))
+         do
+           (cond
+             ((>= svalue 0)
+              (setq value (+ svalue (ash value 6))))
+             ((char= char pad)
+              (setq value (ash value -2)))
+             ((whitespace-p char)
+              ; ignore whitespace
+              )
+             ((minusp svalue)
+              (warn "Bad character ~W in base64 decode" char))))
+      value)))
+
+
+(defun base64-stream-to-integer (stream &key (uri nil))
+  "Decodes a base64 string to an integer"
+  (declare (stream stream)
+           (optimize (speed 3) (space 0) (safety 0)))
+  (let ((pad (if uri *uri-pad-char* *pad-char*))
+        (decode-table (if uri *uri-decode-table* *decode-table*)))
+    (declare (type decode-table decode-table)
+             (character pad))
+    (do* ((value 0)
+          (char (read-char stream nil #\null)
+                (read-char stream nil #\null)))
+         ((eq char #\null)
+          value)
+      (declare (integer value)
+               (character char))
+      (let ((svalue (aref decode-table (the fixnum (char-code char)))))
+           (declare (fixnum svalue))
+           (cond
+             ((>= svalue 0)
+              (setq value (+ svalue (ash value 6))))
+             ((char= char pad)
+              (setq value (ash value -2)))
+             ((whitespace-p char)               ; ignore whitespace
+              )
+             ((minusp svalue)
+              (warn "Bad character ~W in base64 decode" char)))))))
diff --git a/deps/cl-base64/encode.lisp b/deps/cl-base64/encode.lisp
new file mode 100644 (file)
index 0000000..dcddc1a
--- /dev/null
@@ -0,0 +1,322 @@
+;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:          encode.lisp
+;;;; Purpose:       cl-base64 encoding routines
+;;;; Programmer:    Kevin M. Rosenberg
+;;;; Date Started:  Dec 2002
+;;;;
+;;;; $Id$
+;;;;
+;;;; This file implements the Base64 transfer encoding algorithm as
+;;;; defined in RFC 1521 by Borensten & Freed, September 1993.
+;;;; See: http://www.ietf.org/rfc/rfc1521.txt
+;;;;
+;;;; Based on initial public domain code by Juri Pakaste <juri@iki.fi>
+;;;;
+;;;; Copyright 2002-2003 Kevin M. Rosenberg
+;;;; Permission to use with BSD-style license included in the COPYING file
+;;;; *************************************************************************
+
+;;;; Extended by Kevin M. Rosenberg <kevin@rosenberg.net>:
+;;;;   - .asd file
+;;;;   - numerous speed optimizations
+;;;;   - conversion to and from integers
+;;;;   - Renamed functions now that supporting integer conversions
+;;;;   - URI-compatible encoding using :uri key
+;;;;
+;;;; $Id$
+
+(in-package #:cl-base64)
+
+(defun round-next-multiple (x n)
+  "Round x up to the next highest multiple of n."
+  (declare (fixnum n)
+           (optimize (speed 3) (safety 0) (space 0)))
+  (let ((remainder (mod x n)))
+    (declare (fixnum remainder))
+    (if (zerop remainder)
+        x
+        (the fixnum (+ x (the fixnum (- n remainder)))))))
+
+(defmacro def-*-to-base64-* (input-type output-type)
+  `(defun ,(intern (concatenate 'string (symbol-name input-type)
+                                (symbol-name :-to-base64-)
+                                (symbol-name output-type)))
+    (input
+        ,@(when (eq output-type :stream)
+                '(output))
+        &key (uri nil) (columns 0))
+     "Encode a string array to base64. If columns is > 0, designates
+maximum number of columns in a line and the string will be terminated
+with a #\Newline."
+     (declare ,@(case input-type
+                      (:string
+                       '((string input)))
+                      (:usb8-array
+                       '((type (array (unsigned-byte 8) (*)) input))))
+              (fixnum columns)
+              (optimize (speed 3) (safety 0) (space 0)))
+     (let ((pad (if uri *uri-pad-char* *pad-char*))
+           (encode-table (if uri *uri-encode-table* *encode-table*)))
+       (declare (simple-string encode-table)
+                (character pad))
+       (let* ((string-length (length input))
+              (complete-group-count (truncate string-length 3))
+              (remainder (nth-value 1 (truncate string-length 3)))
+              (padded-length (* 4 (truncate (+ string-length 2) 3)))
+              ,@(when (eq output-type :string)
+                      '((num-lines (if (plusp columns)
+                                       (truncate (+ padded-length (1- columns)) columns)
+                                       0))
+                        (num-breaks (if (plusp num-lines)
+                                        (1- num-lines)
+                                        0))
+                        (strlen (+ padded-length num-breaks))
+                        (result (make-string strlen))
+                        (ioutput 0)))
+              (col (if (plusp columns)
+                       0
+                       (the fixnum (1+ padded-length)))))
+         (declare (fixnum string-length padded-length col
+                          ,@(when (eq output-type :string)
+                                  '(ioutput)))
+                  ,@(when (eq output-type :string)
+                          '((simple-string result))))
+         (labels ((output-char (ch)
+                    (if (= col columns)
+                        (progn
+                          ,@(case output-type
+                                  (:stream
+                                   '((write-char #\Newline output)))
+                                 (:string
+                                  '((setf (schar result ioutput) #\Newline)
+                                    (incf ioutput))))
+                          (setq col 1))
+                     (incf col))
+                 ,@(case output-type
+                         (:stream
+                          '((write-char ch output)))
+                         (:string
+                          '((setf (schar result ioutput) ch)
+                            (incf ioutput)))))
+               (output-group (svalue chars)
+                 (declare (fixnum svalue chars))
+                 (output-char
+                  (schar encode-table
+                         (the fixnum
+                           (logand #x3f
+                                   (the fixnum (ash svalue -18))))))
+                 (output-char
+                  (schar encode-table
+                         (the fixnum
+                           (logand #x3f
+                                        (the fixnum (ash svalue -12))))))
+                 (if (> chars 2)
+                     (output-char
+                      (schar encode-table
+                             (the fixnum
+                               (logand #x3f
+                                       (the fixnum (ash svalue -6))))))
+                     (output-char pad))
+                   (if (> chars 3)
+                       (output-char
+                        (schar encode-table
+                               (the fixnum
+                                 (logand #x3f svalue))))
+                       (output-char pad))))
+        (do ((igroup 0 (the fixnum (1+ igroup)))
+             (isource 0 (the fixnum (+ isource 3))))
+            ((= igroup complete-group-count)
+             (cond
+               ((= remainder 2)
+                (output-group
+                 (the fixnum
+                     (+
+                      (the fixnum
+                        (ash
+                         ,(case input-type
+                                (:string
+                                 '(char-code (the character (char input isource))))
+                                (:usb8-array
+                                 '(the fixnum (aref input isource))))
+                         16))
+                      (the fixnum
+                        (ash
+                         ,(case input-type
+                                (:string
+                                 '(char-code (the character (char input
+                                                                  (the fixnum (1+ isource))))))
+                                (:usb8-array
+                                 '(the fixnum (aref input (the fixnum
+                                                            (1+ isource))))))
+                         8))))
+                 3))
+               ((= remainder 1)
+                (output-group
+                 (the fixnum
+                   (ash
+                    ,(case input-type
+                           (:string
+                            '(char-code (the character (char input isource))))
+                           (:usb8-array
+                            '(the fixnum (aref input isource))))
+                    16))
+                 2)))
+             ,(case output-type
+                    (:string
+                     'result)
+                    (:stream
+                     'output)))
+          (declare (fixnum igroup isource))
+          (output-group
+           (the fixnum
+             (+
+              (the fixnum
+                (ash
+                 (the fixnum
+                 ,(case input-type
+                        (:string
+                         '(char-code (the character (char input isource))))
+                        (:usb8-array
+                         '(aref input isource))))
+                 16))
+              (the fixnum
+                (ash
+                 (the fixnum
+                   ,(case input-type
+                          (:string
+                           '(char-code (the character (char input
+                                                            (the fixnum (1+ isource))))))
+                        (:usb8-array
+                         '(aref input (1+ isource)))))
+                 8))
+              (the fixnum
+                ,(case input-type
+                       (:string
+                        '(char-code (the character (char input
+                                                         (the fixnum (+ 2 isource))))))
+                       (:usb8-array
+                        '(aref input (+ 2 isource))))
+                )))
+           4)))))))
+
+(def-*-to-base64-* :string :string)
+(def-*-to-base64-* :string :stream)
+(def-*-to-base64-* :usb8-array :string)
+(def-*-to-base64-* :usb8-array :stream)
+
+
+(defun integer-to-base64-string (input &key (uri nil) (columns 0))
+  "Encode an integer to base64 format."
+  (declare (integer input)
+           (fixnum columns)
+           (optimize (speed 3) (space 0) (safety 0)))
+  (let ((pad (if uri *uri-pad-char* *pad-char*))
+        (encode-table (if uri *uri-encode-table* *encode-table*)))
+    (declare (simple-string encode-table)
+             (character pad))
+    (let* ((input-bits (integer-length input))
+           (byte-bits (round-next-multiple input-bits 8))
+           (padded-bits (round-next-multiple byte-bits 6))
+           (remainder-padding (mod padded-bits 24))
+           (padding-bits (if (zerop remainder-padding)
+                             0
+                             (- 24 remainder-padding)))
+           (padding-chars (/ padding-bits 6))
+           (padded-length (/ (+ padded-bits padding-bits) 6))
+           (last-line-len (if (plusp columns)
+                              (- padded-length (* columns
+                                                  (truncate
+                                                   padded-length columns)))
+                              0))
+           (num-lines (if (plusp columns)
+                          (truncate (+ padded-length (1- columns)) columns)
+                          0))
+           (num-breaks (if (plusp num-lines)
+                           (1- num-lines)
+                           0))
+           (strlen (+ padded-length num-breaks))
+           (last-char (1- strlen))
+           (str (make-string strlen))
+           (col (if (zerop last-line-len)
+                     columns
+                    last-line-len)))
+      (declare (fixnum padded-length num-lines col last-char
+                       padding-chars last-line-len))
+      (unless (plusp columns)
+        (setq col -1)) ;; set to flag to optimize in loop
+
+      (dotimes (i padding-chars)
+        (declare (fixnum i))
+        (setf (schar str (the fixnum (- last-char i))) pad))
+
+      (do* ((strpos (- last-char padding-chars) (1- strpos))
+            (int (ash input (/ padding-bits 3))))
+           ((minusp strpos)
+            str)
+        (declare (fixnum strpos) (integer int))
+        (cond
+          ((zerop col)
+           (setf (schar str strpos) #\Newline)
+           (setq col columns))
+          (t
+           (setf (schar str strpos)
+                 (schar encode-table (the fixnum (logand int #x3f))))
+           (setq int (ash int -6))
+           (decf col)))))))
+
+(defun integer-to-base64-stream (input stream &key (uri nil) (columns 0))
+  "Encode an integer to base64 format."
+  (declare (integer input)
+           (fixnum columns)
+           (optimize (speed 3) (space 0) (safety 0)))
+  (let ((pad (if uri *uri-pad-char* *pad-char*))
+        (encode-table (if uri *uri-encode-table* *encode-table*)))
+    (declare (simple-string encode-table)
+             (character pad))
+    (let* ((input-bits (integer-length input))
+           (byte-bits (round-next-multiple input-bits 8))
+           (padded-bits (round-next-multiple byte-bits 6))
+           (remainder-padding (mod padded-bits 24))
+           (padding-bits (if (zerop remainder-padding)
+                             0
+                             (- 24 remainder-padding)))
+           (padding-chars (/ padding-bits 6))
+           (padded-length (/ (+ padded-bits padding-bits) 6))
+           (strlen padded-length)
+           (nonpad-chars (- strlen padding-chars))
+           (last-nonpad-char (1- nonpad-chars))
+           (str (make-string strlen)))
+      (declare (fixnum padded-length last-nonpad-char))
+      (do* ((strpos 0 (the fixnum (1+ strpos)))
+            (int (ash input (/ padding-bits 3)) (ash int -6))
+            (6bit-value (the fixnum (logand int #x3f))
+                        (the fixnum (logand int #x3f))))
+           ((= strpos nonpad-chars)
+            (let ((col 0))
+              (declare (fixnum col))
+              (dotimes (i nonpad-chars)
+                (declare (fixnum i))
+                (write-char (schar str i) stream)
+                (when (plusp columns)
+                  (incf col)
+                  (when (= col columns)
+                    (write-char #\Newline stream)
+                    (setq col 0))))
+              (dotimes (ipad padding-chars)
+                (declare (fixnum ipad))
+                (write-char pad stream)
+                (when (plusp columns)
+                  (incf col)
+                  (when (= col columns)
+                    (write-char #\Newline stream)
+                    (setq col 0)))))
+            stream)
+        (declare (fixnum 6bit-value strpos)
+                 (integer int))
+        (setf (schar str (- last-nonpad-char strpos))
+              (schar encode-table 6bit-value))
+        ))))
+
diff --git a/deps/cl-base64/package.lisp b/deps/cl-base64/package.lisp
new file mode 100644 (file)
index 0000000..5eac241
--- /dev/null
@@ -0,0 +1,71 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:          package.lisp
+;;;; Purpose:       Package definition for cl-base64
+;;;; Programmer:    Kevin M. Rosenberg
+;;;; Date Started:  Dec 2002
+;;;;
+;;;; $Id$
+;;;;
+;;;; *************************************************************************
+
+(defpackage #:cl-base64
+  (:nicknames #:base64)
+  (:use #:cl)
+  (:export #:base64-stream-to-integer
+           #:base64-string-to-integer
+           #:base64-string-to-string
+           #:base64-stream-to-string
+           #:base64-string-to-stream
+           #:base64-stream-to-stream
+           #:base64-string-to-usb8-array
+           #:base64-stream-to-usb8-array
+           #:string-to-base64-string
+           #:string-to-base64-stream
+           #:usb8-array-to-base64-string
+           #:usb8-array-to-base64-stream
+           #:stream-to-base64-string
+           #:stream-to-base64-stream
+           #:integer-to-base64-string
+           #:integer-to-base64-stream
+
+           ;; For creating custom encode/decode tables
+           #:*uri-encode-table*
+           #:*uri-decode-table*
+           #:make-decode-table
+
+           #:test-base64
+           ))
+
+(in-package #:cl-base64)
+
+
+(defvar *encode-table*
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
+(declaim (type simple-string *encode-table*))
+
+(defvar *uri-encode-table*
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_")
+(declaim (type simple-string *uri-encode-table*))
+
+(deftype decode-table () '(simple-array fixnum (256)))
+
+(defun make-decode-table (encode-table)
+  (let ((dt (make-array 256 :adjustable nil :fill-pointer nil
+                        :element-type 'fixnum
+                        :initial-element -1)))
+    (declare (type decode-table dt))
+    (loop for char of-type character across encode-table
+       for index of-type fixnum from 0 below 64
+       do (setf (aref dt (the fixnum (char-code char))) index))
+    dt))
+
+(defvar *decode-table* (make-decode-table *encode-table*))
+
+(defvar *uri-decode-table* (make-decode-table *uri-encode-table*))
+
+(defvar *pad-char* #\=)
+(defvar *uri-pad-char* #\.)
+(declaim (type character *pad-char* *uri-pad-char*))
diff --git a/deps/cl-base64/tests.lisp b/deps/cl-base64/tests.lisp
new file mode 100644 (file)
index 0000000..927e4b8
--- /dev/null
@@ -0,0 +1,79 @@
+;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:          test.lisp
+;;;; Purpose:       Regression tests for cl-base64
+;;;; Programmer:    Kevin M. Rosenberg
+;;;; Date Started:  Jan 2003
+;;;;
+;;;; $Id$
+;;;; *************************************************************************
+
+(in-package #:cl-user)
+
+(defpackage #:cl-base64-tests
+  (:use #:cl #:kmrcl #:cl-base64 #:ptester))
+
+(in-package #:cl-base64-tests)
+
+(defun do-tests ()
+  (with-tests (:name "cl-base64 tests")
+    (let ((*break-on-test-failures* t))
+      (do* ((length 0 (+ 3 length))
+            (string (make-string length) (make-string length))
+            (usb8 (make-usb8-array length) (make-usb8-array length))
+            (integer (random (expt 10 length)) (random (expt 10 length))))
+           ((>= length 300))
+        (dotimes (i length)
+          (declare (fixnum i))
+          (let ((code (random 256)))
+            (setf (schar string i) (code-char code))
+        (setf (aref usb8 i) code)))
+
+        (do* ((columns 0 (+ columns 4)))
+             ((> columns length))
+          ;; Test against cl-base64 routines
+          (test integer (base64-string-to-integer
+                         (integer-to-base64-string integer :columns columns)))
+          (test string (base64-string-to-string
+                        (string-to-base64-string string :columns columns))
+                :test #'string=)
+
+          ;; Test against AllegroCL built-in routines
+          #+allegro
+          (progn
+          (test integer (excl:base64-string-to-integer
+                         (integer-to-base64-string integer :columns columns)))
+          (test integer (base64-string-to-integer
+                         (excl:integer-to-base64-string integer)))
+          (test (string-to-base64-string string :columns columns)
+                (excl:usb8-array-to-base64-string usb8
+                                                  (if (zerop columns)
+                                                      nil
+                                                      columns))
+                :test #'string=)
+          (test string (base64-string-to-string
+                        (excl:usb8-array-to-base64-string
+                         usb8
+                         (if (zerop columns)
+                             nil
+                             columns)))
+                :test #'string=))))))
+  t)
+
+
+(defun time-routines ()
+  (let* ((str "abcdefghijklmnopqwertyu1234589jhwf2ff")
+         (usb8 (string-to-usb8-array str))
+         (int 12345678901234567890)
+         (n 50000))
+    (time-iterations n (integer-to-base64-string int))
+    (time-iterations n (string-to-base64-string str))
+    #+allego
+    (progn
+      (time-iterations n (excl:integer-to-base64-string int))
+      (time-iterations n (excl:usb8-array-to-base64-string usb8)))))
+
+
+;;#+run-test (test-base64)
diff --git a/deps/cl-fad/CHANGELOG b/deps/cl-fad/CHANGELOG
new file mode 100644 (file)
index 0000000..aa4b15e
--- /dev/null
@@ -0,0 +1,111 @@
+Version 0.7.4
+2016-07-10
+Merge pull request #13 from vibs29/master (Hans Hübner)
+Make copy-stream work for CMUCL Gray Streams (vibs29)
+
+Version 0.7.3
+2014-11-28
+remove version from cl-fad-test system (Hans Huebner)
+update support information (Hans Huebner)
+
+Version 0.7.2
+2013-07-03
+Fix documentation glitch (inconsistent download link) (Luís Oliveira)
+
+Version 0.7.1
+2013-02-18
+Fix for LispWorks (R. Wilker)
+Add :description to .asd file
+
+Version 0.7.0
+2013-01-23
+Tests, pathname manipulation functions (Marco Baringer)
+Temporary files (merged by Marco Baringer)
+Fix symlink behaviour for some platforms (Mihai Bazon and Janis Dzerins)
+
+Version 0.6.4
+2010-11-18
+Adapt to newer ClozureCL version (patch from Zach Beane, thanks to Chun Tian and Ralph Moritz as well)
+
+Version 0.6.3
+2009-09-30
+Removed dependency on :SB-EXECUTABLE (thanks to Attila Lendvai and Tobias Rittweiler)
+
+Version 0.6.2
+2008-03-12
+Never version of OpenMCL have %RMDIR (thanks to Dmitri Hrapof)
+
+Version 0.6.1
+2007-12-29
+Integrated CLISP patch for LIST-DIRECTORY sent by Dan Muller
+
+Version 0.6.0
+2007-05-28
+Support for Scieneer CL (patch from Douglas Crosher)
+
+Version 0.5.2
+2007-05-15
+Fix for (newer versions of) ECL (patch from Dustin Long)
+
+Version 0.5.1
+2006-08-11
+Added CHECKP to COPY-STREAM
+
+Version 0.5.0
+2006-04-21
+Added :BREADTH-FIRST option to WALK-DIRECTORY (thanks to Mac Chan)
+
+Version 0.4.3
+2006-03-15
+For CMUCL use TRUENAME with UNIX-RMDIR to cope with search lists (reported by Pawel Ostrowski)
+
+Version 0.4.2
+2006-01-04
+WALK-DIRECTORY now catches circular symbolic links (thanks to Gary King)
+
+Version 0.4.1
+2006-01-03
+Be more careful in DIRECTORY-WILDCARD (thanks to Gary King)
+Patches for MCL (thanks to Gary King)
+
+Version 0.4.0
+2005-12-10
+Exported COPY-STREAM (suggested by Chris Dean)
+
+Version 0.3.3
+2005-11-14
+Fixed %RMDIR for newer versions of OpenMCL (thanks to James Bielman)
+
+Version 0.3.2
+2005-09-11
+Fixed docs (correct name DELETE-DIRECTORY-AND-FILES)
+Fixed docs (OVERWRITE was missing in COPY-FILE signature)
+Added Debian link
+
+Version 0.3.1
+2005-06-02
+Fixed typo in fad.lisp (thanks to Jack D. Unrue)
+
+Version 0.3.0
+2005-06-01
+Support for ABCL (thanks to Jack D. Unrue)
+
+Version 0.2.0
+2005-05-29
+Support for ECL (thanks to Maciek Pasternacki)
+
+Version 0.1.3
+2005-04-27
+Changed implementation of DIRECTORY-EXISTS-P for LispWorks
+
+Version 0.1.2
+2005-03-17
+Fixed typo in cl-fad.system (tanks to Andrew Philpot)
+
+Version 0.1.1
+2005-01-22
+Fixed typos and versioning
+
+Version 0.1.0
+2005-01-22
+Initial release
diff --git a/deps/cl-fad/LICENSE b/deps/cl-fad/LICENSE
new file mode 100644 (file)
index 0000000..1ca070d
--- /dev/null
@@ -0,0 +1,26 @@
+;;; Copyright (c) 2004, Peter Seibel.  All rights reserved.
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/deps/cl-fad/README b/deps/cl-fad/README
new file mode 100644 (file)
index 0000000..099d514
--- /dev/null
@@ -0,0 +1,34 @@
+Complete documentation for CL-FAD can be found in the 'doc'
+directory.
+
+CL-FAD also supports Nikodemus Siivola's HYPERDOC, see
+<http://common-lisp.net/project/hyperdoc/> and
+<http://www.cliki.net/hyperdoc>.
+
+1. Installation
+
+1.1. Probably the easiest way is
+
+       (load "/path/to/cl-fad/load.lisp")
+
+     This should compile and load CL-FAD on most Common Lisp
+     implementations.
+
+1.2. With MK:DEFSYSTEM you can make a symbolic link from
+     'cl-fad.system' and 'cl-fad-test.system' to your central registry
+     (which by default is in '/usr/local/lisp/Registry/') and then issue
+     the command
+
+       (mk:compile-system "cl-fad")
+
+     Note that this relies on TRUENAME returning the original file a
+     symbolic link is pointing to. This will only work with AllegroCL
+     6.2 if you've applied all patches with (SYS:UPDATE-ALLEGRO).
+
+1.3. You can also use ASDF instead of MK:DEFSYSTEM in a similar way
+     (use the .asd files instead of the .system files).
+
+2. Test
+
+CL-FAD comes with a small test suite.  To start it just load the file
+"test.lisp" and evaluate (CL-FAD-TEST:TEST).
diff --git a/deps/cl-fad/cl-fad.asd b/deps/cl-fad/cl-fad.asd
new file mode 100644 (file)
index 0000000..aafcd67
--- /dev/null
@@ -0,0 +1,49 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/cl-fad/cl-fad.asd,v 1.21 2009/09/30 14:23:09 edi Exp $
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#+:allegro (cl:require :osi)
+
+(asdf:defsystem #:cl-fad
+  :version "0.7.4"
+  :description "Portable pathname library"
+  :serial t
+  :components ((:file "packages")
+               #+:cormanlisp (:file "corman")
+               #+:openmcl (:file "openmcl")
+               (:file "fad")
+               (:file "path" :depends-on ("fad"))
+               (:file "temporary-files" :depends-on ("fad")))
+  :depends-on (#+sbcl :sb-posix :bordeaux-threads :alexandria))
+
+(asdf:defsystem #:cl-fad-test
+  :serial t
+  :components ((:file "packages.test")
+               (:file "fad.test" :depends-on ("packages.test"))
+               (:file "temporary-files.test" :depends-on ("packages.test")))
+  :depends-on (:cl-fad :unit-test :cl-ppcre))
diff --git a/deps/cl-fad/cl-fad.system b/deps/cl-fad/cl-fad.system
new file mode 100644 (file)
index 0000000..de147f8
--- /dev/null
@@ -0,0 +1,48 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/cl-fad/cl-fad.system,v 1.8 2008/03/12 00:10:43 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package #:cl-user)
+
+(defparameter *cl-fad-base-directory*
+  (make-pathname :name nil :type nil :version nil
+                 :defaults (parse-namestring *load-truename*)))
+
+#+:allegro (require :osi)
+#+:sbcl (require :sb-executable)
+#+:sbcl (require :sb-posix)
+
+(mk:defsystem #:cl-fad
+    :source-pathname *cl-fad-base-directory*
+    :source-extension "lisp"
+    :components ((:file "packages")
+                 #+:cormanlisp (:file "corman" :depends-on ("packages"))
+                 #+:openmcl (:file "openmcl" :depends-on ("packages"))
+                 (:file "fad" :depends-on ("packages"
+                                          #+:cormanlisp "corman"
+                                          #+:openmcl "openmcl"))))
diff --git a/deps/cl-fad/corman.lisp b/deps/cl-fad/corman.lisp
new file mode 100644 (file)
index 0000000..fa9e90d
--- /dev/null
@@ -0,0 +1,86 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/cl-fad/corman.lisp,v 1.5 2009/09/30 14:23:09 edi Exp $
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl)
+
+(defun wild-pathname-p (pathspec &optional field)
+  (unless (pathnamep pathspec)
+    (setq pathspec (pathname pathspec)))
+  (labels ((name-wild-p (name)
+             (or (eq :wild name)
+                 (and (stringp name)
+                      (string= "*" name))))
+           (dir-wild-p (dir)
+             (or (find :wild dir)
+                 (find :wild-inferiors dir)
+                 (find "*" dir :test #'string=))))
+    (case field
+      ((:name)
+       (name-wild-p (pathname-name pathspec)))
+      ((:type)
+       (name-wild-p (pathname-type pathspec)))
+      ((:directory)
+       (dir-wild-p (pathname-directory pathspec)))
+      ((nil)
+       (or (name-wild-p (pathname-name pathspec))
+           (name-wild-p (pathname-type pathspec))
+           (dir-wild-p (pathname-directory pathspec))))
+      (t nil))))
+
+(defun file-namestring (pathspec)
+  (flet ((string-list-for-component (component)
+           (cond ((eq component :wild)
+                  (list "*"))
+                 (component
+                  (list component))
+                 (t nil))))
+    (let* ((pathname (pathname pathspec))
+           (name (pathnames::pathname-internal-name pathname))
+           (type (pathnames::pathname-internal-type pathname)))
+      (format nil "~{~A~}~{.~A~}"
+              (string-list-for-component name)
+              (string-list-for-component type)))))
+
+(in-package :win32)
+
+(defwinapi RemoveDirectory
+    ((lpPathName LPCSTR))
+  :return-type BOOL
+  :library-name "Kernel32"
+  :entry-name "RemoveDirectoryA"
+  :linkage-type :pascal)
+
+(defun delete-directory (pathspec)
+  "Deletes the empty directory denoted by the pathname designator
+PATHSPEC.  Returns true if successful, NIL otherwise."
+  (win:RemoveDirectory
+   (ct:lisp-string-to-c-string
+    (namestring (pathname pathspec)))))
+
+(export 'delete-directory)
diff --git a/deps/cl-fad/doc/index.html b/deps/cl-fad/doc/index.html
new file mode 100644 (file)
index 0000000..e5667eb
--- /dev/null
@@ -0,0 +1,700 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html> 
+
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+  <title>CL-FAD - A portable pathname library for Common Lisp</title>
+  <style type="text/css">
+  pre { padding:5px; background-color:#e0e0e0 }
+  h3, h4, h5 { text-decoration: underline; }
+  a { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:visited { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; } 
+  a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; }
+  a.none { text-decoration: none; padding: 0; }
+  a.none:visited { text-decoration: none; padding: 0; } 
+  a.none:hover { text-decoration: none; border: none; padding: 0; } 
+  a.none:focus { text-decoration: none; border: none; padding: 0; } 
+  a.noborder { text-decoration: none; padding: 0; } 
+  a.noborder:visited { text-decoration: none; padding: 0; } 
+  a.noborder:hover { text-decoration: none; border: none; padding: 0; } 
+  a.noborder:focus { text-decoration: none; border: none; padding: 0; }  
+  pre.none { padding:5px; background-color:#ffffff }
+  </style>
+</head>
+
+<body bgcolor=white>
+
+<h2>CL-FAD - A portable pathname library for Common Lisp</h2>
+
+<blockquote>
+<br>&nbsp;<br><h3><a name=abstract class=none>Abstract</a></h3>
+
+CL-FAD (for "<font color=red>F</font>iles <font color=red>a</font>nd
+<font color=red>D</font>irectories") is a thin layer atop Common
+Lisp's standard pathname functions.  It is intended to provide some
+unification between current CL implementations on Windows, OS X,
+Linux, and Unix.  Most of the code was written by Peter Seibel for his book <a href="http://www.gigamonkeys.com/book/"><em>Practical Common Lisp</em></a>.
+
+<p>
+
+CL-FAD comes with a <a
+href="http://www.opensource.org/licenses/bsd-license.php">BSD-style
+license</a> so you can basically do with it whatever you want.
+
+<p>
+<font color=red>Download shortcut:</font> <a href="http://weitz.de/files/cl-fad.tar.gz">http://weitz.de/files/cl-fad.tar.gz</a>.
+</blockquote>
+
+<br>&nbsp;<br><h3><a class=none name="contents">Contents</a></h3>
+<ol>
+  <li><a href="#download">Download and installation</a>
+  <li><a href="#implementations">Supported Lisp implementations</a>
+  <li><a href="#dictionary">The CL-FAD dictionary</a>
+  <ol>
+    <li><a href="#querying">Querying files, directories and pathnames</a>
+      <ol>
+        <li><a href="#directory-exists-p"><code>directory-exists-p</code> [function]</a>
+        <li><a href="#directory-pathname-p"><code>directory-pathname-p</code> [function]</a>
+        <li><a href="#file-exists-p"><code>file-exists-p</code> [function]</a>
+        <li><a href="#pathname-absolute-p"><code>pathname-absolute-p</code> [function]</a>
+        <li><a href="#pathname-equal"><code>pathname-equal</code> [function]</a>
+        <li><a href="#pathname-relative-p"><code>pathname-relative-p</code> [function]</a>
+        <li><a href="#pathname-root-p"><code>pathname-root-p</code> [function]</a>
+    </ol> </li>
+    <li><a href="#manipulating">Manipulating pathnames</a>
+      <ol>
+        <li><a href="#canonical-pathname"><code>canonical-pathname</code> [function]</a>
+        <li><a href="#merge-pathnames-as-directory"><code>merge-pathnames-as-directory</code> [function]</a>
+        <li><a href="#merge-pathnames-as-file"><code>merge-pathnames-as-file</code> [function]</a>
+        <li><a href="#pathname-as-directory"><code>pathname-as-directory</code> [function]</a>
+        <li><a href="#pathname-as-file"><code>pathname-as-file</code> [function]</a>
+        <li><a href="#pathname-directory-pathname"><code>pathname-directory-pathname</code> [function]</a>
+        <li><a href="#pathname-parent-directory"><code>pathname-parent-directory</code> [function]</a>
+    </ol> </li>
+    <li><a href="#traversing">Traversing directories</a>
+      <ol>
+        <li><a href="#list-directory"><code>list-directory</code> [function]</a>
+        <li><a href="#walk-directory"><code>walk-directory</code> [function]</a>
+    </ol> </li>
+    <li><a href="#temporary-files">Temporary Files</a>
+      <ol>
+        <li><a href="#open-temporary"><code>open-temporary</code> [function]</a>
+        <li><a href="#with-output-to-temporary-file"><code>with-output-to-temporary-file</code> [macro]</a>
+        <li><a href="#with-open-temporary-file"><code>with-open-temporary-file</code> [macro]</a>
+        <li><a href="#star-default-template-star"><code>*default-template*</code> [variable]</a>
+        <li><a href="#cannot-create-temporary-file"><code>cannot-create-temporary-file</code> [condition]</a>
+        <li><a href="#invalid-temporary-pathname-template"><code>invalid-temporary-pathname-template</code> [condition]</a>
+        <li><a href="#missing-temp-environment-variable"><code>missing-temp-environment-variable</code> [condition]</a>
+        <li><a href="#lp-host-temporary-files"><code>temporary-files</code> [logical pathname host]</a>
+      </ol>
+    </li>
+    <li><a href="#modifying">Modifying the file system</a>
+      <ol>
+        <li><a href="#copy-file"><code>copy-file</code> [function]</a>
+        <li><a href="#copy-stream"><code>copy-stream</code> [function]</a>
+        <li><a href="#delete-directory-and-files"><code>delete-directory-and-files</code> [function]</a>
+    </ol> </li>
+    <li><a href="#package-path"><code>path</code> [package]</a>
+  </ol>
+  <li><a href="#ack">Acknowledgements</a>
+</ol>
+
+
+
+<br>&nbsp;<br><h3><a class=none name="download">Download and installation</a></h3>
+
+CL-FAD together with this documentation can be downloaded from <a
+href="http://weitz.de/files/cl-fad.tar.gz">http://weitz.de/files/cl-fad.tar.gz</a>. The
+current version is 0.7.2.
+<p>
+CL-FAD comes with simple system definitions for <a
+href="http://www.cliki.net/mk-defsystem">MK:DEFSYSTEM</a> and <a
+href="http://www.cliki.net/asdf">asdf</a> so you can either adapt it
+to your needs or just unpack the archive and from within the CL-FAD
+directory start your Lisp image and evaluate the form
+<code>(mk:compile-system&nbsp;&quot;cl-fad&quot;)</code> - or <code>(asdf:oos&nbsp;'asdf:load-op&nbsp;:cl-fad)</code> for asdf - which should compile and load the whole
+system.
+Installation via <a
+href="http://www.cliki.net/asdf-install">asdf-install</a> should as well
+be possible.  Plus, there are ports
+for <a href="http://www.gentoo.org/proj/en/common-lisp/index.xml">Gentoo Linux</a> thanks to Matthew Kennedy
+and for <a href="http://packages.debian.org/cgi-bin/search_packages.pl?keywords=cl-fad&amp;searchon=names&amp;subword=1&amp;version=all&amp;release=all">Debian Linux</a> thanks to Ren&eacute; van Bevern.
+<p>
+If for some reason you can't or don't want to use MK:DEFSYSTEM or asdf you
+can just <code>LOAD</code> the file <code>load.lisp</code>.
+<p>
+The latest version of the source code lives in the github repository <a href="https://github.com/edicl/cl-fad">edicl/cl-fad</a>.
+
+If you want to send patches,
+please <a href="http://weitz.de/patches.html">read this first</a>.
+Please submit your changes
+as <a href="https://github.com/edicl/cl-fad/pulls">GitHub pull
+request"</a>.
+
+<br>&nbsp;<br><h3><a class=none name="implementations">Supported Lisp implementations</a></h3>
+
+<p>
+The following Common Lisp implementations are currently supported:
+<ul>
+<li><a href="http://armedbear.org/abcl.html">Armed Bear Common Lisp</a>
+<li><a href="http://www.cons.org/cmucl/">CMUCL</a>
+<li><a href="http://www.cormanlisp.com/">Corman Common Lisp</a>
+<li><a href="http://ecls.sf.net/">ECL</a>
+<li><a href="http://www.franz.com/products/allegrocl/">Franz AllegroCL</a>
+<li><a href="http://clisp.cons.org/">GNU CLISP</a>
+<li><a href="http://www.lispworks.com/">LispWorks</a>
+<li><a href="http://www.digitool.com/">Macintosh Common Lisp</a>
+<li><a href="http://openmcl.clozure.com/">OpenMCL</a>
+<li><a href="http://www.scieneer.com/scl/">Scieneer Common Lisp</a>
+<li><a href="http://sbcl.sourceforge.net/">Steel Bank Common Lisp</a>
+</ul>
+
+I'll gladly accepts patches to make CL-FAD work on other platforms.
+
+
+<br>&nbsp;<br><h3><a class=none name="dictionary">The CL-FAD dictionary</a></h3>
+
+<h4><a class=none name="querying">Querying files, directories and pathnames</a></h4>
+
+<p><br>[Function]
+<br><a class=none name="directory-exists-p"><b>directory-exists-p</b> <i> pathspec </i> =&gt; <i> generalized-boolean</i></a>
+
+<blockquote><br>
+Checks whether the file named by the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname designator</a> <code><i>pathspec</i></code>
+exists and if it is a directory.  Returns its <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_t.htm#truename">truename</a> if this is the
+case, <code>NIL</code> otherwise.  The truename is returned in <em>directory form</em> as if
+by <a href="#pathname-as-directory"><code>PATHNAME-AS-DIRECTORY</code></a>.
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="directory-pathname-p"><b>directory-pathname-p</b> <i> pathspec </i> =&gt; <i> generalized-boolean</i></a>
+
+<blockquote><br>
+Returns <code>NIL</code> if <code><i>pathspec</i></code> (a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname designator</a>) does not designate
+a directory, <code><i>pathspec</i></code> otherwise.  It is irrelevant whether the file or
+directory designated by <code><i>pathspec</i></code> does actually exist.
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="file-exists-p"><b>file-exists-p</b> <i> pathspec </i> =&gt; <i> generalized-boolean</i></a>
+
+<blockquote><br>
+Checks whether the file named by the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname designator</a> <code><i>pathspec</i></code>
+exists and returns its <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_t.htm#truename">truename</a> if this is the case, <code>NIL</code> otherwise.
+The truename is returned in "canonical" form, i.e. the truename of a
+directory is returned in <em>directory form</em> as if by <a href="#pathname-as-directory"><code>PATHNAME-AS-DIRECTORY</code></a>.
+</blockquote>
+
+<p><br>[Function]<br><a class=none name='pathname-absolute-p'><b>pathname-absolute-p</b> <i>a</i> =&gt; <i>result</i></a>
+<blockquote>
+<p>Returns true if <code><i>a</i></code> is an absolute pathname. This simply
+tests if <code><i>a</i></code>&#039;s directory list starts with <code>:ABSOLUTE</code></p>
+</blockquote>
+
+<p><br>[Function]<br><a class=none name='pathname-equal'><b>pathname-equal</b> <i>a b</i> =&gt; <i>result</i></a>
+<blockquote>
+
+<p>Returns <em>true</em> if <code><i>a</i></code> and <code><i>b</i></code>
+represent the same pathname. This function does not access the
+filesystem, it only looks at the components of the two pathnames to
+test if they are the same (though by passing both <code><i>a</i></code>
+and <code><i>b</i></code> to probe-file one can make this function test for
+file &#039;sameness&#039;.</p>
+
+<p>Equality is defined as:</p>
+
+<ul>
+  <li>strings that are <code>string=</code>
+  <li>symbols (including <code>nil</code> and keywords) which are <code>eql</code>
+  <li>lists of the same length with equal (as per these rules) elements.
+</ul>
+
+<p>If any of these tree conditions is false for any of the components in
+<code><i>a</i></code> and <code><i>b</i></code> then <code><i>a</i></code>
+and <code><i>b</i></code> are different, otherwise they are the same.</p>
+
+<p><em>NB:</em> This function does not convert name strings to pathnames. So
+&quot;foo.txt&quot; and #P&quot;foo.txt&quot; are different pathnames.</p>
+
+</blockquote>
+
+<p><br>[Function]<br><a class=none name='pathname-relative-p'><b>pathname-relative-p</b> <i>a</i> =&gt; <i>result</i></a>
+<blockquote>
+<p>Returns true if <code><i>a</i></code> is a relative pathname. This simply
+tests if <code><i>a</i></code>&#039;s directory starts
+with <code>:RELATIVE</code>.</p>
+</blockquote>
+
+<p><br>[Function]<br><a class=none name='pathname-root-p'><b>pathname-root-p</b> <i>a</i> =&gt; <i>result</i></a>
+<blockquote>
+<p>Returns <em>true</em> if <code><i>pathname</i></code> is the root
+directory (in other words, a directory which is its own parent).</p>
+</blockquote>
+
+<h4><a class=none name="manipulating">Manipulating pathnames</a></h4>
+
+<p><br>[Function]<br><a class=none name='canonical-pathname'><b>canonical-pathname</b> <i>pathname</i> =&gt; <i>result</i></a>
+<blockquote>
+<p>Remove reduntant information from PATHNAME.</p>
+
+<p>This simply walks down <code>PATHNAME</code>&#039;s
+pathname-directory and drops &quot;.&quot; directories, removes :back
+and its preceding element.</p>
+
+<p>NB: This function does not access the filesystem, it only looks at the
+values in the pathname and works on their known (or assumed)
+meanings.</p>
+
+<p>NB: Since this function does not access the filesystem it will only
+remove <code>:BACK</code> elements from the path (not <code>:UP</code>
+elements). Since some lisps, ccl/sbcl/clisp convert &quot;..&quot; in
+pathnames to <code>:UP</code>, and not <code>:BACK</code>, the actual
+utility of the function is limited.</p>
+</blockquote>
+
+<p><br>[Function]<br><a class=none name='merge-pathnames-as-directory'><b>merge-pathnames-as-directory</b> <i><tt>&amp;rest</tt> pathnames</i> =&gt; <i>result</i></a>
+<blockquote>
+<p>Given a list of, probably relative, pathnames returns a single
+directory pathname containing the logical concatenation of them all.</p>
+
+<p>The returned value is the current directory if one were to cd into
+each of <code><i>pathnames</i></code> in order. For this reason an
+absolute pathname will, effectively, cancel the affect of any previous
+relative pathnames.</p>
+
+<p>The returned value&#039;s defaults are taken from the first element of
+<code><i>pathnames</i></code> (host, version and device).</p>
+
+<p><em>NB:</em> Since this function only looks at directory names the name and
+type of the elements of <code><i>pathnames</i></code> are ignored. Make sure to properly
+use either trailing #\/s, or <a href="#pathname-as-directory">pathname-as-directory</a>, to get the
+expected results.</p>
+
+<p>Examples:</p>
+
+<pre>
+  (merge-pathnames-as-directory #P&quot;foo/&quot; #P&quot;bar/&quot;) == #P&quot;foo/bar/&quot;
+
+  (merge-pathnames-as-directory #P&quot;foo/&quot; #P&quot;./bar/&quot;) == #P&quot;foo/./bar/&quot;
+
+  (merge-pathnames-as-directory #P&quot;foo/&quot; #P&quot;/bar/&quot;) == #P&quot;/bar/&quot;
+
+  (merge-pathnames-as-directory #P&quot;foo/&quot; #P&quot;/bar/&quot; #P&#039;quux/file.txt) == #P&quot;/bar/quux/&quot;
+</pre>
+
+</blockquote>
+
+<p><br>[Function]<br><a class=none name='merge-pathnames-as-file'><b>merge-pathnames-as-file</b> <i><tt>&amp;rest</tt> pathnames</i> =&gt; <i>result</i></a>
+<blockquote>
+<p>Given a list of, probably relative, pathnames returns a single
+filename pathname containing the logical concatenation of them all.</p>
+
+<p>The returned value&#039;s defaults are taken from the first element of
+<code><i>pathnames</i></code> (host, version and device). The returned
+values&#039;s name, type and version are taken from the last element
+of <code><i>pathnames</i></code>. The intervening elements are used only for
+their pathname-directory values.</p>
+
+Examples:
+
+<pre>
+  (merge-pathnames-as-file #P&quot;foo/&quot; #P&quot;bar.txt&quot;) == #P&quot;foo/bar.txt&quot;
+
+  (merge-pathnames-as-file #P&quot;foo/&quot; #P&quot;./bar.txt&quot;) == #P&quot;foo/./bar.txt&quot;
+
+  (merge-pathnames-as-file #P&quot;foo/&quot; #P&quot;/bar/README&quot;) == #P&quot;/bar/README&quot;
+
+  (merge-pathnames-as-file #P&quot;/foo/&quot; #P&quot;/bar/&quot; #P&#039;quux/file.txt) == #P&quot;/bar/quux/file.txt&quot;
+</pre>
+
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="pathname-as-directory"><b>pathname-as-directory</b> <i> pathspec </i> =&gt; <i> pathname</i></a> 
+<blockquote><br>
+Converts the <em>non-wild</em> <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname designator</a> <code><i>pathspec</i></code> to <em>directory form</em>, i.e. it returns a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname">pathname</a> which would return a <em>true</em> value if fed to <a href="#directory-pathname-p"><code>DIRECTORY-PATHNAME-P</code></a>.
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="pathname-as-file"><b>pathname-as-file</b> <i> pathspec </i> =&gt; <i> pathname</i></a>
+
+<blockquote><br>
+Converts the <em>non-wild</em> <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname designator</a> <code><i>pathspec</i></code> to <em>file form</em>, i.e. it returns a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname">pathname</a> which would return a <code>NIL</code> value if fed to <a href="#directory-pathname-p"><code>DIRECTORY-PATHNAME-P</code></a>.
+</blockquote>
+
+<p><br>[Function]<br><a class=none name='pathname-directory-pathname'><b>pathname-directory-pathname</b> <i>pathname</i> =&gt; <i>result</i></a>
+<blockquote>
+<p>Returns a complete pathname representing the directory of
+<code><i>pathname</i></code>. If <code><i>pathname</i></code> is
+already a directory pathname
+(<code>name</code> <code>nil</code>, <code>type</code>
+<code>nil</code>) returns a pathname equal (as
+per <a href="#pathname-equal">pathname-equal</a>) to it.</p>
+</blockquote>
+
+<p><br>[Function]<br><a class=none name='pathname-parent-directory'><b>pathname-parent-directory</b> <i>pathname</i> =&gt; <i>result</i></a>
+<blockquote>
+
+<p>Returns a pathname which would, by name at least,
+contain <code><i>pathname</i></code> as one of its direct
+children. Symlinks can make the parent/child relationship a like
+opaque, but generally speaking the value returned by this function is
+a directory name which contains <code><i>pathname</i></code>.</p>
+
+<p>The root directory, #P&quot;/&quot;, is its own parent. The parent
+directory of a filename is the parent of the filename&#039;s
+dirname.</p>
+
+</blockquote>
+
+<h4><a class=none name="traversing">Traversing directories</a></h4>
+
+<p><br>[Function]
+<br><a class=none name="list-directory"><b>list-directory</b> <i> dirname <tt>&amp;key</tt> follow-symlinks</i> =&gt; <i> list</i></a>
+
+<blockquote><br>
+<p>
+Returns a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#fresh">fresh</a> list of pathnames corresponding to
+all files within the directory named by the non-wild <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname designator</a> <code><i>dirname</i></code>.  The pathnames of sub-directories are returned in
+<em>directory form</em> - see <a href="#pathname-as-directory"><code>PATHNAME-AS-DIRECTORY</code></a>.
+</p>
+<p>
+  If <code><i>follow-symlinks</i></code> is true (which is the
+  default), then the returned list contains truenames (symlinks will
+  be resolved) which essentially means that it might also return files
+  from <b>outside</b> the directory.  This works on all platforms.
+</p>
+<p>
+  When <code><i>follow-symlinks</i></code> is <code>NIL</code>, it should return the actual directory
+  contents, which might include symlinks. (This is currently implemented only on SBCL and CCL.)
+</p>
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="walk-directory"><b>walk-directory</b> <i> dirname fn <tt>&amp;key</tt> directories if-does-not-exist test follow-symlinks</i> =&gt; |</a>
+
+<blockquote><br>
+<p>
+  Recursively applies the function designated by the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#function_designator">function 
+  designator</a> <code><i>fn</i></code> to all files within the directory named 
+  by the non-wild <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname 
+  designator</a> <code><i>dirname</i></code> and all of its sub-directories. <code><i>fn</i></code> 
+  will only be applied to files for which the function <code><i>test</i></code> 
+  returns a <em>true</em> value. (The default value for <code><i>test</i></code> 
+  always returns <em>true</em>.) If <code><i>directories</i></code> is not <code>NIL</code>, 
+  <code><i>fn</i></code> and <code><i>test</i></code> are applied to directories 
+  as well. If <code><i>directories</i></code> is <code>:DEPTH-FIRST</code>, <code><i>fn</i></code> 
+  will be applied to the directory's contents first. If <code><i>directories</i></code> 
+  is <code>:BREADTH-FIRST</code> and <code><i>test</i></code> returns <code>NIL</code>, the 
+  directory's content will be skipped.  <code><i>if-does-not-exist</i></code> must 
+  be one of <code>:ERROR</code> or <code>:IGNORE</code> where <code>:ERROR</code> 
+  (the default) means that an error will be signaled if the directory <code><i>dirname</i></code> 
+  does not exist.
+</p>
+<p>
+  If <code><i>follow-symlinks</i></code> is true (which is
+  the default), then your callback will receive truenames.  Otherwise
+  you should get the actual directory contents, which might include
+  symlinks.  This might not be supported on all platforms.  See
+  <a href="#list-directory"><code>LIST-DIRECTORY</code></a>.
+</p>
+</blockquote>
+
+<h4><a class=none name="temporary-files">Temporary Files</a></h4>
+
+<h5>Synopsis</h5>
+
+    <p>
+      Create a temporary file and return its name:
+<pre>CL-USER&gt; (temporary-file:<code xmlns=""><a href="#with-output-to-temporary-file">with-output-to-temporary-file</a></code> (foo)
+           (print "hello" foo))
+#P"/var/folders/Yu/YuNMNBNPGoqs9G-1Wmj1dk+++TI/-Tmp-/temp-yjck024x"</pre>
+    </p>
+    <p>
+      Create a temporary file, read and write it, have it be deleted
+      automatically:
+<pre>CL-USER&gt; (temporary-file:<code xmlns=""><a href="#with-open-temporary-file">with-open-temporary-file</a></code> (foo :direction :io)
+           (print "hello" foo)
+           (file-position foo 0)
+           (read foo))
+"hello"</pre>
+    </p>
+  
+  <h5><a class="none" name="default-temporary-directory">Default temporary file directory</a></h5>
+    By default, temporary files are created in a system specific
+    directory that defaults based on operating system conventions.  On
+    Unix and Unix-like systems, the directory <tt>/tmp/</tt> is used
+    by default.  It can be overridden by setting the <tt>TMPDIR</tt>
+    environment variable.  On Windows, the value of the environment
+    variable <tt>TEMP</tt> is used.  If it is not set, temporary file
+    creation will fail.
+  
+  <h5><a class="none" name="defining-temporary-directory">Defining the temporary file directory</a></h5>
+    <p>
+      The Lisp application can set the default directory in which
+      temporary files are created by the way of the
+      <code xmlns=""><a href="#temporary-files">temporary-files</a></code> logical pathname host:
+
+<pre>(setf (<a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_logica.htm">logical-pathname-translations</a> "<code xmlns=""><a href="#temporary-files">temporary-files</a></code>") '(("*.*.*" "/var/tmp/")))</pre>
+
+      This would set the directory for temporary files to
+      <tt>/var/tmp/</tt>.  For more information about logical
+      pathnames, please refer to <a href="http://www.cs.cmu.edu/afs/cs/project/ai-repository/ai/html/cltl/clm/node208.html#SECTION002715000000000000000">Common
+      Lisp the Language, 2nd Edition</a> and the <a href="http://clhs.lisp.se/Body/19_.htm">Common Lisp
+      HyperSpec</a>.
+    </p>
+    <p>
+      Physical path names have restrictions regarding the permitted
+      character in file names.  If these restrictions conflict with
+      your desired naming scheme, you can pass a physical pathname as
+      TEMPLATE parameter to the temporary file generation function.
+    </p>
+    <p>
+      Here are a few examples:
+<pre>CL-USER&gt; (<a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_logica.htm">logical-pathname-translations</a> "temporary-files")
+(("*.*.*" #P"/var/folders/Yu/YuNMNBNPGoqs9G-1Wmj1dk+++TI/-Tmp-/"))
+CL-USER&gt; (temporary-file:<code xmlns=""><a href="#with-open-temporary-file">with-open-temporary-file</a></code> (foo)
+           (<a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_pn.htm">pathname</a> foo))
+#P"/var/folders/Yu/YuNMNBNPGoqs9G-1Wmj1dk+++TI/-Tmp-/temp-6rdqdkd1"</pre>
+
+      This used the temporary directory established in the TMPDIR
+      environment variable, by the way of the definition of the
+      temporary-files logical host definition.
+
+<pre>CL-USER&gt; (temporary-file:<code xmlns=""><a href="#with-open-temporary-file">with-open-temporary-file</a></code> (foo :template "/tmp/file.with.dots.in.name.%.txt")
+           (<a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_pn.htm">pathname</a> foo))
+#P"/tmp/file.with.dots.in.name.2EF04KUJ.txt"</pre>
+
+      Here, a physical pathname was used for the
+      <code xmlns=""><i>:template</i></code> keyword argument so that a
+      filename containing multiple dots could be generated.
+
+<pre>CL-USER&gt; (temporary-file:<code xmlns=""><a href="#with-open-temporary-file">with-open-temporary-file</a></code> (foo :template "temporary-files:blah-%.txt")
+           (<a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_pn.htm">pathname</a> foo))
+#P"/var/folders/Yu/YuNMNBNPGoqs9G-1Wmj1dk+++TI/-Tmp-/blah-72mj450d.txt"</pre>
+
+      This used the temporary-files logical pathname host, but changed
+      the filename slightly.
+
+<pre>CL-USER&gt; *default-pathname-defaults*
+#P"/Users/hans/"
+CL-USER&gt; (temporary-file:<code xmlns=""><a href="#with-open-temporary-file">with-open-temporary-file</a></code> (foo :template "blah-%.txt")
+           (<a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_pn.htm">pathname</a> foo))
+#P"/Users/hans/blah-5OEJELG2.txt"</pre>
+
+      Here, a relative pathname was used in the template, which
+      caused the file to be generated in the directory established
+      by <a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/v_defaul.htm">*default-pathname-defaults*</a>.
+    </p>
+    <p>
+      Alternatively, the <code xmlns=""><a href="#*default-template*">*default-template*</a></code>
+      special variable can be set to define a custom default template
+      for generating names.
+    </p>
+  
+  <h5 xmlns=""><a class="none" name="security">Security</a></h5>
+    The TEMPORARY-FILE library does not directly address security
+    issues.  The application that uses it needs to take additional
+    measures if it is important that files created by one process
+    cannot be accessed by other, unrelated processes.  This can be
+    done by using the system dependent security mechanisms like
+    default file permissions or access control lists.
+
+  <h5>Dictionary</h5>
+
+
+    <p xmlns="">[Function]<br><a class="none" name="open-temporary"><b>open-temporary</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">&amp;rest open-arguments &amp;key template generate-random-string max-tries &amp;allow-other-keys</clix:lambda-list></i>
+          =&gt;
+          <i>stream</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+        <p xmlns="http://www.w3.org/1999/xhtml">
+          Create a file with a randomly generated name and return the
+          opened stream.  The resulting pathname is generated from
+          <code xmlns=""><i>template</i></code>, which is a string
+          representing a pathname template.  A percent sign (%) in
+          that string is replaced by a randomly generated string to
+          make the filename unique.  The default for
+          <code xmlns=""><i>template</i></code> places temporary files in the
+          <code xmlns=""><a href="#temporary-files">temporary-files</a></code> logical pathname host,
+          which is automatically set up in a system specific manner.
+          The file name generated from <code xmlns=""><i>template</i></code>
+          is merged with <a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/v_defaul.htm">*default-pathname-defaults*</a>,
+          so random pathnames relative to that directory can be
+          generated by not specifying a directory in
+          <code xmlns=""><i>template</i></code>.
+        </p>
+        <p xmlns="http://www.w3.org/1999/xhtml">
+          <code xmlns=""><i>generate-random-string</i></code> can be passed to
+          override the default function that generates the random name
+          component.  It should return a random string consisting of
+          characters that are permitted in a pathname (logical or
+          physical, depending on <code xmlns=""><i>template</i></code>).
+        </p>
+        <p xmlns="http://www.w3.org/1999/xhtml">
+          The name of the temporary file can be accessed calling the
+          <a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_pn.htm">pathname</a>
+          function on <code xmlns=""><i>stream</i></code>.  For convenience,
+          the temporary file is opened on the physical pathname,
+          i.e. if the <code xmlns=""><i>template</i></code> designate a
+          logical pathname the translation to a physical pathname is
+          performed before opening the stream.
+        </p>
+        <p xmlns="http://www.w3.org/1999/xhtml">
+          In order to create a unique file name,
+          <code xmlns=""><a href="#open-temporary">open-temporary</a></code> may loop internally up
+          to <code xmlns=""><i>max-tries</i></code> times before giving up and
+          signalling a
+          <code xmlns=""><a href="#cannot-create-temporary-file">cannot-create-temporary-file</a></code> condition.
+        </p>
+        <p xmlns="http://www.w3.org/1999/xhtml">
+          Any unrecognized keyword arguments are passed to the call to
+          <a xmlns="" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_open.htm">open</a>.
+        </p>
+      </clix:description></blockquote></p>
+    <p xmlns="">[Macro]<br><a class="none" name="with-output-to-temporary-file"><b>with-output-to-temporary-file</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">(stream &amp;rest args) &amp;body body</clix:lambda-list></i>
+          =&gt;
+          <i>pathname</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+        Create a temporary file using
+        <code xmlns=""><a href="#open-temporary">open-temporary</a></code> with
+        <code xmlns=""><i>args</i></code> and run <code xmlns=""><i>body</i></code>
+        with <code xmlns=""><i>stream</i></code> bound to the temporary file
+        stream.  Returns the pathname of the file that has been
+        created.  See <code xmlns=""><a href="#open-temporary">open-temporary</a></code> for
+        permitted options.
+      </clix:description></blockquote></p>
+    <p xmlns="">[Macro]<br><a class="none" name="with-open-temporary-file"><b>with-open-temporary-file</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">(stream &amp;rest args &amp;key keep &amp;allow-other-keys) &amp;body body</clix:lambda-list></i>
+          =&gt;
+          <i>values</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+        Create a temporary file using
+        <code xmlns=""><a href="#open-temporary">open-temporary</a></code> with
+        <code xmlns=""><i>args</i></code> and run <code xmlns=""><i>body</i></code>
+        with <code xmlns=""><i>stream</i></code> bound to the temporary file
+        stream.  Returns the values returned by
+        <code xmlns=""><i>body</i></code>.  By default, the file is deleted
+        when <code xmlns=""><i>body</i></code> is exited. If a true value is
+        passed in <code xmlns=""><i>keep</i></code>, the file is not deleted
+        when the body is exited.  See
+        <code xmlns=""><a href="#open-temporary">open-temporary</a></code> for more permitted
+        options.
+      </clix:description></blockquote></p>
+    <p xmlns="">
+      [Special variable]<br><a class="none" name="*default-template*"><b>*default-template*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+        This variable can be set to a string representing the desired
+        default template for temporary file name generation.  See
+        <code xmlns=""><a href="#open-temporary">open-temporary</a></code> for a description of the
+        template string format.
+      </clix:description></blockquote></p>
+    <p xmlns="">
+      [Condition type]<br><a class="none" name="cannot-create-temporary-file"><b>cannot-create-temporary-file</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+        Signalled when an attempt to create unique temporary file name
+        failed after the established number of retries.
+      </clix:description></blockquote></p>
+    <p xmlns="">
+      [Condition type]<br><a class="none" name="invalid-temporary-pathname-template"><b>invalid-temporary-pathname-template</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+        Signalled when the <code xmlns=""><i>template</i></code> argument to
+        <code xmlns=""><a href="#open-temporary">open-temporary</a></code> does not contain a valid
+        template string.  The template string must contain a percent
+        sign, which is replaced by the generated random string to
+        yield the filename.
+      </clix:description></blockquote></p>
+    <p xmlns="">
+      [Condition type]<br><a class="none" name="missing-temp-environment-variable"><b>missing-temp-environment-variable</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+        (Windows only) Signalled when the TEMP environment variable is
+        not set.
+      </clix:description></blockquote></p>
+    <p xmlns="">
+      [Logical Pathname Host]<br><a class="none" name="lp-host-temporary-files"><b>temporary-files</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+        This logical pathname host defines where temporary files are
+        stored by default.  It is initialized in a suitable system
+        specific fashion: On Unix and Unix-like systems, the directory
+        specified in the TMPDIR environment variable is used.  If that
+        variable is not set, /tmp is used as the default.  On Windows,
+        the directory specified in the TEMP environment variable is
+        used.  If it is not set, a
+        <code xmlns=""><a href="#missing-temp-environment-variable">missing-temp-environment-variable</a></code> error
+        is signalled.
+      </clix:description></blockquote></p>
+
+
+<h4><a class=none name="modifying">Modifying the file system</a></h4>
+
+<p><br>[Function]
+<br><a class=none name="copy-file"><b>copy-file</b> <i> from to <tt>&amp;key</tt> overwrite</i> =&gt; |</a>
+
+<blockquote><br>
+Copies the file designated by the non-wild <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname designator</a> <code><i>from</i></code> to the
+file designated by the non-wild <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname designator</a> <code><i>to</i></code>.  If <code><i>overwrite</i></code> is <em>true</em> (the default is <code>NIL</code>)
+overwrites the file designtated by <code><i>to</i></code> if it exists.
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="copy-stream"><b>copy-stream</b> <i> from to <tt>&amp;optional</tt> checkp</i> =&gt; |</a>
+
+<blockquote><br> Copies into <code><i>to</i></code> (a stream)
+from <code><i>from</i></code> (also a stream) until the end
+of <code><i>from</i></code> is reached.  The streams should have the
+same <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_stm_el.htm">element
+type</a> unless they are bivalent.  If <code><i>checkp</i></code> is
+true (which is the default), the function will signal an error if the
+element types aren't the same.
+</blockquote>
+<p><br>[Function]
+<br><a class=none name="delete-directory-and-files"><b>delete-directory-and-files</b> <i> dirname<tt>&amp;key</tt> if-does-not-exist</i> =&gt; |</a>
+
+<blockquote><br>
+<p>
+Recursively deletes all files and directories within the directory
+designated by the non-wild <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#pathname_designator">pathname designator</a> <code><i>dirname</i></code> including
+<code><i>dirname</i></code> itself.  <code><i>if-does-not-exist</i></code> must be one of <code>:ERROR</code> or <code>:IGNORE</code>
+where <code>:ERROR</code> (the default) means that an error will be signaled if the directory
+<code><i>dirname</i></code> does not exist.
+</p>
+<p>
+  <b>Warning:</b> this function <em>might</em> remove files from outside the
+  directory, if the directory that you are deleting contains links to
+  external files.  This is currently fixed for SBCL and CCL.
+</p>
+</blockquote>
+
+<h4>The <code>PATH</code> package</h4>
+
+<p><br>[Package]
+<br><a class=none name="package-path">(defpackage <b>path</b>)</a>
+
+<blockquote>
+Provides a set of short names for commonly used pathname manipulation
+functions (these are all functions from the <code>cl-fad</code>
+package which are being exported under different names):
+<dl>
+  <dt><code>dirname</code></dt> <dd><a href="#pathname-as-directory">pathname-as-directory</a></dd>
+  <dt><code>basename</code></dt> <dd><code>cl:file-namestring</code></dd>
+  <dt><code>-e</code></dt> <dd><a href="#file-exists-p">file-exists-p</a></dd>
+  <dt><code>-d</code></dt> <dd><a href="#directory-exists-p">directory-exists-p</a></dd>
+  <dt><code>catfile</code></dt> <dd><a href="#merge-pathnames-as-file">merge-pathnames-as-file</a></dd>
+  <dt><code>catdir</code></dt> <dd><a href="#merge-pathnames-as-directory">merge-pathnames-as-directory</a></dd>
+  <dt><code>rm-r</code></dt> <dd><a href="#delete-directory-and-files">delete-directory-and-files</a></dd>
+  <dt><code>=</code></dt> <dd><a href="#pathname-equal">pathname-equal</a></dd>
+  <dt><code>absolute-p</code></dt> <dd><a href="#pathname-absolute-p">pathname-absolute-p</a></dd>
+  <dt><code>relative-p</code></dt> <dd><a href="#pathname-relative-p">pathname-relative-p</a></dd>
+  <dt><code>root-p</code></dt> <dd><a href="#pathname-root-p">pathname-root-p</a></dd>
+</dl>
+</blockquote>
+
+
+<br>&nbsp;<br><h3><a class=none name="ack">Acknowledgements</a></h3>
+
+The original code for this library was written by Peter Seibel for his
+book <a href="http://www.gigamonkeys.com/book/"><em>Practical Common
+Lisp</em></a>. I added some stuff and made sure it worked properly on
+Windows, specifically with CCL.  Thanks to James Bielman, Maciek
+Pasternacki, Jack D. Unrue, Gary King, and Douglas Crosher who sent
+patches for OpenMCL, ECL, ABCL, MCL, and Scieneer&nbsp;CL.
+
+<p>
+$Header: /usr/local/cvsrep/cl-fad/doc/index.html,v 1.33 2009/09/30 14:23:12 edi Exp $
+<p><a href="http://weitz.de/index.html">BACK TO MY HOMEPAGE</a>
+
+</body>
+</html>
+
diff --git a/deps/cl-fad/fad.lisp b/deps/cl-fad/fad.lisp
new file mode 100644 (file)
index 0000000..b436bb7
--- /dev/null
@@ -0,0 +1,584 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-FAD; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/cl-fad/fad.lisp,v 1.35 2009/09/30 14:23:10 edi Exp $
+
+;;; Copyright (c) 2004, Peter Seibel.  All rights reserved.
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-fad)
+
+(defun component-present-p (value)
+  "Helper function for DIRECTORY-PATHNAME-P which checks whether VALUE
+   is neither NIL nor the keyword :UNSPECIFIC."
+  (and value (not (eql value :unspecific))))
+
+(defun directory-pathname-p (pathspec)
+  "Returns NIL if PATHSPEC \(a pathname designator) does not designate
+a directory, PATHSPEC otherwise.  It is irrelevant whether file or
+directory designated by PATHSPEC does actually exist."
+  (and
+    (not (component-present-p (pathname-name pathspec)))
+    (not (component-present-p (pathname-type pathspec)))
+    pathspec))
+
+(defun pathname-as-directory (pathspec)
+  "Converts the non-wild pathname designator PATHSPEC to directory
+form."
+  (let ((pathname (pathname pathspec)))
+    (when (wild-pathname-p pathname)
+      (error "Can't reliably convert wild pathnames."))
+    (cond ((not (directory-pathname-p pathspec))
+           (make-pathname :directory (append (or (pathname-directory pathname)
+                                                 (list :relative))
+                                             (list (file-namestring pathname)))
+                          :name nil
+                          :type nil
+                          :defaults pathname))
+          (t pathname))))
+
+(defun directory-wildcard (dirname)
+  "Returns a wild pathname designator that designates all files within
+the directory named by the non-wild pathname designator DIRNAME."
+  (when (wild-pathname-p dirname)
+    (error "Can only make wildcard directories from non-wildcard directories."))
+  (make-pathname :name #-:cormanlisp :wild #+:cormanlisp "*"
+                 :type #-(or :clisp :cormanlisp) :wild
+                       #+:clisp nil
+                       #+:cormanlisp "*"
+                 :defaults (pathname-as-directory dirname)))
+
+#+:clisp
+(defun clisp-subdirectories-wildcard (wildcard)
+  "Creates a wild pathname specifically for CLISP such that
+sub-directories are returned by DIRECTORY."
+  (make-pathname :directory (append (pathname-directory wildcard)
+                                    (list :wild))
+                 :name nil
+                 :type nil
+                 :defaults wildcard))
+
+(defun list-directory (dirname &key (follow-symlinks t))
+  "Returns a fresh list of pathnames corresponding to all files within
+   the directory named by the non-wild pathname designator DIRNAME.
+   The pathnames of sub-directories are returned in directory form -
+   see PATHNAME-AS-DIRECTORY.
+
+  If FOLLOW-SYMLINKS is true, then the returned list contains
+truenames (symlinks will be resolved) which essentially means that it
+might also return files from *outside* the directory.  This works on
+all platforms.
+
+  When FOLLOW-SYMLINKS is NIL, it should return the actual directory
+contents, which might include symlinks.  Currently this works on SBCL
+and CCL."
+  (declare (ignorable follow-symlinks))
+  (when (wild-pathname-p dirname)
+    (error "Can only list concrete directory names."))
+  #+:ecl
+  (let ((dir (pathname-as-directory dirname)))
+    (concatenate 'list
+                 (directory (merge-pathnames (pathname "*/") dir))
+                 (directory (merge-pathnames (pathname "*.*") dir))))
+  #-:ecl
+  (let ((wildcard (directory-wildcard dirname)))
+    #+:abcl (system::list-directory dirname)
+    #+:sbcl (directory wildcard :resolve-symlinks follow-symlinks)
+    #+(or :cmu :scl :lispworks) (directory wildcard)
+    #+(or :openmcl :digitool) (directory wildcard :directories t :follow-links follow-symlinks)
+    #+:allegro (directory wildcard :directories-are-files nil)
+    #+:clisp (nconc (directory wildcard :if-does-not-exist :keep)
+                    (directory (clisp-subdirectories-wildcard wildcard)))
+    #+:cormanlisp (nconc (directory wildcard)
+                         (cl::directory-subdirs dirname)))
+  #-(or :sbcl :cmu :scl :lispworks :openmcl :allegro :clisp :cormanlisp :ecl :abcl :digitool)
+  (error "LIST-DIRECTORY not implemented"))
+
+(defun pathname-as-file (pathspec)
+  "Converts the non-wild pathname designator PATHSPEC to file form."
+  (let ((pathname (pathname pathspec)))
+    (when (wild-pathname-p pathname)
+      (error "Can't reliably convert wild pathnames."))
+    (cond ((directory-pathname-p pathspec)
+           (let* ((directory (pathname-directory pathname))
+                  (name-and-type (pathname (first (last directory)))))
+             (make-pathname :directory (butlast directory)
+                            :name (pathname-name name-and-type)
+                            :type (pathname-type name-and-type)
+                            :defaults pathname)))
+          (t pathname))))
+
+(defun file-exists-p (pathspec)
+  "Checks whether the file named by the pathname designator PATHSPEC
+exists and returns its truename if this is the case, NIL otherwise.
+The truename is returned in `canonical' form, i.e. the truename of a
+directory is returned as if by PATHNAME-AS-DIRECTORY."
+  #+(or :sbcl :lispworks :openmcl :ecl :digitool) (probe-file pathspec)
+  #+:allegro (or (excl:probe-directory (pathname-as-directory pathspec))
+                 (probe-file pathspec))
+  #+(or :cmu :scl :abcl) (or (probe-file (pathname-as-directory pathspec))
+                             (probe-file pathspec))
+  #+:cormanlisp (or (and (ccl:directory-p pathspec)
+                         (pathname-as-directory pathspec))
+                    (probe-file pathspec))
+  #+:clisp (or (ignore-errors
+                 (let ((directory-form (pathname-as-directory pathspec)))
+                   (when (ext:probe-directory directory-form)
+                     directory-form)))
+               (ignore-errors
+                 (probe-file (pathname-as-file pathspec))))
+  #-(or :sbcl :cmu :scl :lispworks :openmcl :allegro :clisp :cormanlisp :ecl :abcl :digitool)
+  (error "FILE-EXISTS-P not implemented"))
+
+(defun directory-exists-p (pathspec)
+  "Checks whether the file named by the pathname designator PATHSPEC
+exists and if it is a directory.  Returns its truename if this is the
+case, NIL otherwise.  The truename is returned in directory form as if
+by PATHNAME-AS-DIRECTORY."
+  #+:allegro
+  (and (excl:probe-directory pathspec)
+       (pathname-as-directory (truename pathspec)))
+  #+:lispworks
+  (and (lw:file-directory-p pathspec)
+       (pathname-as-directory (truename pathspec)))
+  #-(or :allegro :lispworks)
+  (let ((result (file-exists-p pathspec)))
+    (and result
+         (directory-pathname-p result)
+         result)))
+
+(defun walk-directory (dirname fn &key directories
+                                       (if-does-not-exist :error)
+                                       (test (constantly t))
+                                       (follow-symlinks t))
+  "Recursively applies the function FN to all files within the
+directory named by the non-wild pathname designator DIRNAME and all of
+its sub-directories.  FN will only be applied to files for which the
+function TEST returns a true value.  If DIRECTORIES is not NIL, FN and
+TEST are applied to directories as well.  If DIRECTORIES
+is :DEPTH-FIRST, FN will be applied to the directory's contents first.
+If DIRECTORIES is :BREADTH-FIRST and TEST returns NIL, the directory's
+content will be skipped. IF-DOES-NOT-EXIST must be one of :ERROR
+or :IGNORE where :ERROR means that an error will be signaled if the
+directory DIRNAME does not exist.  If FOLLOW-SYMLINKS is T, then your
+callback will receive truenames.  Otherwise you should get the actual
+directory contents, which might include symlinks.  This might not be
+supported on all platforms.  See LIST-DIRECTORY."
+  (labels ((walk (name)
+             (cond
+               ((directory-pathname-p name)
+                ;; the code is written in a slightly awkward way for
+                ;; backward compatibility
+                (cond ((not directories)
+                       (dolist (file (list-directory name :follow-symlinks follow-symlinks))
+                         (walk file)))
+                      ((eql directories :breadth-first)
+                       (when (funcall test name)
+                         (funcall fn name)
+                         (dolist (file (list-directory name :follow-symlinks follow-symlinks))
+                           (walk file))))
+                      ;; :DEPTH-FIRST is implicit
+                      (t (dolist (file (list-directory name :follow-symlinks follow-symlinks))
+                           (walk file))
+                         (when (funcall test name)
+                           (funcall fn name)))))
+               ((funcall test name)
+                (funcall fn name)))))
+    (let ((pathname-as-directory (pathname-as-directory dirname)))
+      (case if-does-not-exist
+        ((:error)
+         (cond ((not (file-exists-p pathname-as-directory))
+                (error "File ~S does not exist."
+                       pathname-as-directory))
+               (t (walk pathname-as-directory))))
+        ((:ignore)
+         (when (file-exists-p pathname-as-directory)
+           (walk pathname-as-directory)))
+        (otherwise
+         (error "IF-DOES-NOT-EXIST must be one of :ERROR or :IGNORE."))))
+    (values)))
+
+(defvar *stream-buffer-size* 8192)
+
+(defun copy-stream (from to &optional (checkp t))
+  "Copies into TO \(a stream) from FROM \(also a stream) until the end
+of FROM is reached, in blocks of *stream-buffer-size*.  The streams
+should have the same element type.  If CHECKP is true, the streams are
+checked for compatibility of their types."
+  (when checkp
+    (unless (subtypep (stream-element-type to) (stream-element-type from))
+      (error "Incompatible streams ~A and ~A." from to)))
+  (let ((buf (make-array *stream-buffer-size*
+                         :element-type (stream-element-type from))))
+    (loop
+       (let ((pos #-:clisp (read-sequence buf from)
+                  #+:clisp (ext:read-byte-sequence buf from :no-hang nil)))
+         (when (zerop pos) (return))
+         (write-sequence buf to :end pos))))
+  (values))
+
+(defun copy-file (from to &key overwrite)
+  "Copies the file designated by the non-wild pathname designator FROM
+to the file designated by the non-wild pathname designator TO.  If
+OVERWRITE is true overwrites the file designtated by TO if it exists."
+  #+:allegro (excl.osi:copy-file from to :overwrite overwrite)
+  #-:allegro
+  (let ((element-type #-:cormanlisp '(unsigned-byte 8)
+                      #+:cormanlisp 'unsigned-byte))
+    (with-open-file (in from :element-type element-type)
+      (with-open-file (out to :element-type element-type
+                              :direction :output
+                              :if-exists (if overwrite
+                                           :supersede
+                                           #-:cormanlisp :error
+                                           #+:cormanlisp nil))
+        #+:cormanlisp
+        (unless out
+          (error (make-condition 'file-error
+                                 :pathname to
+                                 :format-control "File already exists.")))
+        (copy-stream in out))))
+  (values))
+
+(defun delete-directory-and-files (dirname &key (if-does-not-exist :error))
+  "Recursively deletes all files and directories within the directory
+designated by the non-wild pathname designator DIRNAME including
+DIRNAME itself.  IF-DOES-NOT-EXIST must be one of :ERROR or :IGNORE
+where :ERROR means that an error will be signaled if the directory
+DIRNAME does not exist.
+
+NOTE: this function is dangerous if the directory that you are
+removing contains symlinks to files outside of it - the target files
+might be removed instead!  This is currently fixed for SBCL and CCL."
+
+  #+:allegro (excl.osi:delete-directory-and-files dirname
+                                                  :if-does-not-exist if-does-not-exist)
+
+  #+:sbcl
+  (if (directory-exists-p dirname)
+      (sb-ext:delete-directory dirname :recursive t)
+      (ecase if-does-not-exist
+        (:error  (error "~S is not a directory" dirname))
+        (:ignore nil)))
+
+  #+:ccl-has-delete-directory
+  (if (directory-exists-p dirname)
+      (ccl:delete-directory dirname)
+      (ecase if-does-not-exist
+        (:error  (error "~S is not a directory" dirname))
+        (:ignore nil)))
+
+  #-(or :allegro :sbcl :ccl-has-delete-directory)
+  (walk-directory dirname
+                  (lambda (file)
+                    (cond ((directory-pathname-p file)
+                           #+:lispworks (lw:delete-directory file)
+                           #+:cmu (multiple-value-bind (ok err-number)
+                                      (unix:unix-rmdir (namestring (truename file)))
+                                    (unless ok
+                                      (error "Error number ~A when trying to delete ~A"
+                                             err-number file)))
+                           #+:scl (multiple-value-bind (ok errno)
+                                      (unix:unix-rmdir (ext:unix-namestring (truename file)))
+                                    (unless ok
+                                      (error "~@<Error deleting ~S: ~A~@:>"
+                                             file (unix:get-unix-error-msg errno))))
+                           #+:clisp (ext:delete-dir file)
+                           #+:openmcl (cl-fad-ccl:delete-directory file)
+                           #+:cormanlisp (win32:delete-directory file)
+                           #+:ecl (si:rmdir file)
+                           #+(or :abcl :digitool) (delete-file file))
+                          (t (delete-file file))))
+                  :follow-symlinks nil
+                  :directories t
+                  :if-does-not-exist if-does-not-exist)
+  (values))
+
+(defun pathname-directory-pathname (pathname)
+  "Returns a complete pathname representing the directory of
+PATHNAME. If PATHNAME is already a directory pathname (name NIL, type
+NIL) returns a pathname equal (as per pathname=) to it."
+  (make-pathname :defaults pathname
+                 :name nil :type nil))
+
+(defun pathname-parent-directory (pathname)
+  "Returns a pathname which would, by name at least, contain PATHNAME
+as one of its direct children. Symlinks can make the parent/child
+relationship a like opaque, but generally speaking the value returned
+by this function is a directory name which contains PATHNAME.
+
+The root directory, #P\"/\", is its own parent. The parent directory
+of a filename is the parent of the filename's dirname."
+  (canonical-pathname
+   (make-pathname :defaults pathname
+                  :directory (if (pathname-root-p pathname)
+                                 (list :absolute)
+                                 (append (or (pathname-directory pathname)
+                                             (list :relative))
+                                         (list :back))))))
+
+(defun canonical-pathname (pathname)
+  "Remove reduntant information from PATHNAME.
+
+This simply walks down PATHNAME's pathname-directory and drops \".\"
+directories, removes :back and its preceding element.
+
+NB: This function does not access the filesystem, it only looks at the
+values in the pathname and works on their known (or assumed)
+meanings.
+
+NB: Since this function does not access the filesystem it will only
+remove :BACK elements from the path (not :UP elements). Since some
+lisps, ccl/sbcl/clisp convert \"..\" in pathnames to :UP, and
+not :BACK, the actual utility of the function is limited."
+  (let ((pathname (pathname pathname))) ;; just make sure to get a pathname object
+    (loop
+       with full-dir = (or (pathname-directory pathname)
+                           (list :relative))
+       with canon-dir = (if (member (first full-dir) '(:relative :absolute))
+                            (list (pop full-dir))
+                            (list :relative))
+       while full-dir
+       do (cond
+            ((string= "." (first full-dir))
+             (pop full-dir))
+            ((eql :back (second full-dir))
+             (pop full-dir)
+             (pop full-dir))
+            (t (push (pop full-dir) canon-dir)))
+       finally (return (make-pathname :defaults pathname :directory (nreverse canon-dir))))))
+
+(defun merge-pathnames-as-directory (&rest pathnames)
+  "Given a list of, probably relative, pathnames returns a single
+directory pathname containing the logical concatenation of them all.
+
+The returned value is the current directory if one were to cd into
+each of PATHNAMES in order. For this reason an absolute pathname will,
+effectively, cancel the affect of any previous relative pathnames.
+
+The returned value's defaults are taken from the first element of
+PATHNAMES (host, version and device).
+
+NB: Since this function only looks at directory names the name and
+type of the elements of PATHNAMES are ignored. Make sure to properly
+use either trailing #\\/s, or pathname-as-directory, to get the
+expected results.
+
+Examples:
+
+    (merge-pathnames-as-directory #P\"foo/\" #P\"bar/\") == #P\"foo/bar/\"
+    (merge-pathnames-as-directory #P\"foo/\" #P\"./bar/\") == #P\"foo/./bar/\"
+    (merge-pathnames-as-directory #P\"foo/\" #P\"/bar/\") == #P\"/bar/\"
+    (merge-pathnames-as-directory #P\"foo/\" #P\"/bar/\" #P'quux/file.txt) == #P\"/bar/quux/\"
+"
+  (when (null pathnames)
+    (return-from merge-pathnames-as-directory
+      (make-pathname :defaults *default-pathname-defaults* :directory nil :name nil :type nil)))
+  (let* ((pathnames (mapcar #'pathname pathnames)))
+    (loop
+       with defaults = (first pathnames)
+       with dir = (pathname-directory defaults)
+       for pathname in (rest pathnames)
+       for type = (first (pathname-directory pathname))
+       do (ecase type
+            ((nil) ;; this is equivalent to (:relative) == ".", so, for this function, just do nothing.
+             )
+            (:absolute
+             (setf dir (pathname-directory pathname)))
+            (:relative
+             (setf dir (append dir (rest (pathname-directory pathname))))))
+       finally (return (make-pathname :defaults defaults :directory dir :name nil :type nil)))))
+
+(defun merge-pathnames-as-file (&rest pathnames)
+    "Given a list of, probably relative, pathnames returns a single
+filename pathname containing the logical concatenation of them all.
+
+The returned value's defaults are taken from the first element of
+PATHNAMES (host, version and device). The returned values's name, type
+and version are taken from the last element of PATHNAMES. The
+intervening elements are used only for their pathname-directory
+values.
+
+Examples:
+
+    (merge-pathnames-as-file #P\"foo/\" #P\"bar.txt\") == #P\"foo/bar.txt\"
+    (merge-pathnames-as-file #P\"foo/\" #P\"./bar.txt\") == #P\"foo/./bar.txt\"
+    (merge-pathnames-as-file #P\"foo/\" #P\"/bar/README\") == #P\"/bar/README\"
+    (merge-pathnames-as-file #P\"/foo/\" #P\"/bar/\" #P'quux/file.txt) == #P\"/bar/quux/file.txt\"
+"
+    (case (length pathnames)
+      (0
+       (when (null pathnames)
+         (make-pathname :defaults *default-pathname-defaults*
+                        :directory nil
+                        :name nil
+                        :type nil)))
+      (1
+       (pathname-as-file (first pathnames)))
+      (t
+       (let* ((defaults (pop pathnames))
+              (file-name-part (first (last pathnames)))
+              (file-name-directory (make-pathname :defaults file-name-part
+                                                  :name nil :type nil))
+              (pathnames (butlast pathnames)))
+         (make-pathname :defaults (apply #'merge-pathnames-as-directory (append (list defaults) pathnames (list file-name-directory)))
+                        :name (pathname-name file-name-part)
+                        :type (pathname-type file-name-part)
+                        :version (pathname-version file-name-part))))))
+
+(defmacro with-component-testers ((a b key) &body body)
+  (let ((k (gensym)))
+    `(let* ((,k ,key)
+            (,a (funcall ,k ,a))
+            (,b (funcall ,k ,b)))
+       (labels ((components-are (test)
+                  (and (funcall test ,a) (funcall test ,b)))
+
+                (components-are-member (values)
+                  (and (member ,a values :test #'eql)
+                       (member ,b values :test #'eql)
+                       (eql ,a ,b)))
+                
+                (components-are-string= ()
+                  (and (stringp ,a) (stringp ,b) (string= ,a ,b)))
+
+                (components-are-every (test)
+                  (and (consp ,a)
+                       (consp ,b)
+                       (every test ,a ,b))))
+
+         
+         (if (or ,@body)
+             (values t ,a ,b)
+             nil)))))
+
+(defun pathname-host-equal (a b)
+  (with-component-testers (a b #'pathname-host)
+    (eq a b)
+    (components-are-member '(nil :unspecific))
+    (components-are-string=)
+    (and (consp a)
+         (consp b)
+         (components-are-every #'string=))))
+
+(defun pathname-device-equal (a b)
+  (with-component-testers (a b #'pathname-device)
+    (components-are-member '(nil :unspecific))
+    (components-are-string=)))
+
+(defun pathname-directory-equal (a b)
+  (with-component-testers (a b #'pathname-directory)
+    (and (null a) (null b))
+    (and (= (length a) (length b))
+         (every (lambda (a b)
+                  (or (and (stringp a) (stringp b) (string= a b))
+                      (and (null a) (null b))
+                      (and (keywordp a) (keywordp b) (eql a b))))
+                a b))))
+
+(defun pathname-name-equal (a b)
+  (with-component-testers (a b #'pathname-name)
+    (components-are-member '(nil :wild :unspecific))
+    (components-are-string=)))
+
+(defun pathname-type-equal (a b)
+  (with-component-testers (a b #'pathname-type)
+    (components-are-member '(nil :wild :unspecific))
+    (components-are-string=)))
+
+(defun pathname-version-equal (a b)
+  (with-component-testers (a b #'pathname-version)
+    (and (null a) (null b))
+    (components-are-member '(:wild :newest :unspecific))
+    (and (integerp a) (integerp b) (= a b))))
+
+(defun pathname-equal (a b)
+  "Returns T if A and B represent the same pathname. This function
+does not access the filesystem, it only looks at the components of the
+two pathnames to test if they are the same (though by
+passing both A and B to probe-file one can make this function test for file 'sameness'.
+
+Equality is defined as:
+
+  - strings that are string equal
+  - symbol (including nil) or keywords which are eql
+  - lists of the same length with equal (as per these rules) elements.
+
+if any of these tree conditions is false for any of the components in
+A and B then A and B are different, otherwise they are the same.
+
+NB: This function does not convert name strings to pathnames. So
+\"foo.txt\" and #P\"foo.txt\" are different pathnames."
+  (if (and a b)
+      (if (and (pathname-host-equal a b)
+               (pathname-device-equal a b)
+               (pathname-directory-equal a b)
+               (pathname-name-equal a b)
+               (pathname-type-equal a b)
+               (pathname-version-equal a b))
+          (values t a b)
+          (values nil))
+      (values nil)))
+
+(defun pathname-absolute-p (a)
+  "Returns true if A is an absolute pathname.
+
+This simply tests if A's directory list starts with :ABSOLUTE"
+  (eql :absolute (first (pathname-directory (pathname a)))))
+
+(defun pathname-relative-p (a)
+  "Returns true if A is a relative pathname.
+
+This simply tests if A's directory starts with :RELATIVE."
+  (let ((dir (pathname-directory (pathname a))))
+    (or (null dir) (eql :relative (first dir)))))
+
+(defun pathname-root-p (a)
+  (let ((dir (pathname-directory (pathname a))))
+    (and (eql :absolute (first dir))
+         (= 1 (length dir)))))
+
+(pushnew :cl-fad *features*)
+
+;; stuff for Nikodemus Siivola's HYPERDOC
+;; see <http://common-lisp.net/project/hyperdoc/>
+;; and <http://www.cliki.net/hyperdoc>
+;; also used by LW-ADD-ONS
+
+#-:abcl
+(defvar *hyperdoc-base-uri* "http://weitz.de/cl-fad/")
+
+#-:abcl
+(let ((exported-symbols-alist
+       (loop for symbol being the external-symbols of :cl-fad
+             collect (cons symbol
+                           (concatenate 'string
+                                        "#"
+                                        (string-downcase symbol))))))
+  (defun hyperdoc-lookup (symbol type)
+    (declare (ignore type))
+    (cdr (assoc symbol
+                exported-symbols-alist
+                :test #'eq))))
diff --git a/deps/cl-fad/fad.test.lisp b/deps/cl-fad/fad.test.lisp
new file mode 100644 (file)
index 0000000..8f547e1
--- /dev/null
@@ -0,0 +1,157 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-FAD-TEST; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/cl-fad/test.lisp,v 1.12 2009/09/30 14:23:10 edi Exp $
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package #:cl-fad-test)
+
+(defparameter *tmp-dir*
+              #+(or :win32 :mswindows :windows) "c:\\tmp\\"
+              #-(or :win32 :mswindows :windows) "/tmp/")
+
+(defvar *test-counter* 0)
+
+(defmacro assert* (form)
+  `(progn
+     (format t "Trying to assert ~A~%" ',form)
+     (assert ,form)
+     (format t "Test ~A passed.~%" (incf *test-counter*))))
+
+(defun test ()
+  (setq *test-counter* 0)
+
+  (assert* (path:= (path:catdir) #P""))
+  (assert* (path:= (path:catdir #P"/") #P"/"))
+  (assert* (path:= (path:catdir #P"a/" #P"b/") #P"a/b/"))
+  (assert* (path:= (path:catdir #P"/a/" #P"/b/" #P"c/" #P"./d/" #P"e" #P"f/") #P"/b/c/./d/f/"))
+
+  (assert* (path:= (path:catfile) #P""))
+  (assert* (path:= (path:catfile #P"R.txt") #P"R.txt"))
+  (assert* (path:= (path:catfile #P"a/" #P"/b/" #P"R.txt") #P"/b/R.txt"))
+
+  
+  (let ((fad-dir (merge-pathnames (pathname-as-directory "fad-test")
+                                  *tmp-dir*)))
+    (delete-directory-and-files fad-dir :if-does-not-exist :ignore)
+    (assert* (directory-pathname-p fad-dir))
+    (assert* (directory-pathname-p (pathname *tmp-dir*)))
+    (let ((foo-file (merge-pathnames "foo.lisp"
+                                     fad-dir)))
+      (assert* (not (directory-pathname-p foo-file)))
+      (assert* (not (file-exists-p foo-file)))
+      (assert* (not (file-exists-p fad-dir)))
+      (with-open-file (out (ensure-directories-exist foo-file)
+                           :direction :output
+                           :if-does-not-exist :create)
+        (write-string "NIL" out))
+      (assert* (file-exists-p foo-file))
+      (assert* (not (directory-exists-p foo-file)))
+      (assert* (file-exists-p fad-dir))
+      (assert* (directory-exists-p fad-dir))
+      (assert* (equal fad-dir
+                      (pathname-as-directory fad-dir)))
+      (assert* (equal foo-file
+                      (pathname-as-file foo-file)))
+      (assert* (not (equal fad-dir
+                           (pathname-as-file fad-dir))))
+      (assert* (not (equal foo-file
+                           (pathname-as-directory foo-file))))
+      (dolist (name '("bar" "baz"))
+        (let ((dir (merge-pathnames (pathname-as-directory name)
+                                    fad-dir)))
+          (dolist (name '("foo.text" "bar.lisp"))
+            (let ((file (merge-pathnames name dir)))
+              (with-open-file (out (ensure-directories-exist file)
+                                   :direction :output
+                                   :if-does-not-exist :create)
+                (write-string "NIL" out))))))
+      ;; /tmp/fad-test/foo.lisp
+      ;; /tmp/fad-test/bar/bar.lisp
+      ;; /tmp/fad-test/bar/foo.text
+      ;; /tmp/fad-test/baz/bar.lisp
+      ;; /tmp/fad-test/baz/foo.text
+      ;; files : 5
+      ;; dirs : 3
+      (let ((file-counter 0)
+            (file-and-dir-counter 0)
+            (bar-counter 0))
+        (walk-directory fad-dir
+                        (lambda (file)
+                          (declare (ignore file))
+                          (incf file-counter)))
+        ;; file-counter => 5
+        (walk-directory fad-dir
+                        (lambda (file)
+                          (declare (ignore file))
+                          (incf file-and-dir-counter))
+                        :directories t)
+        ;; file-and-dir-counter => 5 + 3
+        (walk-directory fad-dir
+                        (lambda (file)
+                          (declare (ignore file))
+                          (incf bar-counter))
+                        :test (lambda (file)
+                                (string= (pathname-name file)
+                                         "bar"))
+                        :directories t)
+        ;; do not traverse the baz directory
+        (walk-directory fad-dir
+                        (lambda (file)
+                          (declare (ignore file))
+                          (incf file-and-dir-counter))
+                        :test (lambda (file)
+                                (not (and (directory-pathname-p file)
+                                          (string= (first (last (pathname-directory file)))
+                                                   "baz"))))
+                        :directories :breadth-first)
+        ;; file-and-dir-counter => 5 + 3 + 2 dirs + 3 files
+        (assert* (= 5 file-counter))
+        (assert* (= 13 file-and-dir-counter))
+        (assert* (= 2 bar-counter)))
+      (let ((bar-file (merge-pathnames "bar.lisp" fad-dir)))
+        (copy-file foo-file bar-file)
+        (assert* (file-exists-p bar-file))
+        (with-open-file (foo-stream foo-file :element-type '(unsigned-byte 8))
+          (with-open-file (bar-stream bar-file :element-type '(unsigned-byte 8))
+            (assert* (= (file-length foo-stream)
+                        (file-length bar-stream)))
+            (loop for foo-byte = (read-byte foo-stream nil nil)
+                  for bar-byte = (read-byte bar-stream nil nil)
+                  while (and foo-byte bar-byte)
+                  do (assert* (eql foo-byte bar-byte))))))
+      (let ((baz-dir (merge-pathnames (pathname-as-directory "baz")
+                                      fad-dir))
+            (list (mapcar #'namestring (list-directory fad-dir))))
+        (assert* (find (namestring (truename foo-file)) list :test #'string=))
+        (assert* (find (namestring (truename baz-dir)) list :test #'string=))
+        (assert* (not (find (namestring (pathname-as-file baz-dir))
+                            list
+                            :test #'string=)))))
+    (delete-directory-and-files fad-dir :if-does-not-exist :error)
+    (assert* (not (file-exists-p fad-dir)))
+    (assert* (not (directory-exists-p fad-dir))))
+  (format t "All tests passed.~%"))
diff --git a/deps/cl-fad/load.lisp b/deps/cl-fad/load.lisp
new file mode 100644 (file)
index 0000000..0fd1a15
--- /dev/null
@@ -0,0 +1,62 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/cl-fad/load.lisp,v 1.9 2009/09/30 14:23:10 edi Exp $
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(defparameter *cl-fad-base-directory*
+  (make-pathname :name nil :type nil :version nil
+                 :defaults (parse-namestring *load-truename*)))
+
+#+:allegro (require :osi)
+#+:sbcl (require :sb-executable)
+#+:sbcl (require :sb-posix)
+
+(let ((cl-fad-base-directory
+        (make-pathname :name nil :type nil :version nil
+                       :defaults (parse-namestring *load-truename*))))
+  (let (must-compile)
+    #+:cormanlisp (declare (ignore must-compile))
+    (dolist (file '("packages"
+                   #+:cormanlisp "corman"
+                   #+:openmcl "openmcl"
+                   "fad"))
+      (let ((pathname (make-pathname :name file :type "lisp" :version nil
+                                     :defaults cl-fad-base-directory)))
+        ;; don't use COMPILE-FILE in Corman Lisp, it's broken - LOAD
+        ;; will yield compiled functions anyway
+        #-:cormanlisp
+        (let ((compiled-pathname (compile-file-pathname pathname)))
+          (unless (and (not must-compile)
+                       (probe-file compiled-pathname)
+                       (< (file-write-date pathname)
+                          (file-write-date compiled-pathname)))
+            (setq must-compile t)
+            (compile-file pathname))
+          (setq pathname compiled-pathname))
+        (load pathname)))))
diff --git a/deps/cl-fad/openmcl.lisp b/deps/cl-fad/openmcl.lisp
new file mode 100644 (file)
index 0000000..ae99f77
--- /dev/null
@@ -0,0 +1,72 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CCL; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/cl-fad/openmcl.lisp,v 1.6 2009/09/30 14:23:10 edi Exp $
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-fad)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (flet ((ccl-function-feature (symbol-name feature)
+           (let ((symbol (find-symbol symbol-name :ccl)))
+             (when (and symbol (fboundp symbol))
+               (pushnew feature *features*)))))
+    (ccl-function-feature "%RMDIR" :ccl-has-%rmdir)
+    (ccl-function-feature "DELETE-DIRECTORY" :ccl-has-delete-directory)))
+
+(defpackage :cl-fad-ccl
+  (:use :cl)
+  (:export delete-directory)
+  (:import-from :ccl
+                :%realpath
+                :signal-file-error
+                :native-translated-namestring
+                :with-cstrs)
+  #+ccl-has-%rmdir
+  (:import-from :ccl :%rmdir)
+  #+ccl-has-delete-directory
+  (:import-from :ccl :delete-directory))
+
+(in-package :cl-fad-ccl)
+
+#-ccl-has-%rmdir
+(defun %rmdir (name)
+  (with-cstrs ((n name))
+    (#_rmdir n)))
+
+;;; ClozureCL 1.6 introduced ccl:delete-directory with semantics that
+;;; are acceptably similar to this "legacy" definition.
+;;;
+;;; Except this legacy definition is not recursive, hence this function is
+;;; used only if there is no :CCL-HAS-DELETE-DIRECTORY feature.
+
+#-ccl-has-delete-directory
+(defun delete-directory (path)
+  (let* ((namestring (native-translated-namestring path)))
+    (when (%realpath namestring)
+      (let* ((err (%rmdir namestring)))
+        (or (eql 0 err) (signal-file-error err path))))))
+
diff --git a/deps/cl-fad/packages.lisp b/deps/cl-fad/packages.lisp
new file mode 100644 (file)
index 0000000..1514c99
--- /dev/null
@@ -0,0 +1,87 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/cl-fad/packages.lisp,v 1.12 2009/09/30 14:23:10 edi Exp $
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package #:cl-user)
+
+(defpackage :cl-fad
+  (:nicknames :fad)
+  (:use :cl)
+  #+:allegro
+  (:shadow :copy-file
+           :delete-directory-and-files)
+  #+:abcl
+  (:shadow :list-directory)
+  (:export :copy-file
+           :copy-stream
+           :delete-directory-and-files
+           :directory-exists-p
+           :directory-pathname-p
+           :file-exists-p
+           :list-directory
+           :pathname-as-directory
+           :pathname-as-file
+           :pathname-directory-pathname
+           :pathname-equal
+           :pathname-parent-directory
+           :pathname-absolute-p
+           :pathname-relative-p
+           :pathname-root-p
+           
+           :canonical-pathname
+           :merge-pathnames-as-directory
+           :merge-pathnames-as-file           
+           
+           :walk-directory
+
+           :open-temporary
+           :with-output-to-temporary-file
+           :with-open-temporary-file
+           :*default-template*
+           :invalid-temporary-pathname-template
+           :cannot-create-temporary-file
+           #+win32 #:missing-temp-environment-variable))
+
+(defpackage :path
+  (:use)
+  (:documentation "Rexporting certain functions from the cl-fad package with shorter names.
+
+This package provides no functionality, it serves only to make file
+system intensive code easier to read (for unix people at least).")
+  (:export #:dirname
+           #:basename
+           #:-e
+           #:-d
+           #:catfile
+           #:catdir
+           #:rm-r
+           #:=
+
+           #:absolute-p
+           #:relative-p
+           #:root-p))
diff --git a/deps/cl-fad/packages.test.lisp b/deps/cl-fad/packages.test.lisp
new file mode 100644 (file)
index 0000000..90736df
--- /dev/null
@@ -0,0 +1,5 @@
+(in-package :common-lisp-user)
+
+(defpackage :cl-fad-test
+  (:use :cl :cl-fad :unit-test)
+  (:export :test))
diff --git a/deps/cl-fad/path.lisp b/deps/cl-fad/path.lisp
new file mode 100644 (file)
index 0000000..c94a0c2
--- /dev/null
@@ -0,0 +1,32 @@
+(in-package :cl-fad)
+
+(defmacro defalias (name args realname)
+  `(progn
+     (defun ,name ,args
+       ,(if (eql '&rest (first args))
+            `(apply #',realname ,(second args))
+            `(,realname ,@args)))
+     (define-compiler-macro ,name (&rest args)
+       (list* ',realname args))))
+
+(defalias path:dirname (pathname) cl-fad:pathname-directory-pathname)
+
+(defun path:basename (pathname) (pathname (file-namestring pathname)))
+
+(defalias path:-e (pathname) cl-fad:file-exists-p)
+
+(defalias path:-d (directory) cl-fad:directory-exists-p)
+
+(defalias path:catfile (&rest pathnames) cl-fad:merge-pathnames-as-file)
+
+(defalias path:catdir (&rest pathnames) cl-fad:merge-pathnames-as-directory)
+
+(defalias path:= (a b) cl-fad:pathname-equal)
+
+(defalias path:absolute-p (pathname) cl-fad:pathname-absolute-p)
+
+(defalias path:relative-p (pathname) cl-fad:pathname-relative-p)
+
+(defalias path:root-p (pathname) cl-fad:pathname-root-p)
+
+(defalias path:rm-r (pathname) cl-fad:delete-directory-and-files)
diff --git a/deps/cl-fad/temporary-files.lisp b/deps/cl-fad/temporary-files.lisp
new file mode 100644 (file)
index 0000000..0dc20da
--- /dev/null
@@ -0,0 +1,174 @@
+(in-package :cl-fad)
+
+(defparameter *default-template* "TEMPORARY-FILES:TEMP-%")
+
+(defparameter *max-tries* 10000)
+
+(defvar *name-random-state* (make-random-state t))
+
+;; from XCVB
+(eval-when (:load-toplevel :execute)
+  (defun getenv (x)
+    "Query the libc runtime environment. See getenv(3)."
+    (declare (ignorable x))
+    #+(or abcl clisp xcl) (ext:getenv x)
+    #+allegro (sys:getenv x)
+    #+clozure (ccl:getenv x)
+    #+(or cmu scl) (cdr (assoc x ext:*environment-list* :test #'string=))
+    #+cormanlisp
+    (let* ((buffer (ct:malloc 1))
+           (cname (ct:lisp-string-to-c-string x))
+           (needed-size (win:getenvironmentvariable cname buffer 0))
+           (buffer1 (ct:malloc (1+ needed-size))))
+      (prog1 (if (zerop (win:getenvironmentvariable cname buffer1 needed-size))
+                 nil
+                 (ct:c-string-to-lisp-string buffer1))
+        (ct:free buffer)
+        (ct:free buffer1)))
+    #+ecl (si:getenv x)
+    #+gcl (system:getenv x)
+    #+lispworks (lispworks:environment-variable x)
+    #+mcl (ccl:with-cstrs ((name x))
+            (let ((value (_getenv name)))
+              (unless (ccl:%null-ptr-p value)
+                (ccl:%get-cstring value))))
+    #+sbcl (sb-ext:posix-getenv x)
+    #-(or abcl allegro clisp clozure cmu cormanlisp ecl gcl lispworks mcl sbcl scl xcl)
+    (error "~S is not supported on your implementation" 'getenv))
+
+  (defun directory-from-environment (environment-variable-name)
+    (let ((string (getenv environment-variable-name)))
+      (when (plusp (length string))
+        (pathname-as-directory string))))
+
+  #+win32
+  (define-condition missing-temp-environment-variable (error)
+    ()
+    (:report (lambda (condition stream)
+               (declare (ignore condition))
+               (format stream "the TEMP environment variable has not been found, cannot continue"))))
+
+  #+win32
+  (defun get-default-temporary-directory ()
+    (or (directory-from-environment "TEMP")
+        (error 'missing-temp-environment-variable)))
+
+  #-win32
+  (defun get-default-temporary-directory ()
+    (or (directory-from-environment "TMPDIR")
+        #-clisp
+        (probe-file #P"/tmp/")
+        #+clisp
+        (and (ext:probe-directory #P"/tmp/")
+             #P"/tmp/")))
+
+  (handler-case
+      (logical-pathname-translations "TEMPORARY-FILES")
+    (error ()
+      (alexandria:if-let (default-temporary-directory (get-default-temporary-directory))
+        (setf (logical-pathname-translations "TEMPORARY-FILES") `(("*.*.*" ,default-temporary-directory)))
+        (warn "could not automatically determine a default mapping for TEMPORARY-FILES")))))
+
+;; locking for multi-threaded operation with unsafe random function
+
+(defvar *create-file-name-lock* (bordeaux-threads:make-lock "Temporary File Name Creation Lock"))
+
+(defmacro with-file-name-lock-held (() &body body)
+  `(bordeaux-threads:with-lock-held (*create-file-name-lock*)
+     ,@body))
+
+(defun generate-random-string ()
+  (with-file-name-lock-held ()
+    (format nil "~:@(~36,8,'0R~)" (random (expt 36 8) *name-random-state*))))
+
+(define-condition invalid-temporary-pathname-template (error)
+  ((string :initarg :string))
+  (:report (lambda (condition stream)
+             (with-slots (string) condition
+               (format stream "invalid temporary file name template ~S, must contain a percent sign that is to be replaced by a random string" string)))))
+
+(defun generate-random-pathname (template random-string-generator)
+  (let ((percent-position (or (position #\% template)
+                              (error 'invalid-temporary-pathname-template :string template))))
+    (merge-pathnames (concatenate 'string
+                                  (subseq template 0 percent-position)
+                                  (funcall random-string-generator)
+                                  (subseq template (1+ percent-position))))))
+
+(define-condition cannot-create-temporary-file (error)
+  ((template :initarg :template)
+   (max-tries :initarg :max-tries))
+  (:report (lambda (condition stream)
+             (with-slots (template max-tries) condition
+               (format stream "cannot create temporary file with template ~A, giving up after ~D attempt~:P"
+                       template max-tries)))))
+
+(defun open-temporary (&rest open-arguments
+                      &key
+                         (template *default-template*)
+                        (generate-random-string 'generate-random-string)
+                         (max-tries *max-tries*)
+                         (direction :output)
+                        &allow-other-keys)
+  "Create a file with a randomly generated name and return the opened
+   stream.  The resulting pathname is generated from TEMPLATE, which
+   is a string representing a pathname template.  A percent sign (%)
+   in that string is replaced by a randomly generated string to make
+   the filename unique.  The default for TEMPLATE places temporary
+   files in the TEMPORARY-FILES logical pathname host, which is
+   automatically set up in a system specific manner.  The file name
+   generated from TEMPLATE is merged with *DEFAULT-PATHNAME-DEFAULTS*,
+   so random pathnames relative to that directory can be generated by
+   not specifying a directory in TEMPLATE.
+
+   GENERATE-RANDOM-STRING can be passed to override the default
+   function that generates the random name component.  It should
+   return a random string consisting of characters that are permitted
+   in a pathname (logical or physical, depending on TEMPLATE).
+
+   The name of the temporary file can be accessed calling the PATHNAME
+   function on STREAM.  For convenience, the temporary file is opened
+   on the physical pathname, i.e. if the TEMPLATE designate a logical
+   pathname the translation to a physical pathname is performed before
+   opening the stream.
+
+   In order to create a unique file name, OPEN-TEMPORARY may loop
+   internally up to MAX-TRIES times before giving up and signalling a
+   CANNOT-CREATE-TEMPORARY-FILE condition."
+  (loop thereis (apply #'open
+                       (translate-logical-pathname (generate-random-pathname template generate-random-string))
+                       :direction direction
+                       :if-exists nil
+                       (alexandria:remove-from-plist open-arguments :template :generate-random-string :max-tries))
+        repeat max-tries
+        finally (error 'cannot-create-temporary-file
+                       :template template
+                       :max-tries max-tries)))
+
+(defmacro with-output-to-temporary-file ((stream &rest args) &body body)
+  "Create a temporary file using OPEN-TEMPORARY with ARGS and run BODY
+  with STREAM bound to the temporary file stream.  Returns the
+  pathname of the file that has been created.  See OPEN-TEMPORARY for
+  permitted options."
+  `(with-open-stream (,stream (open-temporary ,@args))
+     ,@body
+     (pathname ,stream)))
+
+(defmacro with-open-temporary-file ((stream &rest args &key keep &allow-other-keys) &body body)
+  "Create a temporary file using OPEN-TEMPORARY with ARGS and run BODY
+  with STREAM bound to the temporary file stream.  Returns the values
+  returned by BODY.  By default, the file is deleted when BODY is
+  exited. If a true value is passed in KEEP, the file is not deleted
+  when the body is exited.  See OPEN-TEMPORARY for more permitted
+  options."
+  `(with-open-stream (,stream (open-temporary ,@(alexandria:remove-from-plist args :keep)))
+     #+sbcl
+     (declare (sb-ext:muffle-conditions sb-ext:code-deletion-note))
+     ,(if (and (constantp keep)
+               keep)
+          `(progn ,@body)
+          `(unwind-protect
+                (progn ,@body)
+             (unless ,keep
+               (close ,stream)
+               (delete-file (pathname ,stream)))))))
diff --git a/deps/cl-fad/temporary-files.test.lisp b/deps/cl-fad/temporary-files.test.lisp
new file mode 100644 (file)
index 0000000..80d4403
--- /dev/null
@@ -0,0 +1,49 @@
+(in-package :cl-fad-test)
+
+(deftest 'temporary-file 'with-output-to-temporary-file ()
+  (let ((pathname (with-output-to-temporary-file (f)
+                    (write-string "hello" f))))
+    (test-assert (probe-file pathname))
+    (test-equal (alexandria:read-file-into-string pathname) "hello")
+    (delete-file pathname)))
+
+(deftest 'temporary-file 'with-open-temporary-file-keep ()
+
+  (let ((pathname (with-open-temporary-file (f :keep nil)
+                    (pathname f))))
+    (test-assert (null (probe-file pathname))))
+  (let ((pathname (with-open-temporary-file (f :keep t)
+                    (pathname f))))
+    (test-assert (probe-file pathname))
+    (delete-file pathname))
+
+  (let* ((keep nil)
+         (pathname (with-open-temporary-file (f :keep keep)
+                     (pathname f))))
+    (test-assert (null (probe-file pathname))))
+  (let* ((keep t)
+         (pathname (with-open-temporary-file (f :keep keep)
+                     (pathname f))))
+    (test-assert (probe-file pathname))
+    (delete-file pathname)))
+
+(deftest 'temporary-file 'template-tests ()
+  ;; error is signalled when template does not contain a percent sign.
+  (let ((*default-template* "foo"))
+    (test-condition (with-open-temporary-file (f :keep nil))
+                    'invalid-temporary-pathname-template))
+  ;; file name template occurs in generated file name (for logical path name)
+  (let* ((*default-template* "temporary-files:bla%.txt")
+         (pathname (with-open-temporary-file (f :keep nil)
+                     (pathname f))))
+    (test-assert (cl-ppcre:scan "(?i)bla.*\\.txt$" (namestring pathname))))
+  ;; file name template occurs in generated file name (for pysical path name)
+  (let* ((*default-template* (concatenate 'string
+                                          (namestring (translate-logical-pathname "temporary-files:"))
+                                          "bla%.txt"))
+         (pathname (with-open-temporary-file (f :keep nil)
+                     (pathname f))))
+    (test-assert (cl-ppcre:scan "(?i)bla.*\\.txt$" (namestring pathname)))))
+
+                                                 
+    
diff --git a/deps/flexi-streams/CHANGELOG b/deps/flexi-streams/CHANGELOG
new file mode 100644 (file)
index 0000000..75a326b
--- /dev/null
@@ -0,0 +1,283 @@
+Version 1.0.15
+2015-07-01
+Support strings as external-format name specifiers (LispAlien)
+
+Version 1.0.14
+2014-11-28
+update support information (Hans Huebner)
+
+Version 1.0.13
+2014-05-18
+fix version number (Hans Huebner)
+
+Version 1.0.12
+2013-12-30
+Update :description
+
+Version 1.0.11
+2013-12-30
+Don't reset column to NIL on internal write operations (Anton Vodonosov)
+
+Version 1.0.10
+2013-12-09
+Fix file-position errors (markv)
+
+Version 1.0.9
+2013-11-21
+Dummy release without any functional changes
+
+Version 1.0.8
+Make write-sequence call transform-octet (Jason Miller)
+Fix for CMUCL (Raymond Toy, Xu Jingtao)
+
+Version 1.0.7
+2008-08-26
+Don't read a second time if the first READ-SEQUENCE already reached EOF (Drakma bug report by Stas Boukarev)
+
+Version 1.0.6
+2008-08-25
+Don't use a reserve if we can't rewind the stream (Drakma bug report by Stas Boukarev)
+
+Version 1.0.5
+2008-08-01
+Export RUN-ALL-TESTS instead of RUN-TESTS (caught by Nick Allen)
+
+Version 1.0.4
+2008-07-25
+Cosmetic surgery on test suite
+
+Version 1.0.3
+2008-05-30
+Better checks for invalid UTF-8 data
+New restart ACCEPT-OVERLONG-SEQUENCE
+More tests
+Unused variable in CHECK-END
+
+Version 1.0.2
+2008-05-26
+Removed unnecessary test
+
+Version 1.0.1
+2008-05-26
+Removed two faulty declarations
+
+Version 1.0.0
+2008-05-26
+More redesign for the sake of performance
+More checks for invalid data
+More tests
+Exported functions for length computation
+
+Version 0.15.3
+2008-05-23
+Avoid CHANGE-CLASS on LispWorks if possible
+
+Version 0.15.2
+2008-05-22
+Remove debugging remnants (d'ooh!)
+
+Version 0.15.1
+2008-05-21
+Direct access to underlying stream in case of binary sequence operations
+More tests     
+
+Version 0.15.0
+2008-05-21
+Complete redesign, various additions, bugfixes, performance improvements (with the help of Hans Hübner)
+
+Version 0.14.0
+2007-12-30
+Some fixes for LispWorks (when the underlying stream is a character stream)
+Optimized methods for UNREAD-CHAR% in case of 8-bit encodings
+More tests
+       
+Version 0.13.1
+2007-10-11
+Small fix for AllegroCL's "modern" mode
+       
+Version 0.13.0
+2007-09-13
+Better optimizations for STREAM-WRITE-SEQUENCE (thanks to Anton Vodonosov)
+Bugfix for STREAM-WRITE-BYTE   
+
+Version 0.12.0
+2007-09-07
+Added "bound" for flexi input streams
+
+Version 0.11.2
+2007-04-06
+Fixed bug in STREAM-WRITE-STRING implementation (reported by quasi)
+
+Version 0.11.1
+2007-03-22
+More ugliness for a bit of output performance in special cases
+
+Version 0.11.0
+2007-03-09
+Re-factoring of how encoding errors are handled (patch by Anton Vodonosov)
+
+Version 0.10.3
+2007-02-19
+Fixed bug in UTF-16 output (patch by Stelian Ionescu)
+Fixed *SUBSTITUTION-CHAR* example in docs
+
+Version 0.10.2
+2007-01-12
+Another fix - sigh...
+
+Version 0.10.1
+2007-01-11
+Fixed the last change (thanks to Red Daly)     
+
+Version 0.10.0
+2007-01-10
+Added transformers to in-memory streams (thanks to Chris Dean) 
+Documentation fixes
+
+Version 0.9.1
+2006-12-27
+More performance improvements (thanks to Robert J. Macomber for SBCL hints)    
+
+Version 0.9.0
+2006-12-27
+Complete re-factoring to improve performance and reduce consing (at least for LispWorks)
+Added some tests       
+Added *PROVIDE-USE-VALUE-RESTART*      
+Added FLEXI-STREAM-POSITION-SPEC-ERROR condition
+       
+Version 0.8.0
+2006-11-14
+Added USE-VALUE restart for STREAM-READ-CHAR (thanks to Anton Vodonosov)
+Added *SUBSTITUTION-CHAR*      
+
+Version 0.7.2
+2006-11-06
+Removed unnecessary CHECK-EOF-NO-HANG also for in-memory streams (see 0.5.8)
+
+Version 0.7.1
+2006-10-31
+Argh, missed the most important part...
+
+Version 0.7.0
+2006-10-31
+Added KOI8-R (thanks to Igor Plekhov)
+
+Version 0.6.6
+2006-10-06
+Made sure not to apply Gray stream generic function to underlying stream
+
+Version 0.6.5
+2006-10-06
+Optimized STREAM-WRITE-SEQUENCE and STREAM-READ-SEQUENCE for arrays of octets
+       
+Version 0.6.4
+2006-10-05
+Made READ-BYTE/WRITE-BYTE the default behaviour, i.e. we only use the sequence functions for LW if necessary   
+
+Version 0.6.3
+2006-10-02
+Fixed problems with CMUCL Gray streams implementation (reported by Ivan Toshkov)
+
+Version 0.6.2
+2006-09-23
+Added method for MAKE-LOAD-FORM which is needed for OpenMCL (reported by Robert Synnott, see Drakma mailing list)
+
+Version 0.6.1
+2006-09-15
+Switched FILE-POSITION implementation to TRIVIAL-GRAY-STREAMS (thanks to David Lichteblau)
+
+Version 0.6.0
+2006-09-13
+Implemented file positions for LispWorks
+
+Version 0.5.10
+2006-09-04
+Flexi streams can have binary element types now
+
+Version 0.5.9
+2006-09-01
+Added string functions
+
+Version 0.5.8
+2006-09-01
+CHECK-EOF-NO-HANG is not necessary
+Updated LW links in documentation
+Changed package handling in system definition (thanks to Christophe Rhodes)
+       
+Version 0.5.7
+2006-06-29
+Removed incompatibility with AllegroCL, see mailing list archive for details
+
+Version 0.5.6
+2006-06-13
+Fixed Emacs mode lines (reported by Robert Goldman)
+
+Version 0.5.5
+2006-05-24
+Some small fixes for LW
+
+Version 0.5.4
+2006-05-18
+Workaround for CMUCL (thanks to Satyaki Das)
+
+Version 0.5.3
+2006-03-06
+Fixed more typos in stream.lisp
+Added missing exports in packages.lisp
+
+Version 0.5.2
+2006-01-26
+Fixed typos in stream.lisp (thanks to James Bielman)
+
+Version 0.5.1
+2005-12-14
+Some bugfixes in output.lisp (thanks to Jan Idzikowski)
+
+Version 0.5.0
+2005-12-11
+Added in-memory streams        
+Exported types
+Added specific conditions      
+       
+Version 0.4.1
+2005-12-05
+Updated docs   
+       
+Version 0.4.0
+2005-12-05
+Added US-ASCII encoding
+Added *USE-REPLACEMENT-CHAR*
+       
+Version 0.3.0
+2005-11-26
+Added UNREAD-BYTE and PEEK-BYTE
+       
+Version 0.2.4
+2005-11-26
+WIN32:CODE-PAGE only for LispWorks
+
+Version 0.2.3
+2005-11-26
+Added STREAM-TERPRI to appease AllegroCL
+Fixed typo in docs
+
+Version 0.2.2
+2005-11-26
+Patch to make class precendence list work in AllegroCL (David Lichteblau) 
+
+Version 0.2.1
+2005-11-25
+Adapted to new TRIVIAL-GRAY-STREAMS API (David Lichteblau)
+More changes for portability, specifically for SBCL (David Lichteblau)
+
+Version 0.2.0
+2005-11-25
+Portable version thanks to TRIVIAL-GRAY-STREAMS (David Lichteblau)
+
+Version 0.1.1
+2005-11-25
+Documentation enhancements
+
+Version 0.1.0
+2005-11-25
+Initial public release
diff --git a/deps/flexi-streams/ascii.lisp b/deps/flexi-streams/ascii.lisp
new file mode 100644 (file)
index 0000000..f333094
--- /dev/null
@@ -0,0 +1,36 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-\r
+;;; $Header: /usr/local/cvsrep/flexi-streams/ascii.lisp,v 1.9 2008/05/18 21:32:15 edi Exp $\r
+\r
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.\r
+\r
+;;; Redistribution and use in source and binary forms, with or without\r
+;;; modification, are permitted provided that the following conditions\r
+;;; are met:\r
+\r
+;;;   * Redistributions of source code must retain the above copyright\r
+;;;     notice, this list of conditions and the following disclaimer.\r
+\r
+;;;   * Redistributions in binary form must reproduce the above\r
+;;;     copyright notice, this list of conditions and the following\r
+;;;     disclaimer in the documentation and/or other materials\r
+;;;     provided with the distribution.\r
+\r
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED\r
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\r
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\r
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+(in-package :flexi-streams)\r
+\r
+(defconstant +ascii-table+\r
+  ;; currently not used, but we leave it in here just in case...\r
+  (make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533))\r
+  "An array enumerating the character codes for the US-ASCII\r
+encoding.")\r
diff --git a/deps/flexi-streams/code-pages.lisp b/deps/flexi-streams/code-pages.lisp
new file mode 100644 (file)
index 0000000..a0d7427
--- /dev/null
@@ -0,0 +1,62 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-\r
+;;; $Header: /usr/local/cvsrep/flexi-streams/code-pages.lisp,v 1.7 2008/05/18 21:32:15 edi Exp $\r
+\r
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.\r
+\r
+;;; Redistribution and use in source and binary forms, with or without\r
+;;; modification, are permitted provided that the following conditions\r
+;;; are met:\r
+\r
+;;;   * Redistributions of source code must retain the above copyright\r
+;;;     notice, this list of conditions and the following disclaimer.\r
+\r
+;;;   * Redistributions in binary form must reproduce the above\r
+;;;     copyright notice, this list of conditions and the following\r
+;;;     disclaimer in the documentation and/or other materials\r
+;;;     provided with the distribution.\r
+\r
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED\r
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\r
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\r
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+(in-package :flexi-streams)\r
+\r
+;;; the following code was auto-generated with LWW\r
+\r
+(defconstant +code-page-tables+\r
+  `((437 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 199 252 233 226 228 224 229 231 234 235 232 239 238 236 196 197 201 230 198 244 246 242 251 249 255 214 220 162 163 165 8359 402 225 237 243 250 241 209 170 186 191 8976 172 189 188 161 171 187 9617 9618 9619 9474 9508 9569 9570 9558 9557 9571 9553 9559 9565 9564 9563 9488 9492 9524 9516 9500 9472 9532 9566 9567 9562 9556 9577 9574 9568 9552 9580 9575 9576 9572 9573 9561 9560 9554 9555 9579 9578 9496 9484 9608 9604 9612 9616 9600 945 223 915 960 931 963 181 964 934 920 937 948 8734 966 949 8745 8801 177 8805 8804 8992 8993 247 8776 176 8729 183 8730 8319 178 9632 160))) \r
+    (720 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 233 226 132 224 134 231 234 235 232 239 238 141 142 143 144 1617 1618 244 164 1600 251 249 1569 1570 1571 1572 163 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 171 187 9617 9618 9619 9474 9508 9569 9570 9558 9557 9571 9553 9559 9565 9564 9563 9488 9492 9524 9516 9500 9472 9532 9566 9567 9562 9556 9577 9574 9568 9552 9580 9575 9576 9572 9573 9561 9560 9554 9555 9579 9578 9496 9484 9608 9604 9612 9616 9600 1590 1591 1592 1593 1594 1601 181 1602 1603 1604 1605 1606 1607 1608 1609 1610 8801 1611 1612 1613 1614 1615 1616 8776 176 8729 183 8730 8319 178 9632 160))) \r
+    (737 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 931 932 933 934 935 936 937 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 963 962 964 965 966 967 968 9617 9618 9619 9474 9508 9569 9570 9558 9557 9571 9553 9559 9565 9564 9563 9488 9492 9524 9516 9500 9472 9532 9566 9567 9562 9556 9577 9574 9568 9552 9580 9575 9576 9572 9573 9561 9560 9554 9555 9579 9578 9496 9484 9608 9604 9612 9616 9600 969 940 941 942 970 943 972 973 971 974 902 904 905 906 908 910 911 177 8805 8804 938 939 247 8776 176 8729 183 8730 8319 178 9632 160))) \r
+    (775 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 262 252 233 257 228 291 229 263 322 275 342 343 299 377 196 197 201 230 198 333 246 290 162 346 347 214 220 248 163 216 215 164 256 298 243 379 380 378 8221 166 169 174 172 189 188 321 171 187 9617 9618 9619 9474 9508 260 268 280 278 9571 9553 9559 9565 302 352 9488 9492 9524 9516 9500 9472 9532 370 362 9562 9556 9577 9574 9568 9552 9580 381 261 269 281 279 303 353 371 363 382 9496 9484 9608 9604 9612 9616 9600 211 223 332 323 245 213 181 324 310 311 315 316 326 274 325 8217 173 177 8220 190 182 167 247 8222 176 8729 183 185 179 178 9632 160))) \r
+    (850 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 199 252 233 226 228 224 229 231 234 235 232 239 238 236 196 197 201 230 198 244 246 242 251 249 255 214 220 248 163 216 215 402 225 237 243 250 241 209 170 186 191 174 172 189 188 161 171 187 9617 9618 9619 9474 9508 193 194 192 169 9571 9553 9559 9565 162 165 9488 9492 9524 9516 9500 9472 9532 227 195 9562 9556 9577 9574 9568 9552 9580 164 240 208 202 203 200 305 205 206 207 9496 9484 9608 9604 166 204 9600 211 223 212 210 245 213 181 254 222 218 219 217 253 221 175 180 173 177 8215 190 182 167 247 184 176 168 183 185 179 178 9632 160))) \r
+    (852 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 199 252 233 226 228 367 263 231 322 235 336 337 238 377 196 262 201 313 314 244 246 317 318 346 347 214 220 356 357 321 215 269 225 237 243 250 260 261 381 382 280 281 172 378 268 351 171 187 9617 9618 9619 9474 9508 193 194 282 350 9571 9553 9559 9565 379 380 9488 9492 9524 9516 9500 9472 9532 258 259 9562 9556 9577 9574 9568 9552 9580 164 273 272 270 203 271 327 205 206 283 9496 9484 9608 9604 354 366 9600 211 223 212 323 324 328 352 353 340 218 341 368 253 221 355 180 173 733 731 711 728 167 247 184 176 168 729 369 344 345 9632 160))) \r
+    (855 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 1106 1026 1107 1027 1105 1025 1108 1028 1109 1029 1110 1030 1111 1031 1112 1032 1113 1033 1114 1034 1115 1035 1116 1036 1118 1038 1119 1039 1102 1070 1098 1066 1072 1040 1073 1041 1094 1062 1076 1044 1077 1045 1092 1060 1075 1043 171 187 9617 9618 9619 9474 9508 1093 1061 1080 1048 9571 9553 9559 9565 1081 1049 9488 9492 9524 9516 9500 9472 9532 1082 1050 9562 9556 9577 9574 9568 9552 9580 164 1083 1051 1084 1052 1085 1053 1086 1054 1087 9496 9484 9608 9604 1055 1103 9600 1071 1088 1056 1089 1057 1090 1058 1091 1059 1078 1046 1074 1042 1100 1068 8470 173 1099 1067 1079 1047 1096 1064 1101 1069 1097 1065 1095 1063 167 9632 160))) \r
+    (857 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 199 252 233 226 228 224 229 231 234 235 232 239 238 305 196 197 201 230 198 244 246 242 251 249 304 214 220 248 163 216 350 351 225 237 243 250 241 209 286 287 191 174 172 189 188 161 171 187 9617 9618 9619 9474 9508 193 194 192 169 9571 9553 9559 9565 162 165 9488 9492 9524 9516 9500 9472 9532 227 195 9562 9556 9577 9574 9568 9552 9580 164 186 170 202 203 200 65533 205 206 207 9496 9484 9608 9604 166 204 9600 211 223 212 210 245 213 181 65533 215 218 219 217 236 255 175 180 173 177 65533 190 182 167 247 184 176 168 183 185 179 178 9632 160))) \r
+    (860 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 199 252 233 226 227 224 193 231 234 202 232 205 212 236 195 194 201 192 200 244 245 242 218 249 204 213 220 162 163 217 8359 211 225 237 243 250 241 209 170 186 191 210 172 189 188 161 171 187 9617 9618 9619 9474 9508 9569 9570 9558 9557 9571 9553 9559 9565 9564 9563 9488 9492 9524 9516 9500 9472 9532 9566 9567 9562 9556 9577 9574 9568 9552 9580 9575 9576 9572 9573 9561 9560 9554 9555 9579 9578 9496 9484 9608 9604 9612 9616 9600 945 223 915 960 931 963 181 964 934 920 937 948 8734 966 949 8745 8801 177 8805 8804 8992 8993 247 8776 176 8729 183 8730 8319 178 9632 160))) \r
+    (861 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 199 252 233 226 228 224 229 231 234 235 232 208 240 222 196 197 201 230 198 244 246 254 251 221 253 214 220 248 163 216 8359 402 225 237 243 250 193 205 211 218 191 8976 172 189 188 161 171 187 9617 9618 9619 9474 9508 9569 9570 9558 9557 9571 9553 9559 9565 9564 9563 9488 9492 9524 9516 9500 9472 9532 9566 9567 9562 9556 9577 9574 9568 9552 9580 9575 9576 9572 9573 9561 9560 9554 9555 9579 9578 9496 9484 9608 9604 9612 9616 9600 945 223 915 960 931 963 181 964 934 920 937 948 8734 966 949 8745 8801 177 8805 8804 8992 8993 247 8776 176 8729 183 8730 8319 178 9632 160))) \r
+    (862 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 162 163 165 8359 402 225 237 243 250 241 209 170 186 191 8976 172 189 188 161 171 187 9617 9618 9619 9474 9508 9569 9570 9558 9557 9571 9553 9559 9565 9564 9563 9488 9492 9524 9516 9500 9472 9532 9566 9567 9562 9556 9577 9574 9568 9552 9580 9575 9576 9572 9573 9561 9560 9554 9555 9579 9578 9496 9484 9608 9604 9612 9616 9600 945 223 915 960 931 963 181 964 934 920 937 948 8734 966 949 8745 8801 177 8805 8804 8992 8993 247 8776 176 8729 183 8730 8319 178 9632 160))) \r
+    (863 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 199 252 233 226 194 224 182 231 234 235 232 239 238 8215 192 167 201 200 202 244 203 207 251 249 164 212 220 162 163 217 219 402 166 180 243 250 168 184 179 175 206 8976 172 189 188 190 171 187 9617 9618 9619 9474 9508 9569 9570 9558 9557 9571 9553 9559 9565 9564 9563 9488 9492 9524 9516 9500 9472 9532 9566 9567 9562 9556 9577 9574 9568 9552 9580 9575 9576 9572 9573 9561 9560 9554 9555 9579 9578 9496 9484 9608 9604 9612 9616 9600 945 223 915 960 931 963 181 964 934 920 937 948 8734 966 949 8745 8801 177 8805 8804 8992 8993 247 8776 176 8729 183 8730 8319 178 9632 160))) \r
+    (864 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 176 183 8729 8730 9618 9472 9474 9532 9508 9516 9500 9524 9488 9484 9492 9496 946 8734 966 177 189 188 8776 171 187 65271 65272 155 156 65275 65276 159 160 173 65154 163 164 65156 65533 65533 65166 65167 65173 65177 1548 65181 65185 65189 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 65233 1563 65201 65205 65209 1567 162 65152 65153 65155 65157 65226 65163 65165 65169 65171 65175 65179 65183 65187 65191 65193 65195 65197 65199 65203 65207 65211 65215 65217 65221 65227 65231 166 172 247 215 65225 1600 65235 65239 65243 65247 65251 65255 65259 65261 65263 65267 65213 65228 65230 65229 65249 65149 1617 65253 65257 65260 65264 65266 65232 65237 65269 65270 65245 65241 65265 9632 65533))) \r
+    (865 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 199 252 233 226 228 224 229 231 234 235 232 239 238 236 196 197 201 230 198 244 246 242 251 249 255 214 220 248 163 216 8359 402 225 237 243 250 241 209 170 186 191 8976 172 189 188 161 171 164 9617 9618 9619 9474 9508 9569 9570 9558 9557 9571 9553 9559 9565 9564 9563 9488 9492 9524 9516 9500 9472 9532 9566 9567 9562 9556 9577 9574 9568 9552 9580 9575 9576 9572 9573 9561 9560 9554 9555 9579 9578 9496 9484 9608 9604 9612 9616 9600 945 223 915 960 931 963 181 964 934 920 937 948 8734 966 949 8745 8801 177 8805 8804 8992 8993 247 8776 176 8729 183 8730 8319 178 9632 160))) \r
+    (866 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 9617 9618 9619 9474 9508 9569 9570 9558 9557 9571 9553 9559 9565 9564 9563 9488 9492 9524 9516 9500 9472 9532 9566 9567 9562 9556 9577 9574 9568 9552 9580 9575 9576 9572 9573 9561 9560 9554 9555 9579 9578 9496 9484 9608 9604 9612 9616 9600 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1025 1105 1028 1108 1031 1111 1038 1118 176 8729 183 8730 8470 164 9632 160))) \r
+    (869 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 902 135 183 172 166 8216 8217 904 8213 905 906 938 908 147 148 910 939 169 911 178 179 940 163 941 942 943 970 912 972 973 913 914 915 916 917 918 919 189 920 921 171 187 9617 9618 9619 9474 9508 922 923 924 925 9571 9553 9559 9565 926 927 9488 9492 9524 9516 9500 9472 9532 928 929 9562 9556 9577 9574 9568 9552 9580 931 932 933 934 935 936 937 945 946 947 9496 9484 9608 9604 948 949 9600 950 951 952 953 954 955 956 957 958 959 960 961 963 962 964 900 173 177 965 966 967 167 968 901 176 168 969 971 944 974 9632 160))) \r
+    (1250 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 8364 129 8218 131 8222 8230 8224 8225 136 8240 352 8249 346 356 381 377 144 8216 8217 8220 8221 8226 8211 8212 152 8482 353 8250 347 357 382 378 160 711 728 321 164 260 166 167 168 169 350 171 172 173 174 379 176 177 731 322 180 181 182 183 184 261 351 187 317 733 318 380 340 193 194 258 196 313 262 199 268 201 280 203 282 205 206 270 272 323 327 211 212 336 214 215 344 366 218 368 220 221 354 223 341 225 226 259 228 314 263 231 269 233 281 235 283 237 238 271 273 324 328 243 244 337 246 247 345 367 250 369 252 253 355 729))) \r
+    (1251 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 1026 1027 8218 1107 8222 8230 8224 8225 8364 8240 1033 8249 1034 1036 1035 1039 1106 8216 8217 8220 8221 8226 8211 8212 152 8482 1113 8250 1114 1116 1115 1119 160 1038 1118 1032 164 1168 166 167 1025 169 1028 171 172 173 174 1031 176 177 1030 1110 1169 181 182 183 1105 8470 1108 187 1112 1029 1109 1111 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103))) \r
+    (1252 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 8364 129 8218 402 8222 8230 8224 8225 710 8240 352 8249 338 141 381 143 144 8216 8217 8220 8221 8226 8211 8212 732 8482 353 8250 339 157 382 376 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255))) \r
+    (1253 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 8364 129 8218 402 8222 8230 8224 8225 136 8240 138 8249 140 141 142 143 144 8216 8217 8220 8221 8226 8211 8212 152 8482 154 8250 156 157 158 159 160 901 902 163 164 165 166 167 168 169 65533 171 172 173 174 8213 176 177 178 179 900 181 182 183 904 905 906 187 908 189 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 65533 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 65533))) \r
+    (1254 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 8364 129 8218 402 8222 8230 8224 8225 710 8240 352 8249 338 141 142 143 144 8216 8217 8220 8221 8226 8211 8212 732 8482 353 8250 339 157 158 376 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 286 209 210 211 212 213 214 215 216 217 218 219 220 304 350 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 287 241 242 243 244 245 246 247 248 249 250 251 252 305 351 255))) \r
+    (1255 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 8364 129 8218 402 8222 8230 8224 8225 710 8240 138 8249 140 141 142 143 144 8216 8217 8220 8221 8226 8211 8212 732 8482 154 8250 156 157 158 159 160 161 162 163 8362 165 166 167 168 169 215 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 247 187 188 189 190 191 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1520 1521 1522 1523 1524 65533 65533 65533 65533 65533 65533 65533 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 65533 65533 8206 8207 65533))) \r
+    (1256 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 8364 1662 8218 402 8222 8230 8224 8225 710 8240 1657 8249 338 1670 1688 1672 1711 8216 8217 8220 8221 8226 8211 8212 1705 8482 1681 8250 339 8204 8205 1722 160 1548 162 163 164 165 166 167 168 169 1726 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 1563 187 188 189 190 1567 1729 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 215 1591 1592 1593 1594 1600 1601 1602 1603 224 1604 226 1605 1606 1607 1608 231 232 233 234 235 1609 1610 238 239 1611 1612 1613 1614 244 1615 1616 247 1617 249 1618 251 252 8206 8207 1746))) \r
+    (1257 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 8364 129 8218 131 8222 8230 8224 8225 136 8240 138 8249 140 168 711 184 144 8216 8217 8220 8221 8226 8211 8212 152 8482 154 8250 156 175 731 159 160 65533 162 163 164 65533 166 167 216 169 342 171 172 173 174 198 176 177 178 179 180 181 182 183 248 185 343 187 188 189 190 230 260 302 256 262 196 197 280 274 268 201 377 278 290 310 298 315 352 323 325 211 332 213 214 215 370 321 346 362 220 379 381 223 261 303 257 263 228 229 281 275 269 233 378 279 291 311 299 316 353 324 326 243 333 245 246 247 371 322 347 363 252 380 382 729))) \r
+    (1258 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 8364 129 8218 402 8222 8230 8224 8225 710 8240 138 8249 338 141 142 143 144 8216 8217 8220 8221 8226 8211 8212 732 8482 154 8250 339 157 158 376 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 258 196 197 198 199 200 201 202 203 768 205 206 207 272 209 777 211 212 416 214 215 216 217 218 219 220 431 771 223 224 225 226 259 228 229 230 231 232 233 234 235 769 237 238 239 273 241 803 243 244 417 246 247 248 249 250 251 252 432 8363 255))))\r
+  "A list of 8-bit Windows code pages where each element is a\r
+cons with the car being the ID of the code page and the cdr being\r
+a vector enumerating the corresponding character codes.")\r
diff --git a/deps/flexi-streams/conditions.lisp b/deps/flexi-streams/conditions.lisp
new file mode 100644 (file)
index 0000000..602ac6f
--- /dev/null
@@ -0,0 +1,108 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/conditions.lisp,v 1.9 2008/05/25 22:23:58 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(define-condition flexi-stream-error (stream-error)
+  ()
+  (:documentation "Superclass for all errors related to flexi
+streams."))
+
+(define-condition flexi-stream-simple-error (flexi-stream-error simple-condition)
+  ()
+  (:documentation "Like FLEXI-STREAM-ERROR but with formatting
+capabilities."))
+
+(define-condition flexi-stream-element-type-error (flexi-stream-error)
+  ((element-type :initarg :element-type
+                 :reader flexi-stream-element-type-error-element-type))
+  (:report (lambda (condition stream)
+             (format stream "Element type ~S not allowed."
+                     (flexi-stream-element-type-error-element-type condition))))
+  (:documentation "Errors of this type are signalled if the flexi
+stream has a wrong element type."))
+
+(define-condition flexi-stream-out-of-sync-error (flexi-stream-error)
+  ()
+  (:report (lambda (condition stream)
+             (declare (ignore condition))
+             (format stream "Stream out of sync from previous
+lookahead, couldn't rewind.")))
+  (:documentation "This can happen if you're trying to write to an IO
+stream which had prior to that `looked ahead' while reading and now
+can't `rewind' to the octet where you /should/ be."))
+
+(define-condition in-memory-stream-error (stream-error)
+  ()
+  (:documentation "Superclass for all errors related to
+IN-MEMORY streams."))
+
+(define-condition in-memory-stream-simple-error (in-memory-stream-error simple-condition)
+  ()
+  (:documentation "Like IN-MEMORY-STREAM-ERROR but with formatting
+capabilities."))
+
+(define-condition in-memory-stream-closed-error (in-memory-stream-error)
+  ()
+  (:report (lambda (condition stream)
+             (format stream "~S is closed."
+                     (stream-error-stream condition))))
+  (:documentation "An error that is signalled when someone is trying
+to read from or write to a closed IN-MEMORY stream."))
+
+(define-condition in-memory-stream-position-spec-error (in-memory-stream-simple-error)
+  ((position-spec :initarg :position-spec
+                  :reader in-memory-stream-position-spec-error-position-spec))
+  (:documentation "Errors of this type are signalled if an erroneous
+position spec is used in conjunction with FILE-POSITION."))
+
+(define-condition external-format-condition (simple-condition)
+  ((external-format :initarg :external-format
+                    :initform nil
+                    :reader external-format-condition-external-format))
+  (:documentation "Superclass for all conditions related to external
+formats."))
+
+(define-condition external-format-error (external-format-condition error)
+  ()
+  (:documentation "Superclass for all errors related to external
+formats."))
+  
+(define-condition external-format-encoding-error (external-format-error)
+  ()
+  (:documentation "Errors of this type are signalled if there is an
+encoding problem."))
+
+(defun signal-encoding-error (external-format format-control &rest format-args)
+  "Convenience function similar to ERROR to signal conditions of type
+EXTERNAL-FORMAT-ENCODING-ERROR."
+  (error 'external-format-encoding-error
+         :format-control format-control
+         :format-arguments format-args
+         :external-format external-format))
diff --git a/deps/flexi-streams/decode.lisp b/deps/flexi-streams/decode.lisp
new file mode 100644 (file)
index 0000000..ffca853
--- /dev/null
@@ -0,0 +1,471 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/decode.lisp,v 1.35 2008/08/26 10:59:22 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defun recover-from-encoding-error (external-format format-control &rest format-args)
+  "Helper function used by OCTETS-TO-CHAR-CODE below to deal with
+encoding errors.  Checks if *SUBSTITUTION-CHAR* is not NIL and returns
+its character code in this case.  Otherwise signals an
+EXTERNAL-FORMAT-ENCODING-ERROR as determined by the arguments to this
+function and provides a corresponding USE-VALUE restart."
+  (when *substitution-char*
+    (return-from recover-from-encoding-error (char-code *substitution-char*)))
+  (restart-case
+      (apply #'signal-encoding-error external-format format-control format-args)
+    (use-value (char)
+      :report "Specify a character to be used instead."
+      :interactive (lambda ()
+                     (loop
+                      (format *query-io* "Type a character: ")
+                      (let ((line (read-line *query-io*)))
+                        (when (= 1 (length line))
+                          (return (list (char line 0)))))))
+      (char-code char))))
+
+(defgeneric octets-to-char-code (format reader)
+  (declare #.*standard-optimize-settings*)
+  (:documentation "Converts a sequence of octets to a character code
+\(which is returned, or NIL in case of EOF) using the external format
+FORMAT.  The sequence is obtained by calling the function \(which must
+be a functional object) READER with no arguments which should return
+one octet per call.  In the case of EOF, READER should return NIL.
+
+The special variable *CURRENT-UNREADER* must be bound correctly
+whenever this function is called."))
+
+(defgeneric octets-to-string* (format sequence start end)
+  (declare #.*standard-optimize-settings*)
+  (:documentation "A generic function which dispatches on the external
+format and does the real work for OCTETS-TO-STRING."))
+
+(defmethod octets-to-string* :around (format (list list) start end)
+  (declare #.*standard-optimize-settings*)
+  (octets-to-string* format (coerce list 'vector) start end))
+
+(defmacro define-sequence-readers ((format-class) &body body)
+  "Non-hygienic utility macro which defines methods for READ-SEQUENCE*
+and OCTETS-TO-STRING* for the class FORMAT-CLASS.  BODY is described
+in the docstring of DEFINE-CHAR-ENCODERS but can additionally contain
+a form \(UNGET <form>) which has to be replaced by the correct code to
+`unread' the octets for the character designated by <form>."
+  (let* ((body `((block char-decoder
+                   (locally
+                     (declare #.*fixnum-optimize-settings*)
+                     ,@body)))))
+    `(progn
+       (defmethod read-sequence* ((format ,format-class) flexi-input-stream sequence start end)
+         (with-accessors ((position flexi-stream-position)
+                          (bound flexi-stream-bound)
+                          (octet-stack flexi-stream-octet-stack)
+                          (last-octet flexi-stream-last-octet)
+                          (last-char-code flexi-stream-last-char-code)
+                          (stream flexi-stream-stream))
+             flexi-input-stream
+           (let* (buffer
+                  (buffer-pos 0)
+                  (buffer-end 0)
+                  (index start)
+                  donep
+                  ;; whether we will later be able to rewind the stream if
+                  ;; needed (to get rid of unused octets in the buffer)
+                  (can-rewind-p (maybe-rewind stream 0))
+                  (factor (encoding-factor format))
+                  (integer-factor (floor factor))
+                  ;; it's an interesting question whether it makes sense
+                  ;; performance-wise to make RESERVE significantly bigger
+                  ;; (and thus put potentially a lot more octets into
+                  ;; OCTET-STACK), especially for UTF-8
+                  (reserve (cond ((or (not (floatp factor))
+                                      (not can-rewind-p)) 0)
+                                 (t (ceiling (* (- factor integer-factor) (- end start)))))))
+             (declare (fixnum buffer-pos buffer-end index integer-factor reserve)
+                      (boolean can-rewind-p))
+             (flet ((compute-fill-amount ()
+                      "Computes the amount of octets we can savely read into
+the buffer without violating the stream's bound \(if there is one) and
+without potentially reading much more than we need \(unless we can
+rewind afterwards)."
+                      (let ((minimum (min (the fixnum (+ (the fixnum (* integer-factor
+                                                                        (the fixnum (- end index))))
+                                                         reserve))
+                                          +buffer-size+)))
+                        (cond (bound (min minimum (- bound position)))
+                              (t minimum))))
+                    (fill-buffer (end)
+                      "Tries to fill the buffer from BUFFER-POS to END and
+returns NIL if the buffer doesn't contain any new data."
+                      (when donep
+                        (return-from fill-buffer nil))
+                      ;; put data from octet stack into buffer if there is any
+                      (loop
+                       (when (>= buffer-pos end)
+                         (return))
+                       (let ((next-octet (pop octet-stack)))
+                         (cond (next-octet
+                                (setf (aref (the (array octet *) buffer) buffer-pos) (the octet next-octet))
+                                (incf buffer-pos))
+                               (t (return)))))
+                      (setq buffer-end (read-sequence buffer stream
+                                                      :start buffer-pos
+                                                      :end end))
+                      ;; we reached EOF, so we remember this
+                      (when (< buffer-end end)
+                        (setq donep t))
+                      ;; BUFFER-POS is only greater than zero if the buffer
+                      ;; already contains unread data from the octet stack
+                      ;; (see below), so we test for ZEROP here and do /not/
+                      ;; compare with BUFFER-POS
+                      (unless (zerop buffer-end)
+                        (incf position buffer-end))))
+               (let ((minimum (compute-fill-amount)))
+                 (declare (fixnum minimum))
+                 (setq buffer (make-octet-buffer minimum))
+                 ;; fill buffer for the first time or return immediately if
+                 ;; we don't succeed
+                 (unless (fill-buffer minimum)
+                   (return-from read-sequence* start)))
+               (setq buffer-pos 0)
+               (macrolet ((iterate (set-place)
+                            "A very unhygienic macro to implement the
+actual iteration through the sequence including housekeeping for the
+flexi stream.  SET-PLACE is the place \(using the index INDEX) used to
+access the sequence."
+                            `(flet ((leave ()
+                                      "This is the function used to
+abort the LOOP iteration below."
+                                      (when (> index start)
+                                        (setq last-octet nil
+                                              last-char-code ,(sublis '((index . (1- index))) set-place)))
+                                      (return-from read-sequence* index)))
+                               (loop
+                                (when (>= index end)
+                                  ;; check if there are octets in the
+                                  ;; buffer we didn't use - see
+                                  ;; COMPUTE-FILL-AMOUNT above
+                                  (let ((rest (- buffer-end buffer-pos)))
+                                    (when (plusp rest)
+                                      (or (and can-rewind-p
+                                               (maybe-rewind stream rest))
+                                          (loop
+                                           (when (>= buffer-pos buffer-end)
+                                             (return))
+                                           (decf buffer-end)
+                                           (push (aref (the (array octet *) buffer) buffer-end)
+                                                 octet-stack)))))
+                                  (leave))
+                                (let ((next-char-code
+                                       (progn (symbol-macrolet
+                                                  ((octet-getter
+                                                    ;; this is the code to retrieve the next octet (or
+                                                    ;; NIL) and to fill the buffer if needed
+                                                    (block next-octet
+                                                      (when (>= buffer-pos buffer-end)
+                                                        (setq buffer-pos 0)
+                                                        (unless (fill-buffer (compute-fill-amount))
+                                                          (return-from next-octet)))
+                                                      (prog1
+                                                          (aref (the (array octet *) buffer) buffer-pos)
+                                                        (incf buffer-pos)))))
+                                                (macrolet ((unget (form)
+                                                             `(unread-char% ,form flexi-input-stream)))
+                                                  ,',@body)))))
+                                  (unless next-char-code
+                                    (leave))
+                                  (setf ,set-place (code-char next-char-code))
+                                  (incf index))))))
+                 (etypecase sequence
+                   (string (iterate (char sequence index)))
+                   (array (iterate (aref sequence index)))
+                   (list (iterate (nth index sequence)))))))))
+       (defmethod octets-to-string* ((format ,format-class) sequence start end)
+         (declare #.*standard-optimize-settings*)
+         (declare (fixnum start end))
+         (let* ((i start)
+                (string-length (compute-number-of-chars format sequence start end))
+                (string (make-array string-length :element-type 'char*)))
+           (declare (fixnum i string-length))
+           (loop for j of-type fixnum from 0 below string-length
+                 do (setf (schar string j)
+                          (code-char (macrolet ((unget (form)
+                                                  `(decf i (character-length format ,form))))
+                                       ;; we don't need to test for
+                                       ;; the end of SEQUENCE as the
+                                       ;; computation has been done
+                                       ;; for us already
+                                       (symbol-macrolet ((octet-getter (prog1
+                                                                           (aref sequence i)
+                                                                         (incf i))))
+                                         ,@body))))
+                 finally (return string)))))))
+
+(defmacro define-char-decoders ((lf-format-class cr-format-class crlf-format-class) &body body)
+  "Non-hygienic utility macro which defines several decoding-related
+methods for the classes LF-FORMAT-CLASS, CR-FORMAT-CLASS, and
+CRLF-FORMAT-CLASS where it is assumed that CR-FORMAT-CLASS is the same
+encoding as LF-FORMAT-CLASS but with CR instead of LF line endings and
+similar for CRLF-FORMAT-CLASS, i.e. LF-FORMAT-CLASS is the base class.
+BODY is a code template for the code to read octets and return one
+character code.  BODY must contain a symbol OCTET-GETTER representing
+the form which is used to obtain the next octet."
+  (let* ((body (with-unique-names (char-code)
+                 `((let ((,char-code (progn ,@body)))
+                     (when (and ,char-code
+                                (or (<= #xd8 (logand* #x00ff (ash* ,char-code -8)) #xdf)
+                                    (> ,char-code #x10ffff)))
+                       (recover-from-encoding-error format "Illegal code point ~A \(#x~:*~X)." ,char-code))
+                     ,char-code)))))
+    `(progn
+       (defmethod octets-to-char-code ((format ,lf-format-class) reader)
+         (declare #.*fixnum-optimize-settings*)
+         (declare (function reader))
+         (symbol-macrolet ((octet-getter (funcall reader)))
+           ,@(sublis '((char-decoder . octets-to-char-code))
+                     body)))
+       (define-sequence-readers (,lf-format-class) ,@body)
+       (define-sequence-readers (,cr-format-class)
+         ,(with-unique-names (char-code)
+            `(let ((,char-code (progn ,@body)))
+               (case ,char-code
+                 (#.+cr+ #.(char-code #\Newline))
+                 (otherwise ,char-code)))))
+       (define-sequence-readers  (,crlf-format-class)
+         ,(with-unique-names (char-code next-char-code get-char-code)
+            `(flet ((,get-char-code () ,@body))
+               (let ((,char-code (,get-char-code)))
+                 (case ,char-code
+                   (#.+cr+
+                    (let ((,next-char-code (,get-char-code)))
+                      (case ,next-char-code
+                        (#.+lf+ #.(char-code #\Newline))
+                        ;; we saw a CR but no LF afterwards, but then the data
+                        ;; ended, so we just return #\Return
+                        ((nil) +cr+)
+                        ;; if the character we peeked at wasn't a
+                        ;; linefeed character we unread its constituents
+                        (otherwise (unget (code-char ,next-char-code))
+                                   ,char-code))))
+                   (otherwise ,char-code)))))))))
+
+(define-char-decoders (flexi-latin-1-format flexi-cr-latin-1-format flexi-crlf-latin-1-format)
+  octet-getter)
+
+(define-char-decoders (flexi-ascii-format flexi-cr-ascii-format flexi-crlf-ascii-format)
+  (when-let (octet octet-getter)
+    (if (> (the octet octet) 127)
+      (recover-from-encoding-error format
+                                   "No character which corresponds to octet #x~X." octet)
+      octet)))
+
+(define-char-decoders (flexi-8-bit-format flexi-cr-8-bit-format flexi-crlf-8-bit-format)
+  (with-accessors ((decoding-table external-format-decoding-table))
+      format
+    (when-let (octet octet-getter)
+      (let ((char-code (aref (the (simple-array char-code-integer *) decoding-table)
+                             (the octet octet))))
+        (if (or (null char-code)
+                (= (the char-code-integer char-code) 65533))
+          (recover-from-encoding-error format
+                                       "No character which corresponds to octet #x~X." octet)
+          char-code)))))
+
+(define-char-decoders (flexi-utf-8-format flexi-cr-utf-8-format flexi-crlf-utf-8-format)
+  (let (first-octet-seen)
+    (declare (boolean first-octet-seen))
+    (macrolet ((read-next-byte ()
+                 '(prog1
+                      (or octet-getter
+                          (cond (first-octet-seen
+                                 (return-from char-decoder
+                                   (recover-from-encoding-error format
+                                                                "End of data while in UTF-8 sequence.")))
+                                (t (return-from char-decoder nil))))
+                    (setq first-octet-seen t))))
+      (flet ((recover-from-overlong-sequence (value)
+               (restart-case
+                   (recover-from-encoding-error format "`Overlong' UTF-8 sequence for code point #x~X."
+                                                value)                 
+                 (accept-overlong-sequence ()
+                   :report "Accept the code point and continue."
+                   value))))
+        (let ((octet (read-next-byte)))
+          (declare (type octet octet))
+          (block utf-8-sequence
+            (multiple-value-bind (start count)
+                (cond ((not (logbitp 7 octet))
+                       ;; avoid the overlong checks below
+                       (return-from utf-8-sequence octet))
+                      ((= #b11000000 (logand* octet #b11100000))
+                       (values (logand* octet #b00011111) 1))
+                      ((= #b11100000 (logand* octet #b11110000))
+                       (values (logand* octet #b00001111) 2))
+                      ((= #b11110000 (logand* octet #b11111000))
+                       (values (logand* octet #b00000111) 3))
+                      (t (return-from char-decoder
+                           (recover-from-encoding-error format
+                                                        "Unexpected value #x~X at start of UTF-8 sequence."
+                                                        octet))))
+              (declare (fixnum count))
+              (loop for result of-type code-point
+                    = start then (+ (ash* result 6)
+                                    (logand* octet #b111111))
+                    repeat count
+                    for octet of-type octet = (read-next-byte)
+                    unless (= #b10000000 (logand* octet #b11000000))
+                    do (return-from char-decoder
+                         (recover-from-encoding-error format
+                                                      "Unexpected value #x~X in UTF-8 sequence." octet))
+                    finally (return (cond ((< result (ecase count
+                                                       (1 #x00080)
+                                                       (2 #x00800)
+                                                       (3 #x10000)))
+                                           (recover-from-overlong-sequence result))
+                                          (t result)))))))))))
+
+(define-char-decoders (flexi-utf-16-le-format flexi-cr-utf-16-le-format flexi-crlf-utf-16-le-format)
+  (let (first-octet-seen)
+    (declare (boolean first-octet-seen))
+    (macrolet ((read-next-byte ()
+                 '(prog1
+                      (or octet-getter
+                          (cond (first-octet-seen
+                                 (return-from char-decoder
+                                   (recover-from-encoding-error format
+                                                                "End of data while in UTF-16 sequence.")))
+                                (t (return-from char-decoder nil))))
+                    (setq first-octet-seen t))))
+      (flet ((read-next-word ()
+               (+ (the octet (read-next-byte))
+                  (ash* (the octet (read-next-byte)) 8))))
+        (declare (inline read-next-word))
+        (let ((word (read-next-word)))
+          (declare (type (unsigned-byte 16) word))
+          (cond ((<= #xd800 word #xdfff)
+                 (let ((next-word (read-next-word)))
+                   (declare (type (unsigned-byte 16) next-word))
+                   (unless (<= #xdc00 next-word #xdfff)
+                     (return-from char-decoder
+                       (recover-from-encoding-error format
+                                                    "Unexpected UTF-16 word #x~X following #x~X."
+                                                    next-word word)))
+                   (+ (ash* (logand* #b1111111111 word) 10)
+                      (logand* #b1111111111 next-word)
+                      #x10000)))
+                (t word)))))))
+
+(define-char-decoders (flexi-utf-16-be-format flexi-cr-utf-16-be-format flexi-crlf-utf-16-be-format)
+  (let (first-octet-seen)
+    (declare (boolean first-octet-seen))
+    (macrolet ((read-next-byte ()
+                 '(prog1
+                      (or octet-getter
+                          (cond (first-octet-seen
+                                 (return-from char-decoder
+                                   (recover-from-encoding-error format
+                                                                "End of data while in UTF-16 sequence.")))
+                                (t (return-from char-decoder nil))))
+                    (setq first-octet-seen t))))
+      (flet ((read-next-word ()
+               (+ (ash* (the octet (read-next-byte)) 8)
+                  (the octet (read-next-byte)))))
+        (declare (inline read-next-word))
+        (let ((word (read-next-word)))
+          (declare (type (unsigned-byte 16) word))
+          (cond ((<= #xd800 word #xdfff)
+                 (let ((next-word (read-next-word)))
+                   (declare (type (unsigned-byte 16) next-word))
+                   (unless (<= #xdc00 next-word #xdfff)
+                     (return-from char-decoder
+                       (recover-from-encoding-error format
+                                                    "Unexpected UTF-16 word #x~X following #x~X."
+                                                    next-word word)))
+                   (+ (ash* (logand* #b1111111111 word) 10)
+                      (logand* #b1111111111 next-word)
+                      #x10000)))
+                (t word)))))))
+
+(define-char-decoders (flexi-utf-32-le-format flexi-cr-utf-32-le-format flexi-crlf-utf-32-le-format)
+  (let (first-octet-seen)
+    (declare (boolean first-octet-seen))
+    (macrolet ((read-next-byte ()
+                 '(prog1
+                      (or octet-getter
+                          (cond (first-octet-seen
+                                 (return-from char-decoder
+                                   (recover-from-encoding-error format
+                                                                "End of data while in UTF-32 sequence.")))
+                                (t (return-from char-decoder nil))))
+                    (setq first-octet-seen t))))
+      (loop for count of-type fixnum from 0 to 24 by 8
+            for octet of-type octet = (read-next-byte)
+            sum (ash* octet count)))))
+
+(define-char-decoders (flexi-utf-32-be-format flexi-cr-utf-32-be-format flexi-crlf-utf-32-be-format)
+  (let (first-octet-seen)
+    (declare (boolean first-octet-seen))
+    (macrolet ((read-next-byte ()
+                 '(prog1
+                      (or octet-getter
+                          (cond (first-octet-seen
+                                 (return-from char-decoder
+                                   (recover-from-encoding-error format
+                                                                "End of data while in UTF-32 sequence.")))
+                                (t (return-from char-decoder nil))))
+                    (setq first-octet-seen t))))
+      (loop for count of-type fixnum from 24 downto 0 by 8
+            for octet of-type octet = (read-next-byte)
+            sum (ash* octet count)))))
+
+(defmethod octets-to-char-code ((format flexi-cr-mixin) reader)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (ignore reader))
+  (let ((char-code (call-next-method)))
+    (case char-code
+      (#.+cr+ #.(char-code #\Newline))
+      (otherwise char-code))))
+
+(defmethod octets-to-char-code ((format flexi-crlf-mixin) reader)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (function *current-unreader*))
+  (declare (ignore reader))
+  (let ((char-code (call-next-method)))
+    (case char-code
+      (#.+cr+
+       (let ((next-char-code (call-next-method)))
+         (case next-char-code
+           (#.+lf+ #.(char-code #\Newline))
+           ;; we saw a CR but no LF afterwards, but then the data
+           ;; ended, so we just return #\Return
+           ((nil) +cr+)
+           ;; if the character we peeked at wasn't a
+           ;; linefeed character we unread its constituents
+           (otherwise (funcall *current-unreader* (code-char next-char-code))
+                      char-code))))
+      (otherwise char-code))))
+
diff --git a/deps/flexi-streams/doc/foo.txt b/deps/flexi-streams/doc/foo.txt
new file mode 100644 (file)
index 0000000..e596ded
Binary files /dev/null and b/deps/flexi-streams/doc/foo.txt differ
diff --git a/deps/flexi-streams/doc/index.html b/deps/flexi-streams/doc/index.html
new file mode 100644 (file)
index 0000000..941e8e3
--- /dev/null
@@ -0,0 +1,1123 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html> 
+
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+  <title>FLEXI-STREAMS - Flexible bivalent streams for Common Lisp</title>
+  <style type="text/css">
+  pre { padding:5px; background-color:#e0e0e0 }
+  h3, h4 { text-decoration: underline; }
+  a { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:visited { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; } 
+  a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; }
+  a.none { text-decoration: none; padding: 0; }
+  a.none:visited { text-decoration: none; padding: 0; } 
+  a.none:hover { text-decoration: none; border: none; padding: 0; } 
+  a.none:focus { text-decoration: none; border: none; padding: 0; } 
+  a.noborder { text-decoration: none; padding: 0; } 
+  a.noborder:visited { text-decoration: none; padding: 0; } 
+  a.noborder:hover { text-decoration: none; border: none; padding: 0; } 
+  a.noborder:focus { text-decoration: none; border: none; padding: 0; }  
+  </style>
+</head>
+
+<body bgcolor=white>
+
+<h2>FLEXI-STREAMS - Flexible bivalent streams for Common Lisp</h2>
+
+<blockquote>
+<br>&nbsp;<br><h3><a name=abstract class=none>Abstract</a></h3>
+
+FLEXI-STREAMS implements "virtual" bivalent streams that can be
+layered atop real binary or bivalent streams and that can be used to
+read and write character data in various single- or multi-octet
+encodings which can be changed on the fly.  It also supplies
+<em>in-memory</em> binary streams which are similar to string streams.
+<p>
+The library needs a Common Lisp implementation that
+supports <a
+href="http://www.nhplace.com/kent/CL/Issues/stream-definition-by-user.html"><em>Gray
+streams</em></a> and relies on David
+Lichteblau's <a
+href="http://www.cliki.net/trivial-gray-streams">trivial-gray-streams</a>
+to offer portability between different Lisps.
+<p>
+The code comes with
+a <a
+href="http://www.opensource.org/licenses/bsd-license.php">BSD-style
+license</a> so you can basically do with it whatever you want.
+
+<p>
+<font color=red>Download shortcut:</font> <a href="http://weitz.de/files/flexi-streams.tar.gz">http://weitz.de/files/flexi-streams.tar.gz</a>.
+</blockquote>
+
+<br>&nbsp;<br><h3><a class=none name="contents">Contents</a></h3>
+<ol>
+  <li><a href="#example">Example usage</a>
+  <li><a href="#install">Download and installation</a>
+  <li><a href="#support">Support</a>
+  <li><a href="#dictionary">The FLEXI-STREAMS dictionary</a>
+  <ol>
+    <li><a href="#external-formats">External formats</a>
+      <ol>
+      <li><a href="#make-external-format"><code>make-external-format</code></a>
+      <li><a href="#external-format-name"><code>external-format-name</code></a>
+      <li><a href="#external-format-eol-style"><code>external-format-eol-style</code></a>
+      <li><a href="#external-format-little-endian"><code>external-format-little-endian</code></a>
+      <li><a href="#external-format-id"><code>external-format-id</code></a>
+      <li><a href="#external-format-equal"><code>external-format-equal</code></a>
+      <li><a href="#*default-eol-style*"><code>*default-eol-style*</code></a>
+      <li><a href="#*default-little-endian*"><code>*default-little-endian*</code></a>
+      <li><a href="#external-format-condition"><code>external-format-condition</code></a>
+      <li><a href="#external-format-condition-external-format"><code>external-format-condition-external-format</code></a>
+      <li><a href="#external-format-error"><code>external-format-error</code></a>
+      <li><a href="#external-format-encoding-error"><code>external-format-encoding-error</code></a>
+      <li><a href="#*substitution-char*"><code>*substitution-char*</code></a>
+      <li><a href="#accept-overlong-sequence"><code>accept-overlong-sequence</code></a>
+      </ol>
+    <li><a href="#flexi-streams">Flexi streams</a>
+      <ol>
+      <li><a href="#flexi-stream"><code>flexi-stream</code></a>
+      <li><a href="#flexi-input-stream"><code>flexi-input-stream</code></a>
+      <li><a href="#flexi-output-stream"><code>flexi-output-stream</code></a>
+      <li><a href="#flexi-io-stream"><code>flexi-io-stream</code></a>
+      <li><a href="#make-flexi-stream"><code>make-flexi-stream</code></a>
+      <li><a href="#flexi-stream-external-format"><code>flexi-stream-external-format</code></a>
+      <li><a href="#flexi-stream-element-type"><code>flexi-stream-element-type</code></a>
+      <li><a href="#flexi-stream-column"><code>flexi-stream-column</code></a>
+      <li><a href="#flexi-stream-position"><code>flexi-stream-position</code></a>
+      <li><a href="#flexi-stream-bound"><code>flexi-stream-bound</code></a>
+      <li><a href="#flexi-stream-stream"><code>flexi-stream-stream</code></a>
+      <li><a href="#unread-byte"><code>unread-byte</code></a>
+      <li><a href="#peek-byte"><code>peek-byte</code></a>
+      <li><a href="#octet"><code>octet</code></a>
+      <li><a href="#flexi-stream-error"><code>flexi-stream-error</code></a>
+      <li><a href="#flexi-stream-out-of-sync-error"><code>flexi-stream-out-of-sync-error</code></a>
+      <li><a href="#flexi-stream-element-type-error"><code>flexi-stream-element-type-error</code></a>
+      <li><a href="#flexi-stream-element-type-error-element-type"><code>flexi-stream-element-type-error-element-type</code></a>
+      </ol>
+    <li><a href="#in-memory">In-memory streams</a>
+      <ol>
+      <li><a href="#in-memory-stream"><code>in-memory-stream</code></a>
+      <li><a href="#in-memory-input-stream"><code>in-memory-input-stream</code></a>
+      <li><a href="#in-memory-output-stream"><code>in-memory-output-stream</code></a>
+      <li><a href="#list-stream"><code>list-stream</code></a>
+      <li><a href="#vector-stream"><code>vector-stream</code></a>
+      <li><a href="#make-in-memory-input-stream"><code>make-in-memory-input-stream</code></a>
+      <li><a href="#make-in-memory-output-stream"><code>make-in-memory-output-stream</code></a>
+      <li><a href="#get-output-stream-sequence"><code>get-output-stream-sequence</code></a>
+      <li><a href="#output-stream-sequence-length"><code>output-stream-sequence-length</code></a>
+      <li><a href="#with-input-from-sequence"><code>with-input-from-sequence</code></a>
+      <li><a href="#with-output-to-sequence"><code>with-output-to-sequence</code></a>
+      <li><a href="#in-memory-stream-error"><code>in-memory-stream-error</code></a>
+      <li><a href="#in-memory-stream-closed-error"><code>in-memory-stream-closed-error</code></a>
+      <li><a href="#in-memory-stream-position-spec-error"><code>in-memory-stream-position-spec-error</code></a>
+      <li><a href="#in-memory-stream-position-spec-error-position-spec"><code>in-memory-stream-position-spec-error-position-spec</code></a>
+      </ol>
+    <li><a href="#strings">Strings</a>
+      <ol>
+      <li><a href="#string-to-octets"><code>string-to-octets</code></a>
+      <li><a href="#octets-to-string"><code>octets-to-string</code></a>
+      <li><a href="#octet-length"><code>octet-length</code></a>
+      <li><a href="#char-length"><code>char-length</code></a>
+      </ol>
+  </ol>
+  <li><a href="#position">File positions</a>
+  <li><a href="#ack">Acknowledgements</a>
+</ol>
+
+<br>&nbsp;<br><h3><a name="example" class=none>Example usage</a></h3>
+
+The examples were created with <a href="http://www.lispworks.com/">LispWorks</a> 4.4.6&nbsp;pro on Windows. The following two functions create <a href="foo.txt">the same file</a>:
+
+<pre>
+(defun foo (pathspec)
+  "With standard LispWorks streams."
+  (with-open-file (out pathspec
+                       :direction :output
+                       :if-exists :supersede
+                       :external-format '(:utf-8 :eol-style :crlf))
+    (write-line "&Auml;&Ouml;&Uuml;1" out))
+  (with-open-file (out pathspec
+                       :direction :output
+                       :if-exists :append
+                       :external-format '(:latin-1 :eol-style :lf))
+    (write-line "&Auml;&Ouml;&Uuml;2" out))
+  (with-open-file (out pathspec
+                       :direction :output
+                       :if-exists :append
+                       :element-type 'octet)
+    (write-byte #xeb out)
+    (write-sequence #(#xa3 #xa4 #xa5) out))
+  (with-open-file (out pathspec
+                       :direction :output
+                       :if-exists :append
+                       :external-format '(:unicode :little-endian nil :eol-style :crlf))
+    (write-line "&Auml;&Ouml;&Uuml;3" out)))
+
+(defun bar (pathspec)
+  &quot;With a <a href="#flexi-streams" class=noborder>flexi stream</a>.&quot;
+  (with-open-file (out pathspec
+                       :direction :output
+                       :if-exists :supersede
+                       :external-format '(:latin-1 :eol-style :lf))
+    (setq out (<a href="#make-flexi-stream" class=noborder>make-flexi-stream</a> out <a href="#external-formats" class=noborder>:external-format</a> :utf-8))
+    (write-line "&Auml;&Ouml;&Uuml;1" out)
+    (setf (<a href="#flexi-stream-external-format" class=noborder>flexi-stream-external-format</a> out) '(:latin-1 :eol-style :lf))
+    (write-line "&Auml;&Ouml;&Uuml;2" out) 
+    (write-byte #xeb out)
+    (write-sequence #(#xa3 #xa4 #xa5) out)
+    (setf (flexi-stream-external-format out) :ucs-2be)
+    (write-line "&Auml;&Ouml;&Uuml;3" out)))
+</pre>
+
+<p>
+And applying this function
+<pre>
+(defun baz (pathspec)
+  (let (result)
+    (with-open-file (in pathspec :element-type '<a href="#octet" class=noborder>octet</a>)
+      (setq in (<a href="#make-flexi-stream" class=noborder>make-flexi-stream</a> in <a href="#external-formats" class=noborder>:external-format</a> :utf-8))
+      (push (read-line in) result)
+      (push (read-byte in) result)
+      (setf (<a href="#flexi-stream-external-format" class=noborder>flexi-stream-external-format</a> in) '(:latin-1 :eol-style :lf))
+      (push (read-line in) result) 
+      (setf (flexi-stream-external-format in) :greek)
+      (push (read-char in) result)
+      (setf (flexi-stream-external-format in) :latin0)
+      (let ((string (make-string 3 :element-type 'character)))
+        (read-sequence string in)
+        (push string result))
+      (let ((octets (make-array 2 :element-type 'octet)))
+        (read-sequence octets in)
+        (push octets result))
+      (setf (flexi-stream-external-format in) :ucs-2be)
+      (push (read-line in) result))
+    (nreverse result)))
+</pre>
+to the file created above will yield the list
+<pre>
+("&Auml;&Ouml;&Uuml;1"&nbsp;196&nbsp;"&Ouml;&Uuml;2"&nbsp;#\&lambda;&nbsp;"&#163;&#8364;&#165;"&nbsp;#(0&nbsp;196)&nbsp;"&Ouml;&Uuml;3")
+</pre>
+
+<p>
+For more examples see the source code
+of 
+<a href="http://mr-co.de/projects/cl-rfc2047/">CL-RFC2047</a>,
+<a
+href="http://weitz.de/drakma/">Drakma</a>, <a
+href="http://weitz.de/chunga/">Chunga</a>,
+or <a href="http://weitz.de/cl-wbxml/">CL-WBXML</a>.
+
+<br>&nbsp;<br><h3><a name="install" class=none>Download and installation</a></h3>
+
+Before you try to install FLEXI-STREAMS, first check that in your Lisp
+each <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/13_.htm">character</a>'s
+<a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#character_code">character
+code</a> is equal to
+its <a
+href="http://en.wikipedia.org/wiki/Unicode">Unicode</a> <a
+href="http://unicode.org/glossary/">code point</a> and
+that <code>(CHAR-CODE&nbsp;#\Newline)</code>
+and <code>(CHAR-CODE&nbsp;#\Linefeed)</code> have the same
+value&nbsp;(10).  (This is the case for all relevant CL
+implementations which were in use when this library was written.  It
+is <em>not</em> mandated by the ANSI standard, though.)
+<p>
+FLEXI-STREAMS together with this documentation can be downloaded from <a
+href="http://weitz.de/files/flexi-streams.tar.gz">http://weitz.de/files/flexi-streams.tar.gz</a>. The
+current version is 1.0.12.
+<p>
+Before you install FLEXI-STREAMS you first need to
+install the <a
+href="http://www.cliki.net/trivial-gray-streams">trivial-gray-streams</a> library
+unless you already have it.
+<p>
+FLEXI-STREAMS comes with a system definition for <a
+href="http://www.cliki.net/asdf">ASDF</a> so you can install the library with
+<pre>
+(asdf:oos 'asdf:load-op :flexi-streams)
+</pre>
+if you've unpacked it in a place where ASDF can find it.  Installation
+via <a href="http://www.cliki.net/asdf-install">asdf-install</a>
+should also be possible, and there's a port
+to <a href="http://www.cliki.net/Gentoo">Gentoo Lisp</a> thanks to
+Matthew Kennedy.
+<p>
+You can run a test suite which tests <em>some</em> (but
+not <em>all</em>) aspects of the library with
+<pre>
+(asdf:oos 'asdf:test-op :flexi-streams)
+</pre>
+This might take a while...
+<p>
+The current development version of FLEXI-STREAMS can be found
+at <a href="http://bknr.net/trac/browser/trunk/thirdparty">http://bknr.net/trac/browser/trunk/thirdparty</a>.
+This is the one to send <a href="#mail">patches</a> against.  Use at
+your own risk.
+<p>
+Lu&iacute;s Oliveira maintains a <a href="http://darcs.net/">darcs</a>
+repository of FLEXI-STREAMS
+at <a href="http://common-lisp.net/%7Eloliveira/ediware/">http://common-lisp.net/~loliveira/ediware/</a>.
+<p>
+A <a href="http://www.selenic.com/mercurial/wiki/">Mercurial</a>
+repository of older versions is available
+at <a
+href="http://arcanes.fr.eu.org/~pierre/2007/02/weitz/">http://arcanes.fr.eu.org/~pierre/2007/02/weitz/</a>
+thanks to Pierre Thierry.
+
+<br>&nbsp;<br><h3><a name="support" class=none>Support</a></h3>
+
+The development version of flexi-streams can be
+found <a href="https://github.com/edicl/flexi-streams" target="_new">on
+github</a>.  Please use the github issue tracking system to submit bug
+reports.  Patches are welcome, please
+use <a href="https://github.com/edicl/flexi-streams/pulls">GitHub pull
+requests</a>.  If you want to make a change,
+please <a href="http://weitz.de/patches.html" target="_new">read this
+first</a>.
+
+<br>&nbsp;<br><h3><a class=none name="dictionary">The FLEXI-STREAMS dictionary</a></h3>
+
+<h4><a name="external-formats" class=none>External formats</a></h4>
+
+<code>EXTERNAL-FORMAT</code> objects are used to denote the external
+formats of <a href="#flexi-streams">flexi streams</a>.  These objects are created using
+the <a
+href="#make-external-format"><code>MAKE-EXTERNAL-FORMAT</code></a>
+function, and there are <a href="#external-format-name">various
+readers</a> to query their attributes.  Once such an object is
+created it can't be changed.
+<p>
+An external format consists of a basic encoding
+(like <a
+href="http://en.wikipedia.org/wiki/Iso-8859-1">ISO&nbsp;8859-1</a>
+or <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a>), a
+definition how line endings are denoted - by a carriage return
+character (ASCII&nbsp;13), by a line feed character (ASCII&nbsp;10),
+or by both of these characters in a row -, and optionally (for
+encodings that use units larger than 8&nbsp;bits) information
+about the <a href="http://en.wikipedia.org/wiki/Endian">endianess</a>
+of the encoding.
+<p>
+The following encodings are currently supported by FLEXI-STREAMS:
+<ul>
+<li><a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> (denoted by the keyword <code>:UTF-8</code>),
+<li><a href="http://en.wikipedia.org/wiki/UTF-16">UTF-16</a> (denoted by the keyword <code>:UTF-16</code>),
+<li><a href="http://en.wikipedia.org/wiki/UTF-32">UTF-32</a> (denoted by the keyword <code>:UTF-32</code>),
+<li>all <a href="http://czyborra.com/charsets/iso8859.html">ISO 8859</a> character sets (denoted by keywords like <code>:ISO-8859-15</code>),
+<li><a href="http://en.wikipedia.org/wiki/KOI8-R">KOI8-R</a> (denoted by the keyword <code>:KOI8-R</code>),
+<li>a couple
+of <a href="http://czyborra.com/charsets/codepages.html">Windows code
+pages</a> (denoted by the keyword <code>:CODE-PAGE</code> and an
+obligatory <code>:ID</code> argument), and
+<li><a href="http://en.wikipedia.org/wiki/ASCII">US-ASCII</a>.
+</ul>
+<p>
+A couple of alternative names are allowed that are listed below:
+<p>
+<table border=1>
+<tr><td><code>:UTF-8</code></td><td><code>:UTF8</code></td></tr>
+<tr><td rowspan=4 valign=top><code>:UTF-16</code></td><td><code>:UTF16</code></td></tr>
+<tr><td><code>:UCS-2</code></td></tr>
+<tr><td><code>:UCS2</code></td></tr>
+<tr><td><code>:UNICODE</code></td></tr>
+<tr><td rowspan=3 valign=top><code>:UTF-32</code></td><td><code>:UTF32</code></td></tr>
+<tr><td><code>:UCS-4</code></td></tr>
+<tr><td><code>:UCS4</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:ISO-8859-1</code></td><td><code>:LATIN-1</code></td></tr>
+<tr><td><code>:LATIN1</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:ISO-8859-2</code></td><td><code>:LATIN-2</code></td></tr>
+<tr><td><code>:LATIN2</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:ISO-8859-3</code></td><td><code>:LATIN-3</code></td></tr>
+<tr><td><code>:LATIN3</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:ISO-8859-4</code></td><td><code>:LATIN-4</code></td></tr>
+<tr><td><code>:LATIN4</code></td></tr>
+<tr><td><code>:ISO-8859-5</code></td><td><code>:CYRILLIC</code></td></tr>
+<tr><td><code>:ISO-8859-6</code></td><td><code>:ARABIC</code></td></tr>
+<tr><td><code>:ISO-8859-7</code></td><td><code>:GREEK</code></td></tr>
+<tr><td><code>:ISO-8859-8</code></td><td><code>:HEBREW</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:ISO-8859-9</code></td><td><code>:LATIN-5</code></td></tr>
+<tr><td><code>:LATIN5</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:ISO-8859-10</code></td><td><code>:LATIN-6</code></td></tr>
+<tr><td><code>:LATIN6</code></td></tr>
+<tr><td><code>:ISO-8859-11</code></td><td><code>:THAI</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:ISO-8859-13</code></td><td><code>:LATIN-7</code></td></tr>
+<tr><td><code>:LATIN7</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:ISO-8859-14</code></td><td><code>:LATIN-8</code></td></tr>
+<tr><td><code>:LATIN8</code></td></tr>
+<tr><td rowspan=4 valign=top><code>:ISO-8859-15</code></td><td><code>:LATIN-9</code></td></tr>
+<tr><td><code>:LATIN9</code></td></tr>
+<tr><td><code>:LATIN-0</code></td></tr>
+<tr><td><code>:LATIN0</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:ISO-8859-16</code></td><td><code>:LATIN-10</code></td></tr>
+<tr><td><code>:LATIN10</code></td></tr>
+<tr><td rowspan=2 valign=top><code>:CODE-PAGE</code></td><td><code>:CODEPAGE</code></td></tr>
+<tr><td><code>WIN32:CODE-PAGE<br>(only on <a href="http://www.lispworks.com/products/lww.html">LWW</a>)</code></td></tr>
+<tr><td><code>:KOI8-R</code></td><td><code>:KOI8R</code></td></tr>
+<tr><td><code>:US-ASCII</code></td><td><code>:ASCII</code></td></tr>
+</table>
+<p>
+(Note that we treat UCS-2 exactly like UTF-16 although there
+are <a href="http://en.wikipedia.org/wiki/UTF-16">subtle
+differences</a>.  Also note that even though we support encodings like
+UTF-32 some Lisps only supports characters contained within
+the <a
+href="http://en.wikipedia.org/wiki/Basic_Multilingual_Plane">Basic
+Multilingual Plane</a> (like LispWorks) or even less (like CMUCL), so
+if other characters are read from a
+<a href="#flexi-streams">flexi
+stream</a>, <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_cha.htm"><code>READ-CHAR</code></a>
+will try to be helpful and return the corresponding Unicode code point -
+an integer - instead.  This might lead to an error if you're using
+functions
+like <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_lin.htm"><code>READ-LINE</code></a>, though.)
+
+<p>
+Whenever a FLEXI-STREAMS function accepts an external format as one of
+its arguments, you can provide either an <code>EXTERNAL-FORMAT</code>
+object or a shortcut which can be a list or a symbol.  The list
+shortcuts have a syntax similar
+to <a
+href="http://www.lispworks.com/documentation/lw50/LWUG/html/lwuser-360.htm">the
+one used by LispWorks</a> - the cars are the names of and encoding
+and the cdrs of these lists correspond to the keyword arguments
+to <a
+href="#make-external-format"><code>MAKE-EXTERNAL-FORMAT</code></a>, so
+for example
+<pre>(:latin-1 :eol-style :crlf)</pre> 
+is equivalent to 
+<pre>(<a class=noborder href="#make-external-format">make-external-format</a> :latin-1 :eol-style :crlf)</pre> The
+symbol shortcuts are equivalent to
+calling <a
+href="#make-external-format"><code>MAKE-EXTERNAL-FORMAT</code></a>
+without keyword arguments, i.e.
+<pre>:thai</pre>
+behaves like
+<pre>(<a class=noborder href="#make-external-format">make-external-format</a> :thai)</pre>
+Finally, the following expansions are
+available:
+<p>
+<table border=1>
+<tr><td><code>:UCS-2LE</code></td><td><code>(:UCS-2&nbsp;:LITTLE-ENDIAN&nbsp;T)</code></td></tr>
+<tr><td><code>:UCS-2BE</code></td><td><code>(:UCS-2&nbsp;:LITTLE-ENDIAN&nbsp;NIL)</code></td></tr>
+<tr><td><code>:UCS-4LE</code></td><td><code>(:UCS-4&nbsp;:LITTLE-ENDIAN&nbsp;T)</code></td></tr>
+<tr><td><code>:UCS-4BE</code></td><td><code>(:UCS-4&nbsp;:LITTLE-ENDIAN&nbsp;NIL)</code></td></tr>
+<tr><td><code>:UTF-16LE</code></td><td><code>(:UTF-16&nbsp;:LITTLE-ENDIAN&nbsp;T)</code></td></tr>
+<tr><td><code>:UTF-16BE</code></td><td><code>(:UTF-16&nbsp;:LITTLE-ENDIAN&nbsp;NIL)</code></td></tr>
+<tr><td><code>:UTF-32LE</code></td><td><code>(:UTF-32&nbsp;:LITTLE-ENDIAN&nbsp;T)</code></td></tr>
+<tr><td><code>:UTF-32BE</code></td><td><code>(:UTF-32&nbsp;:LITTLE-ENDIAN&nbsp;NIL)</code></td></tr>
+<tr><td><code>:IBM437</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;437)</code></td></tr>
+<tr><td><code>:IBM850</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;850)</code></td></tr>
+<tr><td><code>:IBM852</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;852)</code></td></tr>
+<tr><td><code>:IBM855</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;855)</code></td></tr>
+<tr><td><code>:IBM857</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;857)</code></td></tr>
+<tr><td><code>:IBM860</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;860)</code></td></tr>
+<tr><td><code>:IBM861</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;861)</code></td></tr>
+<tr><td><code>:IBM862</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;862)</code></td></tr>
+<tr><td><code>:IBM863</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;863)</code></td></tr>
+<tr><td><code>:IBM864</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;864)</code></td></tr>
+<tr><td><code>:IBM865</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;865)</code></td></tr>
+<tr><td><code>:IBM866</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;866)</code></td></tr>
+<tr><td><code>:IBM869</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;869)</code></td></tr>
+<tr><td><code>:WINDOWS-1250</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;1250)</code></td></tr>
+<tr><td><code>:WINDOWS-1251</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;1251)</code></td></tr>
+<tr><td><code>:WINDOWS-1252</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;1252)</code></td></tr>
+<tr><td><code>:WINDOWS-1253</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;1253)</code></td></tr>
+<tr><td><code>:WINDOWS-1254</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;1254)</code></td></tr>
+<tr><td><code>:WINDOWS-1255</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;1255)</code></td></tr>
+<tr><td><code>:WINDOWS-1256</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;1256)</code></td></tr>
+<tr><td><code>:WINDOWS-1257</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;1257)</code></td></tr>
+<tr><td><code>:WINDOWS-1258</code></td><td><code>(:CODE-PAGE&nbsp;:ID&nbsp;1258)</code></td></tr>
+</table>
+<p>
+Note that if you provide a shortcut, it
+will be converted to an <code>EXTERNAL-FORMAT</code> object first.
+So, if you're concerned about efficiency, create these objects once and
+re-use them.
+
+<p><br>[Function]
+<br><a class=none name="make-external-format"><b>make-external-format</b> <i>name <tt>&amp;key</tt> eol-style little-endian id</i> =&gt; <i>external-format</i></a>
+
+<blockquote><br> Creates and returns
+an <a href="#external-formats"><code>EXTERNAL-FORMAT</code>
+object</a>.  <code><i>name</i></code> is a
+symbol, <code><i>eol-style</i></code> is one of the
+keywords <code>:CR</code>, <code>:LF</code>, or <code>:CRLF</code>,
+and <code><i>little-endian</i></code> is
+a <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_g.htm#generalized_boolean">generalized
+boolean</a>.  The default value for <code><i>eol-style</i></code> is the value of <a href="#*default-eol-style*"><code>*DEFAULT-EOL-STYLE*</code></a> except for Windows code pages where it is <code>:CRLF</code>.  The default value
+for <code><i>little-endian</i></code> is the value of <a href="#*default-little-endian*"><code>*DEFAULT-LITTLE-ENDIAN*</code></a> - this value is ignored unless <code><i>name</i></code> denotes one of UTF-16 or UTF-32.
+<code><i>id</i></code> must be an integer denoting a Windows code page
+known by FLEXI-STREAMS if <code><i>name</i></code>
+is <code>:CODE-PAGE</code> or <code>WIN32:CODE-PAGE</code>, otherwise
+the value is ignored.  See <a href="#external-formats">the section
+about external formats</a> for more info.
+<p>
+Examples (run on Windows):
+
+<pre>
+CL-USER 1 > (make-external-format :latin-1)
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:ISO-8859-1 :EOL-STYLE :CRLF) 2067DA84&gt;
+
+CL-USER 2 &gt; (make-external-format :latin-1 :eol-style :lf)
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:ISO-8859-1 :EOL-STYLE :LF) 2068B4D4&gt;
+
+CL-USER 3 &gt; (make-external-format :ibm437)
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:CODE-PAGE :ID 437 :EOL-STYLE :CRLF) 2069B33C&gt;
+
+CL-USER 4 &gt; (make-external-format :ucs-2)
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:UTF-16 :EOL-STYLE :CRLF :LITTLE-ENDIAN T) 206B4F4C&gt;
+
+CL-USER 5 &gt; (make-external-format :ucs-2be)
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:UTF-16 :EOL-STYLE :CRLF :LITTLE-ENDIAN NIL) 2067DBE4&gt;
+
+CL-USER 6 > (make-external-format :ucs-2be :eol-style :cr)
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:UTF-16 :EOL-STYLE :CR :LITTLE-ENDIAN NIL) 206B54AC&gt;
+</pre>
+</blockquote>
+
+<p><br>[Readers]
+<br><a class=none name="external-format-name"><b>external-format-name</b> <i>external-format</i> =&gt; <i>name</i></a>
+<br><a class=none name="external-format-eol-style"><b>external-format-eol-style</b> <i>external-format</i> =&gt; <i>eol-style</i></a>
+<br><a class=none name="external-format-little-endian"><b>external-format-little-endian</b> <i>external-format</i> =&gt; <i>little-endian</i></a>
+<br><a class=none name="external-format-id"><b>external-format-id</b> <i>external-format</i> =&gt; <i>id</i></a>
+
+<blockquote><br>
+These methods can be used to query an <a href="#external-formats"><code>EXTERNAL-FORMAT</code> object</a> for its attributes.
+</blockquote>
+
+<p><br>[Functions]
+<br><a class=none name="external-format-equal"><b>external-format-equal</b> <i>external-format-1 external-format-2</i> =&gt; <i>generalized-boolean</i></a>
+
+<blockquote><br>
+Checks whether the two <a href="#external-formats">external formats</a> <code><i>external-format-1</i></code> and <code><i>external-format-2</i></code> are equivalent with respect to their effects on <a href="#flexi-streams">flexi streams</a>.
+<p>
+Examples (run on Windows):
+
+<pre>
+CL-USER 1 &gt; (<a href="#make-external-format" class=noborder>make-external-format</a> :ucs-4le)
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:UTF-32 :EOL-STYLE :CRLF :LITTLE-ENDIAN T) 2067FB74&gt;
+
+CL-USER 2 &gt; (external-format-equal <a href="http://www.lispworks.com/documentation/HyperSpec/Body/v__stst_.htm" class=noborder>*</a> (make-external-format :utf32 :little-endian t))
+T
+
+CL-USER 3 &gt; (make-external-format :code-page :id 437)
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:CODE-PAGE :ID 437 :EOL-STYLE :CRLF) 2069428C&gt;
+
+CL-USER 4 &gt; (external-format-equal * (make-external-format :ibm437))
+T
+</pre>
+
+</blockquote>
+
+<p><br>[Special variable]
+<br><a class=none name="*default-eol-style*"><b>*default-eol-style*</b></a>
+
+<blockquote><br>
+The default value for the <code><i>eol-style</i></code> keyword argument of <a href="#make-external-format"><code>MAKE-EXTERNAL-FORMAT</code></a>.  Its initial value is <code>:CRLF</code> on Windows and <code>:LF</code> on other operating systems.
+</blockquote>
+
+<p><br>[Special variable]
+<br><a class=none name="*default-little-endian*"><b>*default-little-endian*</b></a>
+
+<blockquote><br>
+The default value for the <code><i>little-endian</i></code> keyword argument of <a href="#make-external-format"><code>MAKE-EXTERNAL-FORMAT</code></a>.  Its initial value corresponds to the endianess of the platform FLEXI-STREAMS is used on as revealed by the <code>:LITTLE-ENDIAN</code> <a href="http://www.lispworks.com/documentation/HyperSpec/Body/24_ab.htm">feature</a>.
+</blockquote>
+
+<p><br>[Condition]
+<br><a class=none name="external-format-condition"><b>external-format-condition</b></a>
+
+<blockquote><br>
+All conditions related to <a href="#external-formats">external formats</a> are of this type.
+There's a slot for the external format which can be accessed with <a href="#external-format-condition-external-format"><code>EXTERNAL-FORMAT-CONDITION-EXTERNAL-FORMAT</code></a>.
+</blockquote>
+
+<p><br>[Reader]
+<br><a class=none name="external-format-condition-external-format"><b>external-format-condition-external-format</b> <i>condition</i> =&gt; <i>external-format</i></a>
+
+<blockquote><br> If <code><i>condition</i></code> is of
+type <a href="#external-format-condition"><code>EXTERNAL-FORMAT-CONDITION</code></a>,
+this function will return the associated external format.  Note that
+there are situation which happen during the creation of external
+formats where this method returns <code>NIL</code>.
+</blockquote>
+
+<p><br>[Condition]
+<br><a class=none name="external-format-error"><b>external-format-error</b></a>
+
+<blockquote><br>
+All errors related to <a href="#external-formats">external formats</a> are of this type.
+This is a subtype of <a href="#external-format-condition"><code>EXTERNAL-FORMAT-CONDITION</code></a>.
+</blockquote>
+
+<p><br>[Condition]
+<br><a class=none name="external-format-encoding-error"><b>external-format-encoding-error</b></a>
+
+<blockquote><br>
+All errors related to encoding problems with <a href="#external-formats">external formats</a> are of this type.  (This includes situation where an end of file is encountered in the middle of a multi-octet character.)  When this condition is signalled during reading, <a href="http://www.lispworks.com/documentation/HyperSpec/Body/r_use_va.htm"><code>USE-VALUE</code>
+restart</a> is provided. See also <a href="#*substitution-char*"><code>*SUBSTITUTION-CHAR*</code></a> and the example for it. <a href="#external-format-encoding-error"><code>EXTERNAL-FORMAT-ENCODING-ERROR</code></a> is a subtype of <a href="#external-format-error"><code>EXTERNAL-FORMAT-ERROR</code></a>.
+</blockquote>
+
+<p><br>[Special variable]
+<br><a class=none name="*substitution-char*"><b>*substitution-char*</b></a>
+
+<blockquote><br>
+If this value is not NIL, it should be a character which is used
+(as if by a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/r_use_va.htm"><code>USE-VALUE</code> restart</a>) whenever during reading an error of
+type <a href="#external-format-encoding-error"><code>EXTERNAL-FORMAT-ENCODING-ERROR</code></a> would have been signalled otherwise.
+
+<pre>
+CL-USER 1 &gt; (defun foo ()
+              <font color=orange>;; not a valid UTF-8 sequence</font>
+              (<a href="#with-input-from-sequence" class=noborder>with-input-from-sequence</a> (in '(#xe4 #xf6 #xfc))
+                (setq in (<a href="#make-flexi-stream" class=noborder>make-flexi-stream</a> in :external-format :utf8))
+                (read-line in)))
+FOO
+
+CL-USER 2 &gt; (foo)
+
+Error: Unexpected value #xF6 in UTF-8 sequence.
+  1 (continue) Specify a character to be used instead.
+  2 (abort) Return to level 0.
+  3 Return to top loop level 0.
+
+Type :b for backtrace, :c &lt;option number&gt; to proceed,  or :? for other options
+
+CL-USER 3 : 1 &gt; :c
+Type a character: x
+
+Error: End of file while in UTF-8 sequence.
+  1 (continue) Specify a character to be used instead.
+  2 (abort) Return to level 0.
+  3 Return to top loop level 0.
+
+Type :b for backtrace, :c &lt;option number&gt; to proceed,  or :? for other options
+
+CL-USER 4 : 1 &gt; :c
+Type a character: y
+"xy"
+T
+
+CL-USER 5 &gt; (<a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_handle.htm" class=noborder>handler-bind</a> ((<a href="#external-format-encoding-error" class=noborder>external-format-encoding-error</a> (lambda (condition)
+                                                          (<a href="http://www.lispworks.com/documentation/HyperSpec/Body/r_use_va.htm" class=noborder>use-value</a> #\-))))
+              (foo))
+"--"
+T
+
+CL-USER 6 &gt; (let ((<a href="#*SUBSTITUTION-CHAR*" class=noborder>*substitution-char*</a> #\?))
+              (foo))
+"??"
+T
+</pre>
+</blockquote>
+
+<p><br>[Restart]
+<br><a class=none name="accept-overlong-sequence"><b>accept-overlong-sequence</b></a>
+
+<blockquote><br> This is
+a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/09_adb.htm">restart</a>
+which is established whenever a UTF-8 "overlong" sequence is
+encountered.  If
+you <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_invo_1.htm">invoke</a>
+this restart, the corresponding code point will be accepted although
+it was encoded in an illegal way.
+</blockquote>
+
+<h4><a name="flexi-streams" class=none>Flexi streams</a></h4>
+
+<em>Flexi streams</em> are the core of the FLEXI-STREAMS library.  You
+create them using the
+function <a
+href="#make-flexi-stream"><code>MAKE-FLEXI-STREAM</code></a> which
+takes an open binary stream (called the <em>underlying</em> stream) as its only required argument.
+A <em>binary</em> stream in this context means that if it's an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_i.htm#input">input
+stream</a>, you can read from it with
+<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_by.htm"><code>READ-BYTE</code></a>
+(or, as a workaround for LispWorks, you can at least apply
+<a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_seq.htm"><code>READ-SEQUENCE</code></a>
+to it where the sequence is an array of element
+type <a href="#octet"><code>OCTET</code></a>), and similarly for 
+<a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_wr_by.htm#write-byte"><code>WRITE-BYTE</code></a>
+(<a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_wr_seq.htm"><code>WRITE-SEQUENCE</code></a>
+for LispWorks)
+and <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_o.htm#output">output
+streams</a>.  (Note that this specifically holds
+for <a
+href="http://www.lispworks.com/documentation/lw50/LWRM/html/lwref-91.htm"><em>bivalent</em>
+streams</a> like socket streams.)
+<p>
+A flexi stream behaves like an ordinary Lisp stream.  It is an input
+stream if the underlying binary stream is an input stream, and it is
+an output stream when the underlying binary stream is an output
+stream.  You can write characters as well
+as <a href="#octet">octets</a> to an output flexi stream and similarly
+you can read characters and octets from an input flexi stream.
+<p>
+A flexi stream always has an <a href="#external-formats">external
+format</a> associated with it which is deployed whenever you read
+characters from the stream or write characters to it.  You
+can <a href="#flexi-stream-external-format">change</a> the external
+format while you use the stream.
+<p>
+Once you're using a flexi stream you should <em>not</em> read from or
+write to the underlying stream directly anymore.
+<p>
+If
+you <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_close.htm">close</a>
+a flexi stream, the underlying stream will also be closed.  However, it
+also suffices to close the underlying stream directly should you not
+want to use the flexi stream anymore.  So, the following usage
+(where <code>IN</code> is implicitly closed at the end) is OK:
+<pre>
+(with-open-file (in "/foo/bar/baz.txt")
+  (let ((flexi (<a href="#make-flexi-stream" class=noborder>make-flexi-stream</a> in <a href="#external-formats" class=noborder>:external-format</a> :hebrew)))
+    (read-line flexi)))
+</pre>
+<p>
+Output flexi streams will try to keep track of
+the <a
+href="http://www.lispworks.com/documentation/lw50/LWRM/html/lwref-591.htm">column</a>
+they're in but you can also <a href="#flexi-stream-column">set</a> the
+column directly.  This value will be incremented by one for each
+character written to the stream and it will be set to <code>0</code>
+if you send a <code>#\Newline</code> character.  The column will be
+set to <code>NIL</code> if an <a href="#octet"><code>OCTET</code></a>
+is sent to the stream.  Once the column is <code>NIL</code> it'll stay
+like that unless it is explicitly set to another value.
+<p>
+Input flexi streams keep track of
+their <a href="#flexi-stream-position">position</a> within the stream.
+This value is incremented by one for
+each <a href="#octet"><code>OCTET</code></a> read from the stream, and
+it is incremented by the number of octets actually read for each
+character read from the stream.  So, if the encoding is UTF-8, reading
+the character <code>#\&auml;</code> (a-umlaut) will advance the position by two.
+If the encoding is UTF-32 and the end-of-line style
+is <code>:CRLF</code>, reading a <code>#\Newline</code> will advance
+the position by eight.
+<p>
+You can also set the <a href="#flexi-stream-bound">bound</a> of an
+input flexi stream.  Initially it is <code>NIL</code>, but when it's
+an integer and the
+stream's <a href="#flexi-stream-position">position</a> has gone beyond
+this bound, the stream will behave as if no more input is available.
+<p>
+Caveat: You can
+only <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_unrd_c.htm">unread</a>
+a character from a flexi stream if you haven't changed the external format after you read it.
+<p>
+Caveat: The <em>underlying</em> stream should either be a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_b.htm#binary">binary stream</a> (i.e. have an element type that is a subtype of integer) or it should explicitly use an <a href="http://www.lispworks.com/documentation/lw50/LWUG/html/lwuser-360.htm">external format</a> with <code>:LF</code> as its end-of-line style.  Otherwise it might perform unwanted conversion of line endings on its own.  (LispWorks <a href="http://article.gmane.org/gmane.lisp.lispworks.general/4859">does this</a> even if you write binary data to the stream using <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_wr_seq.htm"><code>WRITE-SEQUENCE</code></a>.)
+
+<p><br>[Standard class]
+<br><a class=none name="flexi-stream"><b>flexi-stream</b></a>
+
+<blockquote><br>
+Every <a href="#flexi-streams"><em>flexi stream</em></a> returned by <a href="#make-flexi-stream"><code>MAKE-FLEXI-STREAM</code></a> is of this type which is a subtype of <a href="http://www.lispworks.com/documentation/HyperSpec/Body/t_stream.htm"><code>STREAM</code></a>.
+</blockquote>
+
+<p><br>[Standard class]
+<br><a class=none name="flexi-input-stream"><b>flexi-input-stream</b></a>
+
+<blockquote><br>
+A <a href="#flexi-streams"><em>flexi stream</em></a> is of this type if its underlying stream is an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_in_stm.htm">input stream</a>.  This is a subtype of <a href="#flexi-stream"><code>FLEXI-STREAM</code></a>.
+</blockquote>
+
+<p><br>[Standard class]
+<br><a class=none name="flexi-output-stream"><b>flexi-output-stream</b></a>
+
+<blockquote><br>
+A <a href="#flexi-streams"><em>flexi stream</em></a> is of this type if its underlying stream is an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_in_stm.htm">output stream</a>.  This is a subtype of <a href="#flexi-stream"><code>FLEXI-STREAM</code></a>.
+</blockquote>
+
+<p><br>[Standard class]
+<br><a class=none name="flexi-io-stream"><b>flexi-io-stream</b></a>
+
+<blockquote><br>
+A <a href="#flexi-streams"><em>flexi stream</em></a> is of this type if it is both a <a href="#flexi-input-stream"><code>FLEXI-INPUT-STREAM</code></a> as well as a <a href="#flexi-output-stream"><code>FLEXI-OUTPUT-STREAM</code></a>.
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="make-flexi-stream"><b>make-flexi-stream</b> <i>stream <tt>&amp;key</tt> external-format element-type column position bound</i> =&gt; <i>flexi-stream</i></a>
+
+<blockquote><br>
+Creates and returns a <a href="#flexi-streams"><em>flexi stream</em></a>, i.e. an object of type <a href="#flexi-stream"><code>FLEXI-STREAM</code></a>.  <code><i>stream</i></code> is the underlying Lisp stream. <code><i>external-format</i></code> is the initial <a href="#external-formats">external format</a> to be used by the stream, the default is the value of evaluating <code>(<a href="#make-external-format">MAKE-EXTERNAL-FORMAT</a>&nbsp;:LATIN1)</code>.  <code><i>element-type</i></code> is the initial <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_stm_el.htm">element type</a> of the flexi stream the default of which is <a href="http://www.lispworks.com/documentation/lw50/LWRM/html/lwref-346.htm"><code>LW:SIMPLE-CHAR</code></a> for LispWorks and <a href="http://www.lispworks.com/documentation/HyperSpec/Body/t_ch.htm"><code>CHARACTER</code></a> otherwise. <code><i>column</i></code> is the initial column of the stream and should only be provided for output streams, the default is <code>0</code>.  <code><i>position</i></code> is the initial octet position of the stream and must only be provided for input streams, the default is <code>0</code>.  <code><i>bound</i></code> should be <code>NIL</code> (the default) or an integer and must only be provided for input streams.  If the octet position of the stream has gone beyond this bound, the stream will behave as if no more input is available.  See <a href="#flexi-streams">the section about flexi streams</a> for more information.
+</blockquote>
+
+<p><br>[Accessors]
+<br><a class=none name="flexi-stream-external-format"><b>flexi-stream-external-format</b> <i>flexi-stream</i> =&gt; <i>external-format</i></a>
+<br><tt>(setf (</tt><b>flexi-stream-external-format</b> <i>flexi-stream</i>) <i>external-format</i><tt>)</tt>
+<br><a class=none name="flexi-stream-element-type"><b>flexi-stream-element-type</b> <i>flexi-stream</i> =&gt; <i>element-type</i></a>
+<br><tt>(setf (</tt><b>flexi-stream-element-type</b> <i>flexi-stream</i>) <i>element-type</i><tt>)</tt>
+<br><a class=none name="flexi-stream-column"><b>flexi-stream-column</b> <i>flexi-output-stream</i> =&gt; <i>column</i></a>
+<br><tt>(setf (</tt><b>flexi-stream-column</b> <i>flexi-output-stream</i>) <i>column</i><tt>)</tt>
+<br><a class=none name="flexi-stream-position"><b>flexi-stream-position</b> <i>flexi-input-stream</i> =&gt; <i>position</i></a>
+<br><tt>(setf (</tt><b>flexi-stream-position</b> <i>flexi-input-stream</i>) <i>position</i><tt>)</tt>
+<br><a class=none name="flexi-stream-bound"><b>flexi-stream-bound</b> <i>flexi-input-stream</i> =&gt; <i>bound</i></a>
+<br><tt>(setf (</tt><b>flexi-stream-bound</b> <i>flexi-input-stream</i>) <i>bound</i><tt>)</tt>
+
+<blockquote><br>
+These methods can be used to get and set the corresponding attributes of a <a href="#flexi-streams">flexi stream</a>.
+<p>
+<a href="#flexi-stream-external-format"><code>(SETF
+FLEXI-STREAM-EXTERNAL-FORMAT)</code></a> accepts keyword symbols
+(<a href="#external-formats">names of external formats</a>), lists
+(which should be valid lists of parameters
+to <a
+href="#make-external-format"><code>MAKE-EXTERNAL-FORMAT</code></a>), or <code>EXTERNAL-FORMAT</code> objects:
+<pre>
+CL-USER 1 &gt; (setf (flexi-stream-external-format *my-stream*) :ucs-4le)
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:UTF-32 :EOL-STYLE :CRLF :LITTLE-ENDIAN T) 206920DC&gt;
+
+CL-USER 2 &gt; (setf (flexi-stream-external-format *my-stream*) '(:ucs-2be :eol-style :br))
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:UTF-16 :EOL-STYLE :BR :LITTLE-ENDIAN NIL) 20696934&gt;
+
+CL-USER 3 &gt; (setf (flexi-stream-external-format *my-stream*) (make-external-format :ibm437))
+#&lt;FLEXI-STREAMS::EXTERNAL-FORMAT (:CODE-PAGE :ID 437 :EOL-STYLE :CRLF) 2068716C&gt;
+</pre>
+</blockquote>
+
+<p><br>[Reader]
+<br><a class=none name="flexi-stream-stream"><b>flexi-stream-stream</b> <i>flexi-stream</i> =&gt; <i>stream</i></a>
+
+<blockquote><br>
+This method returns the underlying stream of a <a href="#flexi-streams">flexi stream</a>.
+</blockquote>
+
+<p><br>[Generic function]
+<br><a class=none name="unread-byte"><b>unread-byte</b> <i>byte stream</i> =&gt; <i>nil</i></a>
+
+<blockquote><br>
+Similar to <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_unrd_c.htm"><code>UNREAD-CHAR</code></a> in that it "unreads" the last <a href="#octet">octet</a> from
+<code><i>stream</i></code> which must be a <a href="#flexi-streams">flexi stream</a>.  Note that you can only call <code>UNREAD-BYTE</code> after a corresponding
+<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_by.htm"><code>READ-BYTE</code></a>, <em>not</em> after <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_cha.htm"><code>READ-CHAR</code></a>.
+</blockquote>
+
+<p><br>[Generic function]
+<br><a class=none name="peek-byte"><b>peek-byte</b> <i>stream <tt>&amp;optional</tt> peek-type eof-error-p eof-value</i> =&gt; <i>byte</i></a>
+
+<blockquote><br>
+<code>PEEK-BYTE</code> is like <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_peek_c.htm"><code>PEEK-CHAR</code></a>, i.e. it returns an <a href="#octet">octet</a> from <code><i>stream</i></code> (which must be a <a href="#flexi-streams">flexi stream</a>)
+without actually removing it.  If <code><i>peek-type</i></code> is <code>NIL</code>, the next octet is
+returned, if <code><i>peek-type</i></code> is <code>T</code>, the next octet which is not <code>0</code> is
+returned, if <code><i>peek-type</i></code> is an octet, the next octet which equals
+<code><i>peek-type</i></code> is returned.  <code><i>eof-error-p</i></code> and <code><i>eof-value</i></code> are interpreted as usual.
+<p>
+Note that the parameters aren't in the same order as with <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_peek_c.htm"><code>PEEK-CHAR</code></a> because it doesn't make much sense to make <code><i>stream</i></code> an optional argument.
+</blockquote>
+
+<p><br>[Type]
+<br><a class=none name="octet"><b>octet</b></a>
+
+<blockquote><br>
+Just a shortcut for <code>(UNSIGNED-BYTE&nbsp;8)</code>.
+</blockquote>
+
+<p><br>[Condition]
+<br><a class=none name="flexi-stream-error"><b>flexi-stream-error</b></a>
+
+<blockquote><br>
+All errors related to <a href="#flexi-streams">flexi streams</a> are of this type.  This is a subtype of <a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_stm_er.htm"><code>STREAM-ERROR</code></a>.
+</blockquote>
+
+<p><br>[Condition]
+<br><a class=none name="flexi-stream-out-of-sync-error"><b>flexi-stream-out-of-sync-error</b></a>
+
+<blockquote><br> This can happen if you're trying to write to
+an <a href="#flexi-io-stream">IO stream</a> which had prior to that
+"looked ahead" while reading and now can't "rewind" to the octet where
+you <em>should</em> be.
+</blockquote>
+
+<p><br>[Condition]
+<br><a class=none name="flexi-stream-element-type-error"><b>flexi-stream-element-type-error</b></a>
+
+<blockquote><br>
+All errors related to problems with the element type of <a href="#flexi-streams">flexi streams</a> are of this type.  This is a subtype of <a href="#flexi-stream-error"><code>FLEXI-STREAM-ERROR</code></a> and has an additional slot for the element type which can be accessed with <a href="#flexi-stream-element-type-error-element-type"><code>FLEXI-STREAM-ELEMENT-TYPE-ERROR-ELEMENT-TYPE</code></a>.
+</blockquote>
+
+<p><br>[Reader]
+<br><a class=none name="flexi-stream-element-type-error-element-type"><b>flexi-stream-element-type-error-element-type</b> <i>condition</i> =&gt; <i>element-type</i></a>
+
+<blockquote><br>
+If <code><i>condition</i></code> is of type <a href="#flexi-stream-element-type-error"><code>FLEXI-STREAM-ELEMENT-TYPE-ERROR</code></a>, this function will return the offending element type.
+</blockquote>
+
+<h4><a name="in-memory" class=none>In-memory streams</a></h4>
+
+The library also provides <em>in-memory</em> binary streams which are modeled after <a href="http://www.lispworks.com/documentation/HyperSpec/Body/t_stg_st.htm">string streams</a> and behave very similar only that they deal with <a href="#octet">octets</a> instead of characters and the underlying data structure is not a string but either a list or a vector.  These streams can obviously be used as the underlying streams for <a href="#flexi-streams">flexi streams</a>.
+
+<p><br>[Standard class]
+<br><a class=none name="in-memory-stream"><b>in-memory-stream</b></a>
+
+<blockquote><br>
+Every <a href="#in-memory"><em>in-memory stream</em></a> returned by <a href="#make-in-memory-input-stream"><code>MAKE-IN-MEMORY-INPUT-STREAM</code></a> or <a href="#make-in-memory-output-stream"><code>MAKE-IN-MEMORY-OUTPUT-STREAM</code></a> is of this type which is a subtype of <a href="http://www.lispworks.com/documentation/HyperSpec/Body/t_stream.htm"><code>STREAM</code></a>.
+</blockquote>
+
+<p><br>[Standard class]
+<br><a class=none name="in-memory-input-stream"><b>in-memory-input-stream</b></a>
+
+<blockquote><br>
+Every <a href="#in-memory"><em>in-memory stream</em></a> returned by <a href="#make-in-memory-input-stream"><code>MAKE-IN-MEMORY-INPUT-STREAM</code></a> is of this type which is a subtype of <a href="#in-memory-stream"><code>IN-MEMORY-STREAM</code></a>.
+</blockquote>
+
+<p><br>[Standard class]
+<br><a class=none name="in-memory-output-stream"><b>in-memory-output-stream</b></a>
+
+<blockquote><br>
+Every <a href="#in-memory"><em>in-memory stream</em></a> returned by <a href="#make-in-memory-output-stream"><code>MAKE-IN-MEMORY-OUTPUT-STREAM</code></a> is of this type which is a subtype of <a href="#in-memory-stream"><code>IN-MEMORY-STREAM</code></a>.
+</blockquote>
+
+<p><br>[Standard class]
+<br><a class=none name="list-stream"><b>list-stream</b></a>
+
+<blockquote><br>
+Every <a href="#in-memory"><em>in-memory input stream</em></a> is of this type if it reads from a list.
+</blockquote>
+
+<p><br>[Standard class]
+<br><a class=none name="vector-stream"><b>vector-stream</b></a>
+
+<blockquote><br>
+Every <a href="#in-memory"><em>in-memory stream</em></a> is of this type if it reads from or writes to a vector.
+</blockquote>
+
+<p><br>[Generic function]
+<br><a class=none name="make-in-memory-input-stream"><b>make-in-memory-input-stream</b> <i>sequence <tt>&amp;key</tt> start end transformer</i> =&gt; <i>in-memory-input-stream</i></a>
+
+<blockquote><br>
+Returns a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_b.htm#binary">binary</a> <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_i.htm#input">input</a> stream (of type <a href="#in-memory-input-stream"><code>IN-MEMORY-INPUT-STREAM</code></a>) which will supply, in order, the
+octets in the subsequence of <code><i>sequence</i></code> bounded by <code><i>start</i></code> (the default is <code>0</code>) and <code><i>end</i></code> (the default is the length of <code><i>sequence</i></code>).  <code><i>sequence</i></code> must either be a list or a vector of <a href="#octet">octets</a>.
+Each octet returned will be transformed in turn by the optional
+<code><i>transformer</i></code> function.
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="make-in-memory-output-stream"><b>make-in-memory-output-stream</b>  <i><tt>&amp;key</tt> element-type transformer</i> =&gt; <i>in-memory-output-stream</i></a>
+
+<blockquote><br>
+Returns a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_b.htm#binary">binary</a> <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_o.htm#output">output</a> stream (of type <a href="#in-memory-output-stream"><code>IN-MEMORY-OUTPUT-STREAM</code></a>) which accepts objects of type <code><i>element-type</i></code> (a subtype of <a href="#octet"><code>OCTET</code></a>) and makes
+available a sequence (see <a href="#get-output-stream-sequence"><code>GET-OUTPUT-STREAM-SEQUENCE</code></a>) that contains the octets that were actually
+output.  The octets stored will each be transformed by the optional <code><i>transformer</i></code> function.
+</blockquote>
+
+<p><br>[Generic function]
+<br><a class=none name="get-output-stream-sequence"><b>get-output-stream-sequence</b> <i>stream <tt>&amp;key</tt> as-list</i> =&gt; <i>sequence</i></a>
+
+<blockquote><br>
+Returns a vector containing, in order, all the octets that have
+been output to the <a href="#in-memory">in-memory output stream</a> <code><i>stream</i></code>. This operation clears any
+octets on <code><i>stream</i></code>, so the vector contains only those octets which have
+been output since the last call to <a href="#get-output-stream-sequence"><code>GET-OUTPUT-STREAM-SEQUENCE</code></a> or since
+the creation of the stream, whichever occurred most recently.  If
+<code><i>as-list</i></code> is true the return value is coerced to a list.
+</blockquote>
+
+<p><br>[Generic function]
+<br><a class=none name="output-stream-sequence-length"><b>output-stream-sequence-length</b> <i>stream</i> =&gt; <i>length</i></a>
+
+<blockquote><br> Returns the current length of the underlying vector
+of the <a href="#in-memory">in-memory output
+stream</a> <code><i>stream</i></code>, i.e. this is the length of the
+sequence that <a href="#get-output-stream-sequence"><code>GET-OUTPUT-STREAM-SEQUENCE</code></a> would return if called at
+this very moment.
+</blockquote>
+
+<p><br>[Macro]
+<br><a class=none name="with-input-from-sequence"><b>with-input-from-sequence</b> <i>(var sequence <tt>&amp;key</tt> start end transformer) statement*</i> =&gt; <i>result*</i></a>
+
+<blockquote><br> Creates an <a href="#in-memory">in-memory input
+stream</a> from the sequence <code><i>sequence</i></code> using the
+parameters <code><i>start</i></code> and <code><i>end</i></code>
+(see <a
+href="#make-in-memory-input-stream"><code>MAKE-IN-MEMORY-INPUT-STREAM</code></a>),
+binds <code><i>var</i></code> to this stream and then executes
+the <code><i>statement*</i></code> forms.  A
+function <code><i>transformer</i></code> may optionally be specified
+to transform the returned octets.  The stream is automatically closed
+on exit from
+<a href="#with-output-to-sequence"><code>WITH-OUTPUT-TO-SEQUENCE</code></a>, no matter whether the exit is normal or
+abnormal.  The return value of this macro is the return value of
+the last statement of <code><i>statement*</i></code>.
+</blockquote>
+
+<p><br>[Macro]
+<br><a class=none name="with-output-to-sequence"><b>with-output-to-sequence</b> <i>(var <tt>&amp;key</tt> as-list element-type transformer) statement*</i> =&gt; <i>sequence</i></a>
+
+<blockquote><br>
+Creates an <a href="#in-memory">in-memory output stream</a>, binds <code><i>var</i></code> to this stream and
+then executes the <code><i>statement*</i></code> forms.  The stream stores
+data of type <code><i>element-type</i></code> (a subtype of <a href="#octet"><code>OCTET</code></a>) which is (optionally) transformed by the
+function <code><i>transformer</i></code> prior to storage. The stream is automatically closed on
+exit from <a href="#with-output-to-sequence"><code>WITH-OUTPUT-TO-SEQUENCE</code></a>, no matter whether the exit is
+normal or abnormal.  The return value of this macro is a vector (or a
+list if <code><i>as-list</i></code> is true) containing the octets that were sent to the
+stream within the body of the macro.
+</blockquote>
+
+<p><br>[Condition]
+<br><a class=none name="in-memory-stream-error"><b>in-memory-stream-error</b></a>
+
+<blockquote><br>
+All errors related to <a href="#in-memory">in-memory streams</a> are of this type.  This is a subtype of <a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_stm_er.htm"><code>STREAM-ERROR</code></a>.
+</blockquote>
+
+<p><br>[Condition]
+<br><a class=none name="in-memory-stream-closed-error"><b>in-memory-stream-closed-error</b></a>
+
+<blockquote><br>
+An error of this type is signalled if one tries to read from or write to an <a href="#in-memory">in-memory stream</a> which had already been closed.  This is a subtype of <a href="#in-memory-stream-error"><code>IN-MEMORY-STREAM-ERROR</code></a>.
+</blockquote>
+
+<p><br>[Condition]
+<br><a class=none name="in-memory-stream-position-spec-error"><b>in-memory-stream-position-spec-error</b></a>
+
+<blockquote><br> Errors of this type are signalled if an erroneous
+position spec is used in conjunction
+with <a href="#position"><code>FILE-POSITION</code></a>.  This is a
+subtype
+of <a href="#in-memory-stream-error"><code>IN-MEMORY-STREAM-ERROR</code></a>
+and has an additional slot for the position spec which can be accessed
+with <a href="#in-memory-stream-position-spec-error-position-spec"><code>IN-MEMORY-STREAM-POSITION-SPEC-ERROR-POSITION-SPEC</code></a>.
+</blockquote>
+
+<p><br>[Reader]
+<br><a class=none name="in-memory-stream-position-spec-error-position-spec"><b>in-memory-stream-position-spec-error-position-spec</b> <i>condition</i> =&gt; <i>position-spec</i></a>
+
+<blockquote><br>
+If <code><i>condition</i></code> is of type <a href="#in-memory-stream-position-spec-error"><code>IN-MEMORY-STREAM-POSITION-SPEC-ERROR</code></a>, this function will return the offending position spec.
+</blockquote>
+
+<h4><a name="strings" class=none>Strings</a></h4>
+
+This section collects a few convenience functions for strings conversions.
+
+<p><br>[Function]
+<br><a class=none name="string-to-octets"><b>string-to-octets</b> <i>string <tt>&amp;key</tt> external-format start end</i> =&gt; <i>vector</i></a>
+
+<blockquote><br>
+
+Converts the Lisp string <code><i>string</i></code> from <code><i>start</i></code> to <code><i>end</i></code> to an array of
+<a href="#octet">octets</a> corresponding to the <a href="#external-formats">external
+format</a> designated by <code><i>external-format</i></code>. The defaults for
+<code><i>start</i></code> and <code><i>end</i></code>
+are <code>0</code> and the length of the string.  The default
+for <code><i>external-format</i></code> is <code>:LATIN1</code>.
+<p>
+In spite of the name, <code><i>string</i></code> can be any sequence of characters, but
+the function is optimized for strings.
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="octets-to-string"><b>octets-to-string</b> <i>sequence <tt>&amp;key</tt> external-format start end</i> =&gt; <i>string</i></a>
+
+<blockquote><br> Converts the Lisp
+sequence <code><i>sequence</i></code> of <a href="#octet">octets</a>
+from <code><i>start</i></code> to <code><i>end</i></code> to a string
+using the <a href="#external-formats">external format</a> designated
+by <code><i>external-format</i></code>.  The defaults for
+<code><i>start</i></code> and <code><i>end</i></code>
+are <code>0</code> and the length of the sequence.  The default
+for <code><i>external-format</i></code> is <code>:LATIN1</code>.
+<p>
+This function is optimized for the case
+of <code><i>sequence</i></code> being
+a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/t_vector.htm">vector</a>.
+Don't use lists if you are in hurry.
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="octet-length"><b>octet-length</b> <i>string <tt>&amp;key</tt> external-format start end</i> =&gt; <i>length</i></a>
+
+<blockquote><br>
+
+Returns the length of the subsequence of <code><i>string</i></code> from <code><i>start</i></code> to <code><i>end</i></code> in
+<a href="#octet">octets</a> if encoded using
+the <a href="#external-formats">external format</a> designated
+by <code><i>external-format</i></code>.
+The defaults for
+<code><i>start</i></code> and <code><i>end</i></code>
+are <code>0</code> and the length of <code><i>string</i></code>.  The default
+for <code><i>external-format</i></code> is <code>:LATIN1</code>.
+<p>
+In spite of the name, <code><i>string</i></code> can be any sequence of characters, but
+the function is optimized for strings.
+</blockquote>
+
+<p><br>[Function]
+<br><a class=none name="char-length"><b>char-length</b> <i>sequence <tt>&amp;key</tt> external-format start end</i> =&gt; <i>length</i></a>
+
+<blockquote><br>
+
+Kind of the inverse of <a href="#octet-length"><code>OCTET-LENGTH</code></a>.
+Returns the length of the subsequence (of <a href="#octet">octets</a>) of <code><i>sequence</i></code> from <code><i>start</i></code> to <code><i>end</i></code> in
+characters if decoded using
+the <a href="#external-formats">external format</a> designated
+by <code><i>external-format</i></code>.
+The defaults for
+<code><i>start</i></code> and <code><i>end</i></code>
+are <code>0</code> and the length of the sequence.  The default
+for <code><i>external-format</i></code> is <code>:LATIN1</code>.  Note that this function doesn't check for the validity of the data in <code><i>sequence</i></code>.
+<p>
+This function is optimized for the case
+of <code><i>sequence</i></code> being
+a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/t_vector.htm">vector</a>.
+Don't use lists if you are in hurry.
+</blockquote>
+
+<br>&nbsp;<br><h3><a class=none name="position">File positions</a></h3>
+
+For <a href="#flexi-streams">flexi streams</a> as well
+as for  <a href="#input-memory">in-memory
+streams</a>, <a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_file_p.htm">FILE-POSITION</a>
+will usually return <code>NIL</code> and do nothing when a second
+argument is supplied.  This is correct
+w.r.t. the <a
+href="http://www.lispworks.com/documentation/HyperSpec/">ANSI
+standard</a>, but not very helpful.  However, even
+with <a
+href="http://www.nhplace.com/kent/CL/Issues/stream-definition-by-user.html">Gray
+streams</a> there is no <em>portable</em> way to implement a better
+behaviour.
+<p>
+For <a href="http://www.lispworks.com/">LispWorks</a>
+and <a href="http://clisp.sf.net/">CLISP</a>,
+<a
+href="http://www.lispworks.com/documentation/HyperSpec/Body/f_file_p.htm">FILE-POSITION</a>
+for <a href="#flexi-streams">flexi streams</a> will work as if the
+function had been applied to the underlying stream, and
+for <a href="#input-memory">in-memory streams</a> it will try to do
+something sensible if the underlying data structure is a vector
+(i.e. <em>not</em> a list).  Patches for other Common Lisp
+implementations should be sent to
+the <a
+href="http://common-lisp.net/project/cl-plus-ssl/#trivial-gray-streams">trivial-gray-streams</a>
+maintainers.
+
+<br>&nbsp;<br><h3><a class=none name="ack">Acknowledgements</a></h3>
+
+Thanks to David Lichteblau for numerous portability patches.  Thanks
+to Igor Plekhov for the KOI8-R code.  Thanks to Anton Vodonosov for
+numerous patches and additions.  Thanks
+to <a href="http://netzhansa.blogspot.com/">Hans H&uuml;bner</a> for
+his work on making FLEXI-STREAMS faster.
+
+<p>
+$Header: /usr/local/cvsrep/flexi-streams/doc/index.html,v 1.126 2008/08/26 10:59:24 edi Exp $
+<p><a href="http://weitz.de/index.html">BACK TO MY HOMEPAGE</a>
+
+</body>
+</html>
diff --git a/deps/flexi-streams/encode.lisp b/deps/flexi-streams/encode.lisp
new file mode 100644 (file)
index 0000000..f04b04b
--- /dev/null
@@ -0,0 +1,282 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/encode.lisp,v 1.26 2008/05/26 10:55:08 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defgeneric char-to-octets (format char writer)
+  (declare #.*standard-optimize-settings*)
+  (:documentation "Converts the character CHAR to a sequence of octets
+using the external format FORMAT.  The conversion is performed by
+calling the unary function \(which must be a functional object) WRITER
+repeatedly each octet.  The return value of this function is
+unspecified."))
+
+(defgeneric write-sequence* (format stream sequence start end)
+  (declare #.*standard-optimize-settings*)
+  (:documentation "A generic function which dispatches on the external
+format and does the real work for STREAM-WRITE-SEQUENCE."))
+
+(defgeneric string-to-octets* (format string start end)
+  (declare #.*standard-optimize-settings*)
+  (:documentation "A generic function which dispatches on the external
+format and does the real work for STRING-TO-OCTETS."))
+
+(defmethod string-to-octets* :around (format (list list) start end)
+  (declare #.*standard-optimize-settings*)
+  (string-to-octets* format (coerce list 'string*) start end))
+
+(defmacro define-sequence-writers ((format-class) &body body)
+  "Non-hygienic utility macro which defines methods for
+WRITE-SEQUENCE* and STRING-TO-OCTETS* for the class FORMAT-CLASS.  For
+BODY see the docstring of DEFINE-CHAR-ENCODERS."
+  (let ((body `((locally
+                  (declare #.*fixnum-optimize-settings*)
+                  ,@body))))
+    `(progn
+       (defmethod string-to-octets* ((format ,format-class) string start end)
+         (declare #.*standard-optimize-settings*)
+         (declare (fixnum start end) (string string))
+         (let ((octets (make-array (compute-number-of-octets format string start end)
+                                   :element-type 'octet))
+               (j 0))
+           (declare (fixnum j))
+           (loop for i of-type fixnum from start below end do
+                 (macrolet ((octet-writer (form)
+                              `(progn
+                                 (setf (aref (the (array octet *) octets) j) ,form)
+                                 (incf j))))
+                   (symbol-macrolet ((char-getter (char string i)))
+                     (progn ,@body))))
+           octets))
+       (defmethod write-sequence* ((format ,format-class) stream sequence start end)
+         (declare #.*standard-optimize-settings*)
+         (declare (fixnum start end))
+         (with-accessors ((column flexi-stream-column))
+             stream
+           (let* ((octet-seen-p nil)
+                  (buffer-pos 0)
+                  ;; estimate should be good enough...
+                  (factor (encoding-factor format))
+                  ;; we don't want arbitrarily large buffer, do we?
+                  (buffer-size (min +buffer-size+ (ceiling (* factor (- end start)))))
+                  (buffer (make-octet-buffer buffer-size))
+                  (underlying-stream (flexi-stream-stream stream)))
+             (declare (fixnum buffer-pos buffer-size)
+                      (boolean octet-seen-p)
+                      (type (array octet *) buffer))
+             (macrolet ((octet-writer (form)
+                          `(write-octet ,form)))
+               (labels ((flush-buffer ()
+                          "Sends all octets in BUFFER to the underlying stream."
+                          (write-sequence buffer underlying-stream :end buffer-pos)
+                          (setq buffer-pos 0))
+                        (write-octet (octet)
+                          "Adds one octet to the buffer and flushes it if necessary."
+                          (declare (type octet octet))
+                          (when (>= buffer-pos buffer-size)
+                            (flush-buffer))
+                          (setf (aref buffer buffer-pos) octet)
+                          (incf buffer-pos))
+                        (write-object (object)
+                          "Dispatches to WRITE-OCTET or WRITE-CHARACTER
+depending on the type of OBJECT."
+                          (etypecase object
+                            (octet (setq octet-seen-p t)
+                                   (write-octet object))
+                            (character (symbol-macrolet ((char-getter object))
+                                         ,@body)))))
+                 (macrolet ((iterate (&body output-forms)
+                              "An unhygienic macro to implement the actual
+iteration through SEQUENCE.  OUTPUT-FORM is the form to retrieve one
+sequence element and put its octet representation into the buffer."
+                              `(loop for index of-type fixnum from start below end
+                                     do (progn ,@output-forms)
+                                     finally (when (plusp buffer-pos)
+                                               (flush-buffer)))))
+                   (etypecase sequence
+                     (string (iterate
+                              (symbol-macrolet ((char-getter (char sequence index)))
+                                ,@body)))
+                     (array (iterate
+                             (symbol-macrolet ((char-getter (aref sequence index)))
+                               ,@body)))
+                     (list  (iterate (write-object (nth index sequence))))))
+                 ;; update the column slot, setting it to NIL if we sent
+                 ;; octets
+                 (setq column
+                       (cond (octet-seen-p nil)
+                             (t (let ((last-newline-pos (position #\Newline sequence
+                                                                  :test #'char=
+                                                                  :start start
+                                                                  :end end
+                                                                  :from-end t)))
+                                  (cond (last-newline-pos (- end last-newline-pos 1))
+                                        (column (+ column (- end start))))))))))))))))
+
+(defmacro define-char-encoders ((lf-format-class cr-format-class crlf-format-class) &body body)
+  "Non-hygienic utility macro which defines several encoding-related
+methods for the classes LF-FORMAT-CLASS, CR-FORMAT-CLASS, and
+CRLF-FORMAT-CLASS where it is assumed that CR-FORMAT-CLASS is the same
+encoding as LF-FORMAT-CLASS but with CR instead of LF line endings and
+similar for CRLF-FORMAT-CLASS, i.e. LF-FORMAT-CLASS is the base class.
+BODY is a code template for the code to convert one character to
+octets.  BODY must contain a symbol CHAR-GETTER representing the form
+which is used to obtain the character and a forms like \(OCTET-WRITE
+<thing>) to write the octet <thing>.  The CHAR-GETTER form might be
+called more than once."
+  `(progn
+     (defmethod char-to-octets ((format ,lf-format-class) char writer)
+       (declare #.*fixnum-optimize-settings*)
+       (declare (character char) (function writer))
+       (symbol-macrolet ((char-getter char))
+         (macrolet ((octet-writer (form)
+                      `(funcall writer ,form)))
+           ,@body)))
+     (define-sequence-writers (,lf-format-class) ,@body)
+     (define-sequence-writers (,cr-format-class)
+       ;; modify the body so that the getter replaces a #\Newline
+       ;; with a #\Return
+       ,@(sublis `((char-getter . ,(with-unique-names (char)
+                                     `(let ((,char char-getter))
+                                        (declare (character ,char))
+                                        (if (char= ,char #\Newline)
+                                          #\Return
+                                          ,char)))))
+                 body))
+     (define-sequence-writers (,crlf-format-class)
+       ;; modify the body so that we potentially write octets for
+       ;; two characters (#\Return and #\Linefeed) - the original
+       ;; body is wrapped with the WRITE-CHAR local function
+       ,(with-unique-names (char write-char)
+          `(flet ((,write-char (,char)
+                    ,@(sublis `((char-getter . ,char)) body)))
+             (let ((,char char-getter))
+               (declare (character ,char))
+               (cond ((char= ,char #\Newline)
+                      (,write-char #\Return)
+                      (,write-char #\Linefeed))
+                     (t (,write-char ,char)))))))))
+
+(define-char-encoders (flexi-latin-1-format flexi-cr-latin-1-format  flexi-crlf-latin-1-format)
+  (let ((octet (char-code char-getter)))
+    (when (> octet 255)
+      (signal-encoding-error format "~S (code ~A) is not a LATIN-1 character." char-getter octet))
+    (octet-writer octet)))
+
+(define-char-encoders (flexi-ascii-format flexi-cr-ascii-format flexi-crlf-ascii-format)
+  (let ((octet (char-code char-getter)))
+    (when (> octet 127)
+      (signal-encoding-error format "~S (code ~A) is not an ASCII character." char-getter octet))
+    (octet-writer octet)))
+
+(define-char-encoders (flexi-8-bit-format flexi-cr-8-bit-format flexi-crlf-8-bit-format)
+  (with-accessors ((encoding-hash external-format-encoding-hash))
+      format
+    (let ((octet (gethash (char-code char-getter) encoding-hash)))
+      (unless octet
+        (signal-encoding-error format "~S (code ~A) is not in this encoding." char-getter octet))
+      (octet-writer octet))))
+
+(define-char-encoders (flexi-utf-8-format flexi-cr-utf-8-format flexi-crlf-utf-8-format)
+  ;; the old version using LDB was more elegant, but some Lisps had
+  ;; trouble optimizing it
+  (let ((char-code (char-code char-getter)))
+    (tagbody
+     (cond ((< char-code #x80)
+            (octet-writer char-code)
+            (go zero))
+           ((< char-code #x800)
+            (octet-writer (logior* #b11000000 (ash* char-code -6)))
+            (go one))
+           ((< char-code #x10000)
+            (octet-writer (logior* #b11100000 (ash* char-code -12)))
+            (go two))
+           (t
+            (octet-writer (logior* #b11110000 (ash* char-code -18)))))
+     (octet-writer (logior* #b10000000 (logand* #b00111111 (ash* char-code -12))))
+     two
+     (octet-writer (logior* #b10000000 (logand* #b00111111 (ash* char-code -6))))
+     one
+     (octet-writer (logior* #b10000000 (logand* #b00111111 char-code)))
+     zero)))
+
+(define-char-encoders (flexi-utf-16-le-format flexi-cr-utf-16-le-format flexi-crlf-utf-16-le-format)
+  (flet ((write-word (word)
+           (octet-writer (logand* #x00ff word))
+           (octet-writer (ash* (logand* #xff00 word) -8))))
+    (declare (inline write-word))
+    (let ((char-code (char-code char-getter)))
+      (declare (type char-code-integer char-code))
+      (cond ((< char-code #x10000)
+             (write-word char-code))
+            (t (decf char-code #x10000)
+               (write-word (logior* #xd800 (ash* char-code -10)))
+               (write-word (logior* #xdc00 (logand* #x03ff char-code))))))))
+
+(define-char-encoders (flexi-utf-16-be-format flexi-cr-utf-16-be-format flexi-crlf-utf-16-be-format)
+  (flet ((write-word (word)
+           (octet-writer (ash* (logand* #xff00 word) -8))
+           (octet-writer (logand* #x00ff word))))
+    (declare (inline write-word))
+    (let ((char-code (char-code char-getter)))
+      (declare (type char-code-integer char-code))
+      (cond ((< char-code #x10000)
+             (write-word char-code))
+            (t (decf char-code #x10000)
+               (write-word (logior* #xd800 (ash* char-code -10)))
+               (write-word (logior* #xdc00 (logand* #x03ff char-code))))))))
+
+(define-char-encoders (flexi-utf-32-le-format flexi-cr-utf-32-le-format flexi-crlf-utf-32-le-format)
+  (let ((char-code (char-code char-getter)))
+    (octet-writer (logand* #x00ff char-code))
+    (octet-writer (logand* #x00ff (ash* char-code -8)))
+    (octet-writer (logand* #x00ff (ash* char-code -16)))
+    (octet-writer (logand* #x00ff (ash* char-code -24)))))
+
+(define-char-encoders (flexi-utf-32-be-format flexi-cr-utf-32-be-format flexi-crlf-utf-32-be-format)
+  (let ((char-code (char-code char-getter)))
+    (octet-writer (logand* #x00ff (ash* char-code -24)))
+    (octet-writer (logand* #x00ff (ash* char-code -16)))
+    (octet-writer (logand* #x00ff (ash* char-code -8)))
+    (octet-writer (logand* #x00ff char-code))))
+
+(defmethod char-to-octets ((format flexi-cr-mixin) char writer)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (character char))
+  (if (char= char #\Newline)
+    (call-next-method format #\Return writer)
+    (call-next-method)))
+
+(defmethod char-to-octets ((format flexi-crlf-mixin) char writer)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (character char))
+  (cond ((char= char #\Newline)
+         (call-next-method format #\Return writer)
+         (call-next-method format #\Linefeed writer))
+        (t (call-next-method))))
diff --git a/deps/flexi-streams/external-format.lisp b/deps/flexi-streams/external-format.lisp
new file mode 100644 (file)
index 0000000..8fa7c41
--- /dev/null
@@ -0,0 +1,389 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/external-format.lisp,v 1.24 2008/05/26 10:55:08 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defclass external-format ()
+  ((name :initarg :name
+         :reader external-format-name
+         :documentation "The name of the external format - a
+keyword.")
+   (id :initarg :id
+       :initform nil
+       :reader external-format-id
+       :documentation "If the external format denotes a Windows
+code page this ID specifies which one to use.  Otherwise the
+value is ignored \(and usually NIL).")
+   (little-endian :initarg :little-endian
+                  :initform *default-little-endian*
+                  :reader external-format-little-endian
+                  :documentation "Whether multi-octet values are
+read and written with the least significant octet first.  For
+8-bit encodings like :ISO-8859-1 this value is ignored.")
+   (eol-style :initarg :eol-style
+              :reader external-format-eol-style
+              :documentation "The character\(s) to or from which
+a #\Newline will be translated - one of the keywords :CR, :LF,
+or :CRLF."))
+  (:documentation "EXTERNAL-FORMAT objects are used to denote
+encodings for flexi streams or for the string functions defined in
+strings.lisp."))
+
+(defmethod make-load-form ((thing external-format) &optional environment)
+  "Defines a way to reconstruct external formats.  Needed for OpenMCL."
+  (make-load-form-saving-slots thing :environment environment))
+
+(defclass flexi-cr-mixin ()
+  ()
+  (:documentation "A mixin for external-formats where the end-of-line
+designator is #\Return."))
+
+(defclass flexi-crlf-mixin ()
+  ()
+  (:documentation "A mixin for external-formats where the end-of-line
+designator is the sequence #\Return #\Linefeed."))
+
+(defclass flexi-8-bit-format (external-format)
+  ((encoding-hash :accessor external-format-encoding-hash)
+   (decoding-table :accessor external-format-decoding-table))
+  (:documentation "The class for all flexi streams which use an 8-bit
+encoding and thus need additional slots for the encoding/decoding
+tables."))
+
+(defclass flexi-cr-8-bit-format (flexi-cr-mixin flexi-8-bit-format)
+  ()
+  (:documentation "Special class for external formats which use an
+8-bit encoding /and/ have #\Return as the line-end character."))
+
+(defclass flexi-crlf-8-bit-format (flexi-crlf-mixin flexi-8-bit-format)
+  ()
+  (:documentation "Special class for external formats which use an
+8-bit encoding /and/ have the sequence #\Return #\Linefeed as the
+line-end character."))
+
+(defclass flexi-ascii-format (flexi-8-bit-format)
+  ()
+  (:documentation "Special class for external formats which use the
+US-ASCII encoding."))
+
+(defclass flexi-cr-ascii-format (flexi-cr-mixin flexi-ascii-format)
+  ()
+  (:documentation "Special class for external formats which use the
+US-ASCII encoding /and/ have #\Return as the line-end character."))
+
+(defclass flexi-crlf-ascii-format (flexi-crlf-mixin flexi-ascii-format)
+  ()
+  (:documentation "Special class for external formats which use the
+US-ASCII encoding /and/ have the sequence #\Return #\Linefeed as the
+line-end character."))
+
+(defclass flexi-latin-1-format (flexi-8-bit-format)
+  ()
+  (:documentation "Special class for external formats which use the
+ISO-8859-1 encoding."))
+
+(defclass flexi-cr-latin-1-format (flexi-cr-mixin flexi-latin-1-format)
+  ()
+  (:documentation "Special class for external formats which use the
+ISO-8859-1 encoding /and/ have #\Return as the line-end character."))
+
+(defclass flexi-crlf-latin-1-format (flexi-crlf-mixin flexi-latin-1-format)
+  ()
+  (:documentation "Special class for external formats which use the
+ISO-8859-1 encoding /and/ have the sequence #\Return #\Linefeed as the
+line-end character."))
+
+(defclass flexi-utf-32-format (external-format)
+  ()
+  (:documentation "Abstract class for external formats which use the
+UTF-32 encoding."))
+
+(defclass flexi-utf-32-le-format (flexi-utf-32-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-32 encoding with little-endian byte ordering."))
+
+(defclass flexi-cr-utf-32-le-format (flexi-cr-mixin flexi-utf-32-le-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-32 encoding with little-endian byte ordering /and/ have #\Return
+as the line-end character."))
+
+(defclass flexi-crlf-utf-32-le-format (flexi-crlf-mixin flexi-utf-32-le-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-32 encoding with little-endian byte ordering /and/ have the
+sequence #\Return #\Linefeed as the line-end character."))
+
+(defclass flexi-utf-32-be-format (flexi-utf-32-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-32 encoding with big-endian byte ordering."))
+
+(defclass flexi-cr-utf-32-be-format (flexi-cr-mixin flexi-utf-32-be-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-32 encoding with big-endian byte ordering /and/ have #\Return as
+the line-end character."))
+
+(defclass flexi-crlf-utf-32-be-format (flexi-crlf-mixin flexi-utf-32-be-format)
+  ()
+  (:documentation "Special class for external formats which use the
+the UTF-32 encoding with big-endian byte ordering /and/ have the
+sequence #\Return #\Linefeed as the line-end character."))
+
+(defclass flexi-utf-16-format (external-format)
+  ()
+  (:documentation "Abstract class for external formats which use the
+UTF-16 encoding."))
+
+(defclass flexi-utf-16-le-format (flexi-utf-16-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-16 encoding with little-endian byte ordering."))
+
+(defclass flexi-cr-utf-16-le-format (flexi-cr-mixin flexi-utf-16-le-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-16 encoding with little-endian byte ordering /and/ have #\Return
+as the line-end character."))
+
+(defclass flexi-crlf-utf-16-le-format (flexi-crlf-mixin flexi-utf-16-le-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-16 encoding with little-endian byte ordering /and/ have the
+sequence #\Return #\Linefeed as the line-end character."))
+
+(defclass flexi-utf-16-be-format (flexi-utf-16-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-16 encoding with big-endian byte ordering."))
+
+(defclass flexi-cr-utf-16-be-format (flexi-cr-mixin flexi-utf-16-be-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-16 encoding with big-endian byte ordering /and/ have #\Return as
+the line-end character."))
+
+(defclass flexi-crlf-utf-16-be-format (flexi-crlf-mixin flexi-utf-16-be-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-16 encoding with big-endian byte ordering /and/ have the sequence
+#\Return #\Linefeed as the line-end character."))
+
+(defclass flexi-utf-8-format (external-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-8 encoding."))
+
+(defclass flexi-cr-utf-8-format (flexi-cr-mixin flexi-utf-8-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-8 encoding /and/ have #\Return as the line-end character."))
+
+(defclass flexi-crlf-utf-8-format (flexi-crlf-mixin flexi-utf-8-format)
+  ()
+  (:documentation "Special class for external formats which use the
+UTF-8 encoding /and/ have the sequence #\Return #\Linefeed as the
+line-end character."))
+
+(defmethod initialize-instance :after ((external-format flexi-8-bit-format) &rest initargs)
+  "Sets the fixed encoding/decoding tables for this particular
+external format."
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore initargs))
+  (with-accessors ((encoding-hash external-format-encoding-hash)
+                   (decoding-table external-format-decoding-table)
+                   (name external-format-name)
+                   (id external-format-id))
+      external-format
+    (multiple-value-setq (encoding-hash decoding-table)
+        (cond ((ascii-name-p name)
+               (values +ascii-hash+ +ascii-table+))
+              ((koi8-r-name-p name)
+               (values +koi8-r-hash+ +koi8-r-table+))
+              ((iso-8859-name-p name)
+               (values (cdr (assoc name +iso-8859-hashes+ :test #'eq))                       
+                       (cdr (assoc name +iso-8859-tables+ :test #'eq))))
+              ((code-page-name-p name)
+               (values (cdr (assoc id +code-page-hashes+))                       
+                       (cdr (assoc id +code-page-tables+))))))))
+
+(defun external-format-class-name (real-name &key eol-style little-endian id)
+  "Given the initargs for a general external format returns the name
+\(a symbol) of the most specific subclass matching these arguments."
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore id))
+  (cond ((ascii-name-p real-name)
+         (ecase eol-style
+           (:lf 'flexi-ascii-format)
+           (:cr 'flexi-cr-ascii-format)
+           (:crlf 'flexi-crlf-ascii-format)))
+        ((eq real-name :iso-8859-1)
+         (ecase eol-style
+           (:lf 'flexi-latin-1-format)
+           (:cr 'flexi-cr-latin-1-format)
+           (:crlf 'flexi-crlf-latin-1-format)))
+        ((or (koi8-r-name-p real-name)
+             (iso-8859-name-p real-name)
+             (code-page-name-p real-name))
+         (ecase eol-style
+           (:lf 'flexi-8-bit-format)
+           (:cr 'flexi-cr-8-bit-format)
+           (:crlf 'flexi-crlf-8-bit-format)))
+        (t (ecase real-name
+             (:utf-8 (ecase eol-style
+                       (:lf 'flexi-utf-8-format)
+                       (:cr 'flexi-cr-utf-8-format)
+                       (:crlf 'flexi-crlf-utf-8-format)))
+             (:utf-16 (ecase eol-style
+                        (:lf (if little-endian
+                               'flexi-utf-16-le-format
+                               'flexi-utf-16-be-format))
+                        (:cr (if little-endian
+                               'flexi-cr-utf-16-le-format
+                               'flexi-cr-utf-16-be-format))
+                        (:crlf (if little-endian
+                                 'flexi-crlf-utf-16-le-format
+                                 'flexi-crlf-utf-16-be-format))))
+             (:utf-32 (ecase eol-style
+                        (:lf (if little-endian
+                               'flexi-utf-32-le-format
+                               'flexi-utf-32-be-format))
+                        (:cr (if little-endian
+                               'flexi-cr-utf-32-le-format
+                               'flexi-cr-utf-32-be-format))
+                        (:crlf (if little-endian
+                                 'flexi-crlf-utf-32-le-format
+                                 'flexi-crlf-utf-32-be-format))))))))
+                         
+(defun make-external-format% (name &key (little-endian *default-little-endian*)
+                                   id eol-style)
+  "Used internally by MAKE-EXTERNAL-FORMAT to default some of the
+keywords arguments and to determine the right subclass of
+EXTERNAL-FORMAT."
+  (declare #.*standard-optimize-settings*)
+  (let* ((real-name (normalize-external-format-name name))
+         (initargs
+          (cond ((or (iso-8859-name-p real-name)
+                    (koi8-r-name-p real-name)
+                     (ascii-name-p real-name))
+                 (list :eol-style (or eol-style *default-eol-style*)))
+                ((code-page-name-p real-name)
+                 (list :id (or (known-code-page-id-p id)
+                               (error 'external-format-error
+                                      :format-control "Unknown code page ID ~S"
+                                      :format-arguments (list id)))
+                       ;; default EOL style for Windows code pages is :CRLF
+                       :eol-style (or eol-style :crlf)))
+                (t (list :eol-style (or eol-style *default-eol-style*)
+                         :little-endian little-endian)))))
+    (apply #'make-instance (apply #'external-format-class-name real-name initargs)
+           :name real-name
+           initargs)))
+
+(defun make-external-format (name &rest args
+                                  &key (little-endian *default-little-endian*)
+                                       id eol-style)
+  "Creates and returns an external format object as specified.
+NAME is a keyword like :LATIN1 or :UTF-8, LITTLE-ENDIAN specifies
+the `endianess' of the external format and is ignored for 8-bit
+encodings, EOL-STYLE is one of the keywords :CR, :LF, or :CRLF
+which denote the end-of-line character \(sequence), ID is the ID
+of a Windows code page \(and ignored for other encodings)."
+  (declare #.*standard-optimize-settings*)
+  ;; the keyword arguments are only there for arglist display in the IDE
+  (declare (ignore id little-endian))
+  (let ((shortcut-args (cdr (assoc name +shortcut-map+ :test #'string-equal))))
+    (cond (shortcut-args
+           (apply #'make-external-format%
+                  (append shortcut-args
+                          `(:eol-style ,eol-style))))
+          (t (apply #'make-external-format% name args)))))
+
+(defun maybe-convert-external-format (external-format)
+  "Given an external format designator \(a keyword, a list, or an
+EXTERNAL-FORMAT object) returns the corresponding EXTERNAL-FORMAT
+object."
+  (declare #.*standard-optimize-settings*)
+  (typecase external-format
+    (symbol (make-external-format external-format))
+    (list (apply #'make-external-format external-format))
+    (otherwise external-format)))
+  
+(defun external-format-equal (ef1 ef2)
+  "Checks whether two EXTERNAL-FORMAT objects denote the same encoding."
+  (declare #.*standard-optimize-settings*)
+  (let* ((name1 (external-format-name ef1))
+         (code-page-name-p (code-page-name-p name1)))
+    ;; they must habe the same canonical name
+    (and (eq name1
+             (external-format-name ef2))
+         ;; if both are code pages the IDs must be the same
+         (or (not code-page-name-p)
+             (eql (external-format-id ef1)
+                  (external-format-id ef2)))
+         ;; for non-8-bit encodings the endianess must be the same
+         (or code-page-name-p
+             (ascii-name-p name1)
+            (koi8-r-name-p name1)
+             (iso-8859-name-p name1)
+             (eq name1 :utf-8)
+             (eq (not (external-format-little-endian ef1))
+                 (not (external-format-little-endian ef2))))
+         ;; the EOL style must also be the same
+         (eq (external-format-eol-style ef1)
+             (external-format-eol-style ef2)))))
+
+(defun normalize-external-format (external-format)
+  "Returns a list which is a `normalized' representation of the
+external format EXTERNAL-FORMAT.  Used internally by PRINT-OBJECT, for
+example.  Basically, the result is an argument list that can be fed
+back to MAKE-EXTERNAL-FORMAT to create an equivalent object."
+  (declare #.*standard-optimize-settings*)
+  (let ((name (external-format-name external-format))
+        (eol-style (external-format-eol-style external-format)))
+    (cond ((or (ascii-name-p name)
+               (koi8-r-name-p name)
+               (iso-8859-name-p name)
+               (eq name :utf-8))
+           (list name :eol-style eol-style))
+          ((code-page-name-p name)
+           (list name
+                 :id (external-format-id external-format)
+                 :eol-style eol-style))
+          (t (list name
+                   :eol-style eol-style
+                   :little-endian (external-format-little-endian external-format))))))
+
+(defmethod print-object ((object external-format) stream)
+  "How an EXTERNAL-FORMAT object is rendered.  Uses
+NORMALIZE-EXTERNAL-FORMAT."
+  (print-unreadable-object (object stream :type t :identity t)
+    (prin1 (normalize-external-format object) stream)))
diff --git a/deps/flexi-streams/flexi-streams.asd b/deps/flexi-streams/flexi-streams.asd
new file mode 100644 (file)
index 0000000..025fcc2
--- /dev/null
@@ -0,0 +1,73 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/flexi-streams.asd,v 1.79 2008/08/26 10:59:22 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(defpackage :flexi-streams-system
+  (:use :asdf :cl))
+
+(in-package :flexi-streams-system)
+
+(defsystem :flexi-streams
+  :version "1.0.15"
+  :serial t
+  :description "Flexible bivalent streams for Common Lisp"
+  :components ((:file "packages")
+               (:file "mapping")
+               (:file "ascii")
+               (:file "koi8-r")
+               (:file "iso-8859")
+               (:file "code-pages")
+               (:file "specials")
+               (:file "util")
+               (:file "conditions")
+               (:file "external-format")
+               (:file "length")
+               (:file "encode")
+               (:file "decode")
+               (:file "in-memory")
+               (:file "stream")
+               #+:lispworks (:file "lw-char-stream")
+               (:file "output")
+               (:file "input")
+               (:file "io")
+               (:file "strings"))
+  :depends-on (:trivial-gray-streams))
+
+(defsystem :flexi-streams-test
+  :components ((:module "test"
+                        :serial t
+                        :components ((:file "packages")
+                                     (:file "test"))))
+  :depends-on (:flexi-streams))
+
+(defmethod perform ((o test-op) (c (eql (find-system 'flexi-streams))))
+  (operate 'load-op 'flexi-streams-test)
+  (funcall (intern (symbol-name :run-all-tests)
+                   (find-package :flexi-streams-test))))
diff --git a/deps/flexi-streams/in-memory.lisp b/deps/flexi-streams/in-memory.lisp
new file mode 100644 (file)
index 0000000..10484cc
--- /dev/null
@@ -0,0 +1,406 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/in-memory.lisp,v 1.31 2008/05/19 07:57:07 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defclass in-memory-stream (trivial-gray-stream-mixin)
+  ((transformer :initarg :transformer
+                :accessor in-memory-stream-transformer
+                :documentation "A function used to transform the
+written/read octet to the value stored/retrieved in/from the
+underlying vector.")   
+   #+:cmu
+   (open-p :initform t
+           :accessor in-memory-stream-open-p
+           :documentation "For CMUCL we have to keep track of this
+manually."))
+  (:documentation "An IN-MEMORY-STREAM is a binary stream that reads
+octets from or writes octets to a sequence in RAM."))
+
+(defclass in-memory-input-stream (in-memory-stream fundamental-binary-input-stream)
+  ()
+  (:documentation "An IN-MEMORY-INPUT-STREAM is a binary stream that
+reads octets from a sequence in RAM."))
+
+#+:cmu
+(defmethod output-stream-p ((stream in-memory-input-stream))
+  "Explicitly states whether this is an output stream."
+  (declare (optimize speed))
+  nil)
+
+(defclass in-memory-output-stream (in-memory-stream fundamental-binary-output-stream)
+  ()
+  (:documentation "An IN-MEMORY-OUTPUT-STREAM is a binary stream that
+writes octets to a sequence in RAM."))
+
+#+:cmu
+(defmethod input-stream-p ((stream in-memory-output-stream))
+  "Explicitly states whether this is an input stream."
+  (declare (optimize speed))
+  nil)
+
+(defclass list-stream ()
+  ((list :initarg :list
+         :accessor list-stream-list
+         :documentation "The underlying list of the stream."))
+  (:documentation "A LIST-STREAM is a mixin for IN-MEMORY streams
+where the underlying sequence is a list."))
+
+(defclass vector-stream ()
+  ((vector :initarg :vector
+           :accessor vector-stream-vector
+           :documentation "The underlying vector of the stream which
+\(for output) must always be adjustable and have a fill pointer."))
+  (:documentation "A VECTOR-STREAM is a mixin for IN-MEMORY streams
+where the underlying sequence is a vector."))
+
+(defclass list-input-stream (list-stream in-memory-input-stream)
+  ()
+  (:documentation "A binary input stream that gets its data from an
+associated list of octets."))
+
+(defclass vector-input-stream (vector-stream in-memory-input-stream)
+  ((index :initarg :index
+          :accessor vector-stream-index
+          :type (integer 0 #.array-dimension-limit)
+          :documentation "An index into the underlying vector denoting
+the current position.")
+   (end :initarg :end
+        :accessor vector-stream-end
+        :type (integer 0 #.array-dimension-limit)
+        :documentation "An index into the underlying vector denoting
+the end of the available data."))
+  (:documentation "A binary input stream that gets its data from an
+associated vector of octets."))
+
+(defclass vector-output-stream (vector-stream in-memory-output-stream)
+  ()
+  (:documentation "A binary output stream that writes its data to an
+associated vector."))
+
+#+:cmu
+(defmethod open-stream-p ((stream in-memory-stream))
+  "Returns a true value if STREAM is open.  See ANSI standard."
+  (declare #.*standard-optimize-settings*)
+  (in-memory-stream-open-p stream))
+
+#+:cmu
+(defmethod close ((stream in-memory-stream) &key abort)
+  "Closes the stream STREAM.  See ANSI standard."
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore abort))
+  (prog1
+      (in-memory-stream-open-p stream)
+    (setf (in-memory-stream-open-p stream) nil)))
+
+(defmethod check-if-open ((stream in-memory-stream))
+  "Checks if STREAM is open and signals an error otherwise."
+  (declare #.*standard-optimize-settings*)
+  (unless (open-stream-p stream)
+    (error 'in-memory-stream-closed-error
+           :stream stream)))
+
+(defmethod stream-element-type ((stream in-memory-stream))
+  "The element type is always OCTET by definition."
+  (declare #.*standard-optimize-settings*)
+  'octet)
+
+(defmethod transform-octet ((stream in-memory-stream) octet)
+  "Applies the transformer of STREAM to octet and returns the result."
+  (declare #.*standard-optimize-settings*)
+  (funcall (or (in-memory-stream-transformer stream)
+               #'identity) octet))
+
+(defmethod stream-read-byte ((stream list-input-stream))
+  "Reads one byte by simply popping it off of the top of the list."
+  (declare #.*standard-optimize-settings*)
+  (check-if-open stream)
+  (with-accessors ((list list-stream-list))
+      stream
+    (transform-octet stream (or (pop list) (return-from stream-read-byte :eof)))))
+
+(defmethod stream-listen ((stream list-input-stream))
+  "Checks whether list is not empty."
+  (declare #.*standard-optimize-settings*)
+  (check-if-open stream)
+  (with-accessors ((list list-stream-list))
+      stream
+    list))
+
+(defmethod stream-read-sequence ((stream list-input-stream) sequence start end &key)
+  "Repeatedly pops elements from the list until it's empty."
+  (declare #.*standard-optimize-settings*)
+  (declare (fixnum start end))
+  (with-accessors ((list list-stream-list))
+      stream
+    (loop for index of-type fixnum from start below end
+          while list
+          do (setf (elt sequence index) (pop list))
+          finally (return index))))
+
+(defmethod stream-read-byte ((stream vector-input-stream))
+  "Reads one byte and increments INDEX pointer unless we're beyond
+END pointer."
+  (declare #.*standard-optimize-settings*)
+  (check-if-open stream)
+  (with-accessors ((index vector-stream-index)
+                   (end vector-stream-end)
+                   (vector vector-stream-vector))
+      stream
+    (let ((current-index index))
+      (declare (fixnum current-index))
+      (cond ((< current-index (the fixnum end))
+             (incf (the fixnum index))
+             (transform-octet stream (aref vector current-index)))
+            (t :eof)))))
+
+(defmethod stream-listen ((stream vector-input-stream))
+  "Checking whether INDEX is beyond END."
+  (declare #.*standard-optimize-settings*)
+  (check-if-open stream)
+  (with-accessors ((index vector-stream-index)
+                   (end vector-stream-end))
+      stream
+    (< (the fixnum index) (the fixnum end))))
+  
+(defmethod stream-read-sequence ((stream vector-input-stream) sequence start end &key)
+  "Traverses both sequences in parallel until the end of one of them
+is reached."
+  (declare #.*standard-optimize-settings*)
+  (declare (fixnum start end))
+  (loop with vector-end of-type fixnum = (vector-stream-end stream)
+        with vector = (vector-stream-vector stream)
+        for index of-type fixnum from start below end
+        for vector-index of-type fixnum = (vector-stream-index stream)
+        while (< vector-index vector-end)
+        do (setf (elt sequence index)
+                 (aref vector vector-index))
+           (incf (the fixnum (vector-stream-index stream)))
+        finally (return index)))
+
+(defmethod stream-write-byte ((stream vector-output-stream) byte)
+  "Writes a byte \(octet) by extending the underlying vector."
+  (declare #.*standard-optimize-settings*)
+  (check-if-open stream)
+  (with-accessors ((vector vector-stream-vector))
+      stream
+    (vector-push-extend (transform-octet stream byte) vector)))
+
+(defmethod stream-write-sequence ((stream vector-output-stream) sequence start end &key)
+  "Just calls VECTOR-PUSH-EXTEND repeatedly."
+  (declare #.*standard-optimize-settings*)
+  (declare (fixnum start end))
+  (with-accessors ((vector vector-stream-vector))
+      stream
+    (loop for index of-type fixnum from start below end
+          do (vector-push-extend (transform-octet stream (elt sequence index)) vector))
+    sequence))
+
+(defmethod stream-file-position ((stream vector-input-stream))
+  "Simply returns the index into the underlying vector."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((index vector-stream-index))
+      stream
+    index))
+
+(defmethod (setf stream-file-position) (position-spec (stream vector-input-stream))
+  "Sets the index into the underlying vector if POSITION-SPEC is acceptable."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((index vector-stream-index)
+                   (end vector-stream-end))
+      stream
+    (setq index
+          (case position-spec
+            (:start 0)
+            (:end end)
+            (otherwise
+             (unless (integerp position-spec)
+               (error 'in-memory-stream-position-spec-error
+                      :format-control "Unknown file position designator: ~S."
+                      :format-arguments (list position-spec)
+                      :stream stream
+                      :position-spec position-spec))
+             (unless (<= 0 position-spec end)
+               (error 'in-memory-stream-position-spec-error
+                      :format-control "File position designator ~S is out of bounds."
+                      :format-arguments (list position-spec)
+                      :stream stream
+                      :position-spec position-spec))
+             position-spec)))
+    position-spec))
+
+(defmethod stream-file-position ((stream vector-output-stream))
+  "Simply returns the fill pointer of the underlying vector."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((vector vector-stream-vector))
+      stream
+    (fill-pointer vector)))
+
+(defmethod (setf stream-file-position) (position-spec (stream vector-output-stream))
+  "Sets the fill pointer underlying vector if POSITION-SPEC is
+acceptable.  Adjusts the vector if necessary."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((vector vector-stream-vector))
+      stream
+    (let* ((total-size (array-total-size vector))
+           (new-fill-pointer
+            (case position-spec
+              (:start 0)
+              (:end
+               (warn "File position designator :END doesn't really make sense for an output stream.")
+               total-size)
+              (otherwise
+               (unless (integerp position-spec)
+                 (error 'in-memory-stream-position-spec-error
+                        :format-control "Unknown file position designator: ~S."
+                        :format-arguments (list position-spec)
+                        :stream stream
+                        :position-spec position-spec))
+               (unless (<= 0 position-spec array-total-size-limit)
+                 (error 'in-memory-stream-position-spec-error
+                        :format-control "File position designator ~S is out of bounds."
+                        :format-arguments (list position-spec)
+                        :stream stream
+                        :position-spec position-spec))
+               position-spec))))
+      (declare (fixnum total-size new-fill-pointer))
+      (when (> new-fill-pointer total-size)
+        (adjust-array vector new-fill-pointer))
+      (setf (fill-pointer vector) new-fill-pointer)
+      position-spec)))
+
+(defmethod make-in-memory-input-stream ((vector vector) &key (start 0)
+                                                             (end (length vector))
+                                                             transformer)
+  "Returns a binary input stream which will supply, in order, the
+octets in the subsequence of VECTOR bounded by START and END.
+Each octet returned will be transformed in turn by the optional
+TRANSFORMER function."
+  (declare #.*standard-optimize-settings*)
+  (make-instance 'vector-input-stream
+                 :vector vector
+                 :index start
+                 :end end
+                 :transformer transformer))
+
+(defmethod make-in-memory-input-stream ((list list) &key (start 0)
+                                                         (end (length list))
+                                                         transformer)
+  "Returns a binary input stream which will supply, in order, the
+octets in the subsequence of LIST bounded by START and END.  Each
+octet returned will be transformed in turn by the optional
+TRANSFORMER function."
+  (declare #.*standard-optimize-settings*)
+  (make-instance 'list-input-stream
+                 :list (subseq list start end)
+                 :transformer transformer))
+
+(defun make-output-vector (&key (element-type 'octet))
+  "Creates and returns an array which can be used as the underlying
+vector for a VECTOR-OUTPUT-STREAM."
+  (declare #.*standard-optimize-settings*)
+  (make-array 0 :adjustable t
+                :fill-pointer 0
+                :element-type element-type))
+
+(defun make-in-memory-output-stream (&key (element-type 'octet) transformer)
+  "Returns a binary output stream which accepts objects of type
+ELEMENT-TYPE \(a subtype of OCTET) and makes available a sequence
+that contains the octes that were actually output.  The octets
+stored will each be transformed by the optional TRANSFORMER
+function."
+  (declare #.*standard-optimize-settings*)
+  (make-instance 'vector-output-stream
+                 :vector (make-output-vector :element-type element-type)
+                 :transformer transformer))
+
+(defmethod get-output-stream-sequence ((stream in-memory-output-stream) &key as-list)
+  "Returns a vector containing, in order, all the octets that have
+been output to the IN-MEMORY stream STREAM. This operation clears any
+octets on STREAM, so the vector contains only those octets which have
+been output since the last call to GET-OUTPUT-STREAM-SEQUENCE or since
+the creation of the stream, whichever occurred most recently.  If
+AS-LIST is true the return value is coerced to a list."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((vector vector-stream-vector))
+      stream
+    (prog1
+        (if as-list
+          (coerce vector 'list)
+          vector)
+      (setq vector
+            (make-output-vector)))))
+
+(defmethod output-stream-sequence-length ((stream in-memory-output-stream))
+  "Returns the current length of the underlying vector of the
+IN-MEMORY output stream STREAM."
+  (declare (optimize speed))
+  (with-accessors ((vector vector-stream-vector))
+      stream
+    (length (the (simple-array * (*)) vector))))
+
+(defmacro with-input-from-sequence ((var sequence &key start end transformer) 
+                                    &body body)
+  "Creates an IN-MEMORY input stream from SEQUENCE using the
+parameters START and END, binds VAR to this stream and then
+executes the code in BODY.  A function TRANSFORMER may optionally
+be specified to transform the returned octets.  The stream is
+automatically closed on exit from WITH-INPUT-FROM-SEQUENCE, no
+matter whether the exit is normal or abnormal.  The return value
+of this macro is the return value of BODY."
+  (with-rebinding (sequence)
+    `(let (,var)
+       (unwind-protect
+           (progn
+             (setq ,var (make-in-memory-input-stream ,sequence
+                                                     :start (or ,start 0)
+                                                     :end (or ,end (length ,sequence))
+                                                     :transformer ,transformer))
+             ,@body)
+         (when ,var (close ,var))))))
+
+(defmacro with-output-to-sequence ((var &key as-list (element-type ''octet) transformer)
+                                   &body body)
+  "Creates an IN-MEMORY output stream, binds VAR to this stream
+and then executes the code in BODY.  The stream stores data of
+type ELEMENT-TYPE \(a subtype of OCTET) which is \(optionally)
+transformed by the function TRANSFORMER prior to storage.  The
+stream is automatically closed on exit from
+WITH-OUTPUT-TO-SEQUENCE, no matter whether the exit is normal or
+abnormal.  The return value of this macro is a vector \(or a list
+if AS-LIST is true) containing the octets that were sent to the
+stream within BODY."
+  `(let (,var)
+     (unwind-protect
+         (progn
+           (setq ,var (make-in-memory-output-stream :element-type ,element-type
+                                                    :transformer ,transformer))
+           ,@body
+           (get-output-stream-sequence ,var :as-list ,as-list))
+       (when ,var (close ,var)))))
diff --git a/deps/flexi-streams/input.lisp b/deps/flexi-streams/input.lisp
new file mode 100644 (file)
index 0000000..125d5ff
--- /dev/null
@@ -0,0 +1,294 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/input.lisp,v 1.78 2008/05/25 19:25:44 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+#-:lispworks
+(defmethod read-byte* ((flexi-input-stream flexi-input-stream))
+  "Reads one byte \(octet) from the underlying stream of
+FLEXI-OUTPUT-STREAM \(or from the internal stack if it's not
+empty)."
+  (declare #.*standard-optimize-settings*)
+  ;; we're using S instead of STREAM here because of an
+  ;; issue with SBCL:
+  ;; <http://article.gmane.org/gmane.lisp.steel-bank.general/1386>
+  (with-accessors ((position flexi-stream-position)
+                   (bound flexi-stream-bound)
+                   (octet-stack flexi-stream-octet-stack)
+                   (s flexi-stream-stream))
+      flexi-input-stream
+    (declare (integer position)
+             (type (or null integer) bound))
+    (when (and bound
+               (>= position bound))
+      (return-from read-byte* nil))
+    (incf position)
+    (or (pop octet-stack)
+        (read-byte s nil nil)
+        (progn (decf position) nil))))
+
+#+:lispworks
+(defmethod read-byte* ((flexi-input-stream flexi-input-stream))
+  "Reads one byte \(octet) from the underlying \(binary) stream of
+FLEXI-OUTPUT-STREAM \(or from the internal stack if it's not empty)."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((position flexi-stream-position)
+                   (bound flexi-stream-bound)
+                   (octet-stack flexi-stream-octet-stack)
+                   (stream flexi-stream-stream))
+      flexi-input-stream
+    (declare (integer position)
+             (type (or null integer) bound))
+    (when (and bound
+               (>= position bound))
+      (return-from read-byte* nil))
+    (incf position)
+    (or (pop octet-stack)
+        (read-byte stream nil nil)
+        (progn (decf position) nil))))
+
+#+:lispworks
+(defmethod read-byte* ((flexi-input-stream flexi-char-input-stream))
+  "Reads one byte \(octet) from the underlying stream of
+FLEXI-OUTPUT-STREAM \(or from the internal stack if it's not empty).
+Only used for LispWorks bivalent streams which aren't binary."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((position flexi-stream-position)
+                   (bound flexi-stream-bound)
+                   (octet-stack flexi-stream-octet-stack)
+                   (stream flexi-stream-stream))
+      flexi-input-stream
+    (declare (integer position)
+             (type (or null integer) bound))
+    (when (and bound
+               (>= position bound))
+      (return-from read-byte* nil))
+    (incf position)
+    (or (pop octet-stack)
+        ;; we use READ-SEQUENCE because READ-BYTE doesn't work with all
+        ;; bivalent streams in LispWorks
+        (let* ((buffer (make-array 1 :element-type 'octet))
+               (new-position (read-sequence buffer stream)))
+          (cond ((zerop new-position)
+                 (decf position) nil)
+                (t (aref buffer 0)))))))
+
+(defmethod stream-clear-input ((flexi-input-stream flexi-input-stream))
+  "Calls the corresponding method for the underlying input stream
+and also clears the value of the OCTET-STACK slot."
+  (declare #.*standard-optimize-settings*)
+  ;; note that we don't reset the POSITION slot
+  (with-accessors ((octet-stack flexi-stream-octet-stack)
+                   (stream flexi-stream-stream))
+      flexi-input-stream
+    (setq octet-stack nil)
+    (clear-input stream)))
+
+(defmethod stream-listen ((flexi-input-stream flexi-input-stream))
+  "Calls the corresponding method for the underlying input stream
+but first checks if \(old) input is available in the OCTET-STACK
+slot."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((position flexi-stream-position)
+                   (bound flexi-stream-bound)
+                   (octet-stack flexi-stream-octet-stack)
+                   (stream flexi-stream-stream))
+      flexi-input-stream
+    (declare (integer position)
+             (type (or null integer) bound))
+    (when (and bound
+               (>= position bound))
+      (return-from stream-listen nil))
+    (or octet-stack (listen stream))))
+
+(defmethod stream-read-byte ((stream flexi-input-stream))
+  "Reads one byte \(octet) from the underlying stream."
+  (declare #.*standard-optimize-settings*)
+  ;; set LAST-CHAR-CODE slot to NIL because we can't UNREAD-CHAR after
+  ;; this operation
+  (with-accessors ((last-char-code flexi-stream-last-char-code)
+                   (last-octet flexi-stream-last-octet))
+      stream
+    (setq last-char-code nil)
+    (let ((octet (read-byte* stream)))
+      (setq last-octet octet)
+      (or octet :eof))))
+
+(defun unread-char% (char flexi-input-stream)
+  "Used internally to put a character CHAR which was already read back
+on the stream.  Uses the OCTET-STACK slot and decrements the POSITION
+slot accordingly."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((position flexi-stream-position)
+                   (octet-stack flexi-stream-octet-stack)
+                   (external-format flexi-stream-external-format))
+      flexi-input-stream
+    (let ((counter 0) octets-reversed)
+      (declare (fixnum counter))
+      (flet ((writer (octet)
+               (incf counter)
+               (push octet octets-reversed)))
+        (declare (dynamic-extent (function writer)))
+        (char-to-octets external-format char #'writer)
+        (decf position counter)
+        (setq octet-stack (nreconc octets-reversed octet-stack))))))
+
+(defmethod stream-read-char ((stream flexi-input-stream))
+  (declare #.*standard-optimize-settings*)
+  ;; note that we do nothing for the :LF EOL style because we assume
+  ;; that #\Newline is the same as #\Linefeed in all Lisps which will
+  ;; use this library
+  (with-accessors ((external-format flexi-stream-external-format)
+                   (last-octet flexi-stream-last-octet)
+                   (last-char-code flexi-stream-last-char-code))
+      stream
+    ;; set LAST-OCTET slot to NIL because we can't UNREAD-BYTE after
+    ;; this operation
+    (setq last-octet nil)
+    (flet ((reader ()
+             (read-byte* stream))
+           (unreader (char)
+             (unread-char% char stream)))
+      (declare (dynamic-extent (function reader) (function unreader)))
+      (let* ((*current-unreader* #'unreader)
+             (char-code (or (octets-to-char-code external-format #'reader)
+                            (return-from stream-read-char :eof))))
+        ;; remember this character and its char code for UNREAD-CHAR
+        (setq last-char-code char-code)
+        (or (code-char char-code) char-code)))))
+
+(defmethod stream-read-char-no-hang ((stream flexi-input-stream))
+  "Reads one character if the underlying stream has at least one
+octet available."
+  (declare #.*standard-optimize-settings*)
+  ;; note that this may block for non-8-bit encodings - I think
+  ;; there's no easy way to handle this correctly
+  (and (stream-listen stream)
+       (stream-read-char stream)))
+
+(defmethod stream-read-sequence ((flexi-input-stream flexi-input-stream) sequence start end &key)
+  "An optimized version which uses a buffer underneath.  The function
+can deliver characters as well as octets and it decides what to do
+based on the element type of the sequence \(which takes precedence)
+and the element type of the stream.  What you'll really get might also
+depend on your Lisp.  Some of the implementations are more picky than
+others - see for example FLEXI-STREAMS-TEST::SEQUENCE-TEST."
+  (declare #.*standard-optimize-settings*)
+  (declare (fixnum start end))
+  (with-accessors ((octet-stack flexi-stream-octet-stack)
+                   (external-format flexi-stream-external-format)
+                   (last-octet flexi-stream-last-octet)
+                   (last-char-code flexi-stream-last-char-code)
+                   (element-type flexi-stream-element-type)
+                   (stream flexi-stream-stream))
+      flexi-input-stream
+    (when (>= start end)
+      (return-from stream-read-sequence start))
+    (when (or (subtypep (etypecase sequence
+                          (vector (array-element-type sequence))
+                          (list t))
+                        'integer)
+              (and (not (stringp sequence))
+                   (type-equal element-type 'octet)))
+      ;; if binary data is requested, just read from the underlying
+      ;; stream directly and skip the rest (but flush octet stack
+      ;; first)
+      (let ((index start))
+        (declare (fixnum index))
+        (when octet-stack
+          (replace sequence octet-stack :start1 start :end1 end)
+          (let ((octets-flushed (min (length octet-stack) (- end start))))
+            (incf index octets-flushed)
+            (setq octet-stack (nthcdr octets-flushed octet-stack))))
+        (setq index (read-sequence sequence stream :start index :end end))
+        (when (> index start)
+          (setq last-char-code nil
+                last-octet (elt sequence (1- index))))
+        (return-from stream-read-sequence index)))
+    ;; otherwise hand over to the external format to do the work
+    (read-sequence* external-format flexi-input-stream sequence start end)))
+
+(defmethod stream-unread-char ((stream flexi-input-stream) char)
+  "Implements UNREAD-CHAR for streams of type FLEXI-INPUT-STREAM.
+Makes sure CHAR will only be unread if it was the last character
+read and if it was read with the same encoding that's currently
+being used by the stream."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((last-char-code flexi-stream-last-char-code))
+      stream
+    (unless last-char-code
+      (error 'flexi-stream-error
+             :format-control "No character to unread from this stream \(or external format has changed or last reading operation was binary)."))
+    (unless (= (char-code char) last-char-code)
+      (error 'flexi-stream-error
+             :format-control "Last character read (~S) was different from ~S."
+             :format-arguments (list (code-char last-char-code) char)))
+    (unread-char% char stream)
+    (setq last-char-code nil)
+    nil))
+
+(defmethod unread-byte (byte (flexi-input-stream flexi-input-stream))
+  "Similar to UNREAD-CHAR in that it `unreads' the last octet from
+STREAM.  Note that you can only call UNREAD-BYTE after a corresponding
+READ-BYTE."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((last-octet flexi-stream-last-octet)
+                   (octet-stack flexi-stream-octet-stack)
+                   (position flexi-stream-position))
+      flexi-input-stream
+    (unless last-octet
+      (error 'flexi-stream-error
+             :format-control "No byte to unread from this stream \(or last reading operation read a character)."))
+    (unless (= byte last-octet)
+      (error 'flexi-stream-error
+             :format-control "Last byte read was different from #x~X."
+             :format-arguments (list byte)))
+    (setq last-octet nil)
+    (decf (the integer position))
+    (push byte octet-stack)
+    nil))
+    
+(defmethod peek-byte ((flexi-input-stream flexi-input-stream)
+                      &optional peek-type (eof-error-p t) eof-value)
+  "PEEK-BYTE is like PEEK-CHAR, i.e. it returns an octet from
+FLEXI-INPUT-STREAM without actually removing it.  If PEEK-TYPE is NIL
+the next octet is returned, if PEEK-TYPE is T, the next octet which is
+not 0 is returned, if PEEK-TYPE is an octet, the next octet which
+equals PEEK-TYPE is returned.  EOF-ERROR-P and EOF-VALUE are
+interpreted as usual."
+  (declare #.*standard-optimize-settings*)
+  (loop for octet = (read-byte flexi-input-stream eof-error-p eof-value)
+        until (cond ((null peek-type))
+                    ((eql octet eof-value))
+                    ((eq peek-type t)
+                     (plusp octet))
+                    (t (= octet peek-type)))
+        finally (unless (eql octet eof-value)
+                  (unread-byte octet flexi-input-stream))
+                (return octet)))
\ No newline at end of file
diff --git a/deps/flexi-streams/io.lisp b/deps/flexi-streams/io.lisp
new file mode 100644 (file)
index 0000000..a746700
--- /dev/null
@@ -0,0 +1,110 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/io.lisp,v 1.2 2008/05/20 23:44:45 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defmethod reset-input-state ((flexi-io-stream flexi-io-stream))
+  "This method is used to clear any state associated with previous
+input before output is attempted on the stream.  It can fail if the
+octet stack is not empty and the stream can't be `rewound'."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((last-char-code flexi-stream-last-char-code)
+                   (last-octet flexi-stream-last-octet)
+                   (octet-stack flexi-stream-octet-stack)
+                   (stream flexi-stream-stream))
+      flexi-io-stream
+    (when octet-stack
+      (unless (maybe-rewind stream (length octet-stack))
+        (error 'flexi-stream-out-of-sync-error
+               :stream flexi-io-stream))
+      (setq octet-stack nil))
+    (setq last-octet nil
+          last-char-code nil)))
+
+(defmethod stream-write-byte :before ((stream flexi-io-stream) byte)
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore byte))
+  (reset-input-state stream))
+  
+(defmethod stream-write-char :before ((stream flexi-io-stream) char)
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore char))
+  (reset-input-state stream))
+  
+(defmethod stream-write-sequence :before ((stream flexi-io-stream) sequence start end &key)
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore sequence start end))
+  (reset-input-state stream))
+  
+(defmethod stream-clear-output :before ((stream flexi-io-stream))
+  (declare #.*standard-optimize-settings*)
+  (reset-input-state stream))
+
+(defmethod reset-output-state ((flexi-io-stream flexi-io-stream))
+  "This method is used to clear any state associated with previous
+output before the stream is used for input."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((column flexi-stream-column))
+      flexi-io-stream
+    (setq column nil)))
+  
+(defmethod stream-read-byte :before ((stream flexi-io-stream))
+  (declare #.*standard-optimize-settings*)
+  (reset-output-state stream))
+  
+(defmethod stream-read-char :before ((stream flexi-io-stream))
+  (declare #.*standard-optimize-settings*)
+  (reset-output-state stream))
+
+(defmethod stream-read-sequence :before ((stream flexi-io-stream) sequence start end &key)
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore sequence start end))
+  (reset-output-state stream))
+
+(defmethod stream-unread-char :before ((stream flexi-io-stream) char)
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore char))
+  (reset-output-state stream))
+  
+(defmethod unread-byte :before (byte (stream flexi-io-stream))
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore byte))
+  (reset-output-state stream))
+  
+(defmethod stream-clear-input :before ((stream flexi-io-stream))
+  (declare #.*standard-optimize-settings*)
+  (reset-output-state stream))
+
+(defmethod write-byte* :after (byte (stream flexi-io-stream))
+  "Keep POSITION slot up to date even when performing output."
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore byte))
+  (with-accessors ((position flexi-stream-position))
+      stream
+    (incf position)))
\ No newline at end of file
diff --git a/deps/flexi-streams/iso-8859.lisp b/deps/flexi-streams/iso-8859.lisp
new file mode 100644 (file)
index 0000000..af5d043
--- /dev/null
@@ -0,0 +1,53 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-\r
+;;; $Header: /usr/local/cvsrep/flexi-streams/iso-8859.lisp,v 1.7 2008/05/18 21:32:15 edi Exp $\r
+\r
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.\r
+\r
+;;; Redistribution and use in source and binary forms, with or without\r
+;;; modification, are permitted provided that the following conditions\r
+;;; are met:\r
+\r
+;;;   * Redistributions of source code must retain the above copyright\r
+;;;     notice, this list of conditions and the following disclaimer.\r
+\r
+;;;   * Redistributions in binary form must reproduce the above\r
+;;;     copyright notice, this list of conditions and the following\r
+;;;     disclaimer in the documentation and/or other materials\r
+;;;     provided with the distribution.\r
+\r
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED\r
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\r
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\r
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+\r
+(in-package :flexi-streams)\r
+\r
+;;; the following code was auto-generated from files which can be\r
+;;; found at <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/>\r
+\r
+(defconstant +iso-8859-tables+\r
+  `((:iso-8859-1 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255))) \r
+    (:iso-8859-2 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 260 728 321 164 317 346 167 168 352 350 356 377 173 381 379 176 261 731 322 180 318 347 711 184 353 351 357 378 733 382 380 340 193 194 258 196 313 262 199 268 201 280 203 282 205 206 270 272 323 327 211 212 336 214 215 344 366 218 368 220 221 354 223 341 225 226 259 228 314 263 231 269 233 281 235 283 237 238 271 273 324 328 243 244 337 246 247 345 367 250 369 252 253 355 729))) \r
+    (:iso-8859-3 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 294 728 163 164 65533 292 167 168 304 350 286 308 173 65533 379 176 295 178 179 180 181 293 183 184 305 351 287 309 189 65533 380 192 193 194 65533 196 266 264 199 200 201 202 203 204 205 206 207 65533 209 210 211 212 288 214 215 284 217 218 219 220 364 348 223 224 225 226 65533 228 267 265 231 232 233 234 235 236 237 238 239 65533 241 242 243 244 289 246 247 285 249 250 251 252 365 349 729))) \r
+    (:iso-8859-4 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 260 312 342 164 296 315 167 168 352 274 290 358 173 381 175 176 261 731 343 180 297 316 711 184 353 275 291 359 330 382 331 256 193 194 195 196 197 198 302 268 201 280 203 278 205 206 298 272 325 332 310 212 213 214 215 216 370 218 219 220 360 362 223 257 225 226 227 228 229 230 303 269 233 281 235 279 237 238 299 273 326 333 311 244 245 246 247 248 371 250 251 252 361 363 729))) \r
+    (:iso-8859-5 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 173 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 8470 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 167 1118 1119))) \r
+    (:iso-8859-6 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 65533 65533 65533 164 65533 65533 65533 65533 65533 65533 65533 1548 173 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 1563 65533 65533 65533 1567 65533 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 65533 65533 65533 65533 65533 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533))) \r
+    (:iso-8859-7 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 8216 8217 163 8364 8367 166 167 168 169 890 171 172 173 65533 8213 176 177 178 179 900 901 902 183 904 905 906 187 908 189 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 65533 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 65533))) \r
+    (:iso-8859-8 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 65533 162 163 164 165 166 167 168 169 215 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 247 187 188 189 190 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 65533 8215 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 65533 65533 8206 8207 65533))) \r
+    (:iso-8859-9 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 286 209 210 211 212 213 214 215 216 217 218 219 220 304 350 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 287 241 242 243 244 245 246 247 248 249 250 251 252 305 351 255))) \r
+    (:iso-8859-10 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 260 274 290 298 296 310 167 315 272 352 358 381 173 362 330 176 261 275 291 299 297 311 183 316 273 353 359 382 8213 363 331 256 193 194 195 196 197 198 302 268 201 280 203 278 205 206 207 208 325 332 211 212 213 214 360 216 370 218 219 220 221 222 223 257 225 226 227 228 229 230 303 269 233 281 235 279 237 238 239 240 326 333 243 244 245 246 361 248 371 250 251 252 253 254 312))) \r
+    (:iso-8859-11 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 65533 65533 65533 65533 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 65533 65533 65533 65533))) \r
+    (:iso-8859-13 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 8221 162 163 164 8222 166 167 216 169 342 171 172 173 174 198 176 177 178 179 8220 181 182 183 248 185 343 187 188 189 190 230 260 302 256 262 196 197 280 274 268 201 377 278 290 310 298 315 352 323 325 211 332 213 214 215 370 321 346 362 220 379 381 223 261 303 257 263 228 229 281 275 269 233 378 279 291 311 299 316 353 324 326 243 333 245 246 247 371 322 347 363 252 380 382 8217))) \r
+    (:iso-8859-14 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 7682 7683 163 266 267 7690 167 7808 169 7810 7691 7922 173 174 376 7710 7711 288 289 7744 7745 182 7766 7809 7767 7811 7776 7923 7812 7813 7777 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 372 209 210 211 212 213 214 7786 216 217 218 219 220 221 374 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 373 241 242 243 244 245 246 7787 248 249 250 251 252 253 375 255))) \r
+    (:iso-8859-15 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 8364 165 352 167 353 169 170 171 172 173 174 175 176 177 178 179 381 181 182 183 382 185 186 187 338 339 376 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255))) \r
+    (:iso-8859-16 . ,(make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 260 261 321 8364 8222 352 167 353 169 536 171 377 173 378 379 176 177 268 322 381 8221 182 183 382 269 537 187 338 339 376 380 192 193 194 258 196 262 198 199 200 201 202 203 204 205 206 207 272 323 210 211 212 336 214 346 368 217 218 219 220 280 538 223 224 225 226 259 228 263 230 231 232 233 234 235 236 237 238 239 273 324 242 243 244 337 246 347 369 249 250 251 252 281 539 255))))\r
+  "A list of the ISO-8859 encodings where each element is a cons\r
+with the car being a keyword denoting the encoding and the cdr\r
+being a vector enumerating the corresponding character codes.")\r
diff --git a/deps/flexi-streams/koi8-r.lisp b/deps/flexi-streams/koi8-r.lisp
new file mode 100644 (file)
index 0000000..5c8352e
--- /dev/null
@@ -0,0 +1,36 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/koi8-r.lisp,v 1.2 2008/05/18 21:32:15 edi Exp $
+
+;;; Copyright (c) 2006, Igor Plekhov.  All rights reserved.
+;;; Copyright (c) 2006-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+;; http://unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT
+(defconstant +koi8-r-table+
+  (make-decoding-table '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 9472 9474 9484 9488 9492 9496 9500 9508 9516 9524 9532 9600 9604 9608 9612 9616 9617 9618 9619 8992 9632 8729 8730 8776 8804 8805 160 8993 176 178 183 247 9552 9553 9554 1105 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 9568 9569 1025 9570 9571 9572 9573 9574 9575 9576 9577 9578 9579 9580 169 1102 1072 1073 1094 1076 1077 1092 1075 1093 1080 1081 1082 1083 1084 1085 1086 1087 1103 1088 1089 1090 1091 1078 1074 1100 1099 1079 1096 1101 1097 1095 1098 1070 1040 1041 1062 1044 1045 1060 1043 1061 1048 1049 1050 1051 1052 1053 1054 1055 1071 1056 1057 1058 1059 1046 1042 1068 1067 1047 1064 1069 1065 1063 1066))
+    "An array enumerating the character codes for the KOI8-R encoding.")
diff --git a/deps/flexi-streams/length.lisp b/deps/flexi-streams/length.lisp
new file mode 100644 (file)
index 0000000..30b790b
--- /dev/null
@@ -0,0 +1,468 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/length.lisp,v 1.6 2008/05/29 10:25:14 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defgeneric encoding-factor (format)
+  (:documentation "Given an external format FORMAT, returns a factor
+which denotes the octets to characters ratio to expect when
+encoding/decoding.  If the returned value is an integer, the factor is
+assumed to be exact.  If it is a \(double) float, the factor is
+supposed to be based on heuristics and usually not exact.
+
+This factor is used in string.lisp.")
+  (declare #.*standard-optimize-settings*))
+
+(defmethod encoding-factor ((format flexi-8-bit-format))
+  (declare #.*standard-optimize-settings*)
+  ;; 8-bit encodings map octets to characters in an exact one-to-one
+  ;; fashion
+  1)
+
+(defmethod encoding-factor ((format flexi-utf-8-format))
+  (declare #.*standard-optimize-settings*)
+  ;; UTF-8 characters can be anything from one to six octets, but we
+  ;; assume that the "overhead" is only about 5 percent - this
+  ;; estimate is obviously very much dependant on the content
+  1.05d0)
+
+(defmethod encoding-factor ((format flexi-utf-16-format))
+  (declare #.*standard-optimize-settings*)
+  ;; usually one character maps to two octets, but characters with
+  ;; code points above #x10000 map to four octets - we assume that we
+  ;; usually don't see these characters but of course have to return a
+  ;; float
+  2.0d0)
+
+(defmethod encoding-factor ((format flexi-utf-32-format))
+  (declare #.*standard-optimize-settings*)
+  ;; UTF-32 always matches every character to four octets
+  4)
+
+(defmethod encoding-factor ((format flexi-crlf-mixin))
+  (declare #.*standard-optimize-settings*)
+  ;; if the sequence #\Return #\Linefeed is the line-end marker, this
+  ;; obviously makes encodings potentially longer and definitely makes
+  ;; the estimate unexact
+  (* 1.02d0 (call-next-method)))
+
+(defgeneric check-end (format start end i)
+  (declare #.*fixnum-optimize-settings*)
+  (:documentation "Helper function used below to determine if we tried
+to read past the end of the sequence.")
+  (:method (format start end i)
+   (declare #.*fixnum-optimize-settings*)
+   (declare (ignore start))
+   (declare (fixnum end i))
+   (when (> i end)
+     (signal-encoding-error format "This sequence can't be decoded ~
+using ~A as it is too short.  ~A octet~:P missing at the end."
+                            (external-format-name format)
+                            (- i end))))
+  (:method ((format flexi-utf-16-format) start end i)
+   (declare #.*fixnum-optimize-settings*)
+   (declare (fixnum start end i))
+   (declare (ignore i))
+   ;; don't warn twice
+   (when (evenp (- end start))
+     (call-next-method))))
+
+(defgeneric compute-number-of-chars (format sequence start end)
+  (declare #.*standard-optimize-settings*)
+  (:documentation "Computes the exact number of characters required to
+decode the sequence of octets in SEQUENCE from START to END using the
+external format FORMAT."))
+
+(defmethod compute-number-of-chars :around (format (list list) start end)
+  (declare #.*standard-optimize-settings*)
+  (call-next-method format (coerce list 'vector) start end))
+
+(defmethod compute-number-of-chars ((format flexi-8-bit-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end))
+  (declare (ignore sequence))
+  (- end start))
+
+(defmethod compute-number-of-chars ((format flexi-crlf-mixin) sequence start end)
+  ;; this method only applies to the 8-bit formats as all other
+  ;; formats with CRLF line endings have their own specialized methods
+  ;; below
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (vector sequence))
+  (let ((i start)
+        (length (- end start)))
+    (declare (fixnum i length))
+    (loop
+     (when (>= i end)
+       (return))
+     (let ((position (search #.(vector +cr+ +lf+) sequence :start2 i :end2 end :test #'=)))
+       (unless position
+         (return))
+       (setq i (1+ position))
+       (decf length)))
+    length))
+
+(defmethod compute-number-of-chars ((format flexi-utf-8-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (vector sequence))
+  (let ((sum 0)
+        (i start))
+    (declare (fixnum i sum))
+    (loop
+     (when (>= i end)
+       (return))
+     (let* ((octet (aref sequence i))
+            ;; note that there are no validity checks here
+            (length (cond ((not (logbitp 7 octet)) 1)
+                          ((= #b11000000 (logand* octet #b11100000)) 2)
+                          ((= #b11100000 (logand* octet #b11110000)) 3)
+                          (t 4))))
+       (declare (fixnum length) (type octet octet))
+       (incf sum)
+       (incf i length)))
+    (check-end format start end i)
+    sum))
+
+(defmethod compute-number-of-chars ((format flexi-crlf-utf-8-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (vector sequence))
+  (let ((sum 0)
+        (i start)
+        (last-octet 0))
+    (declare (fixnum i sum) (type octet last-octet))
+    (loop
+     (when (>= i end)
+       (return))
+     (let* ((octet (aref sequence i))
+            ;; note that there are no validity checks here
+            (length (cond ((not (logbitp 7 octet)) 1)
+                          ((= #b11000000 (logand* octet #b11100000)) 2)
+                          ((= #b11100000 (logand* octet #b11110000)) 3)
+                          (t 4))))
+       (declare (fixnum length) (type octet octet))
+       (unless (and (= octet +lf+) (= last-octet +cr+))
+         (incf sum))
+       (incf i length)
+       (setq last-octet octet)))
+    (check-end format start end i)
+    sum))
+
+(defmethod compute-number-of-chars :before ((format flexi-utf-16-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (vector sequence))
+  (declare (ignore sequence))
+  (when (oddp (- end start))
+    (signal-encoding-error format "~A octet~:P cannot be decoded ~
+using UTF-16 as ~:*~A is not even."
+                           (- end start))))
+  
+(defmethod compute-number-of-chars ((format flexi-utf-16-le-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end))
+  (let ((sum 0)
+        (i start))
+    (declare (fixnum i sum))
+    (decf end 2)
+    (loop
+     (when (> i end)
+       (return))
+     (let* ((high-octet (aref sequence (1+ i)))
+            (length (cond ((<= #xd8 high-octet #xdf) 4)
+                          (t 2))))
+       (declare (fixnum length) (type octet high-octet))
+       (incf sum)
+       (incf i length)))
+    (check-end format start (+ end 2) i)
+    sum))
+
+(defmethod compute-number-of-chars ((format flexi-utf-16-be-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (vector sequence))
+  (let ((sum 0)
+        (i start))
+    (declare (fixnum i sum))
+    (decf end 2)
+    (loop
+     (when (> i end)
+       (return))
+     (let* ((high-octet (aref sequence i))
+            (length (cond ((<= #xd8 high-octet #xdf) 4)
+                          (t 2))))
+       (declare (fixnum length) (type octet high-octet))
+       (incf sum)
+       (incf i length)))
+    (check-end format start (+ end 2) i)
+    sum))
+
+(defmethod compute-number-of-chars ((format flexi-crlf-utf-16-le-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (vector sequence))
+  (let ((sum 0)
+        (i start)
+        (last-octet 0))
+    (declare (fixnum i sum) (type octet last-octet))
+    (decf end 2)
+    (loop
+     (when (> i end)
+       (return))
+     (let* ((high-octet (aref sequence (1+ i)))
+            (length (cond ((<= #xd8 high-octet #xdf) 4)
+                          (t 2))))
+       (declare (fixnum length) (type octet high-octet))
+       (unless (and (zerop high-octet)
+                    (= (the octet (aref sequence i)) +lf+)
+                    (= last-octet +cr+))         
+         (incf sum))
+       (setq last-octet (if (zerop high-octet)
+                          (aref sequence i)
+                          0))
+       (incf i length)))
+    (check-end format start (+ end 2) i)
+    sum))
+
+(defmethod compute-number-of-chars ((format flexi-crlf-utf-16-be-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (vector sequence))
+  (let ((sum 0)
+        (i start)
+        (last-octet 0))
+    (declare (fixnum i sum) (type octet last-octet))
+    (decf end 2)
+    (loop
+     (when (> i end)
+       (return))
+     (let* ((high-octet (aref sequence i))
+            (length (cond ((<= #xd8 high-octet #xdf) 4)
+                          (t 2))))
+       (declare (fixnum length) (type octet high-octet))
+       (unless (and (zerop high-octet)
+                    (= (the octet (aref sequence (1+ i))) +lf+)
+                    (= last-octet +cr+))
+         (incf sum))
+       (setq last-octet (if (zerop high-octet)
+                          (aref sequence (1+ i))
+                          0))
+       (incf i length)))
+    (check-end format start (+ end 2) i)
+    sum))
+
+(defmethod compute-number-of-chars :before ((format flexi-utf-32-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end))
+  (declare (ignore sequence))
+  (let ((length (- end start)))
+    (when (plusp (mod length 4))
+      (signal-encoding-error format "~A octet~:P cannot be decoded ~
+using UTF-32 as ~:*~A is not a multiple-value of four."
+                             length))))
+
+(defmethod compute-number-of-chars ((format flexi-utf-32-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end))
+  (declare (ignore sequence))
+  (ceiling (- end start) 4))
+
+(defmethod compute-number-of-chars ((format flexi-crlf-utf-32-le-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (vector sequence))
+  (let ((i start)
+        (length (ceiling (- end start) 4)))
+    (decf end 8)
+    (loop
+     (when (> i end)
+       (return))
+     (cond ((loop for j of-type fixnum from i
+                  for octet across #.(vector +cr+ 0 0 0 +lf+ 0 0 0)
+                  always (= octet (aref sequence j)))
+            (decf length)
+            (incf i 8))
+           (t (incf i 4))))
+    length))
+
+(defmethod compute-number-of-chars ((format flexi-crlf-utf-32-be-format) sequence start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (vector sequence))
+  (let ((i start)
+        (length (ceiling (- end start) 4)))
+    (decf end 8)
+    (loop
+     (when (> i end)
+       (return))
+     (cond ((loop for j of-type fixnum from i
+                  for octet across #.(vector 0 0 0 +cr+ 0 0 0 +lf+)
+                  always (= octet (aref sequence j)))
+            (decf length)
+            (incf i 8))
+           (t (incf i 4))))
+    length))
+
+(defgeneric compute-number-of-octets (format sequence start end)
+  (declare #.*standard-optimize-settings*)
+  (:documentation "Computes the exact number of octets required to
+encode the sequence of characters in SEQUENCE from START to END using
+the external format FORMAT."))
+
+(defmethod compute-number-of-octets :around (format (list list) start end)
+  (declare #.*standard-optimize-settings*)
+  (call-next-method format (coerce list 'string*) start end))
+
+(defmethod compute-number-of-octets ((format flexi-8-bit-format) string start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end))
+  (declare (ignore string))  
+  (- end start))
+
+(defmethod compute-number-of-octets ((format flexi-utf-8-format) string start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (string string))
+  (let ((sum 0)
+        (i start))
+    (declare (fixnum i sum))
+    (loop
+     (when (>= i end)
+       (return))
+     (let* ((char-code (char-code (char string i)))
+            (char-length (cond ((< char-code #x80) 1)
+                               ((< char-code #x800) 2)
+                               ((< char-code #x10000) 3)
+                               (t 4))))
+       (declare (fixnum char-length) (type char-code-integer char-code))
+       (incf sum char-length)
+       (incf i)))
+    sum))
+
+(defmethod compute-number-of-octets ((format flexi-crlf-utf-8-format) string start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (string string))
+  (let ((sum 0)
+        (i start))
+    (declare (fixnum i sum))
+    (loop
+     (when (>= i end)
+       (return))
+     (let* ((char-code (char-code (char string i)))
+            (char-length (cond ((= char-code #.(char-code #\Newline)) 2)
+                               ((< char-code #x80) 1)
+                               ((< char-code #x800) 2)
+                               ((< char-code #x10000) 3)
+                               (t 4))))
+       (declare (fixnum char-length) (type char-code-integer char-code))
+       (incf sum char-length)
+       (incf i)))
+    sum))
+
+(defmethod compute-number-of-octets ((format flexi-utf-16-format) string start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (string string))
+  (let ((sum 0)
+        (i start))
+    (declare (fixnum i sum))
+    (loop
+     (when (>= i end)
+       (return))
+     (let* ((char-code (char-code (char string i)))
+            (char-length (cond ((< char-code #x10000) 2)
+                               (t 4))))
+       (declare (fixnum char-length) (type char-code-integer char-code))
+       (incf sum char-length)
+       (incf i)))
+    sum))
+
+(defmethod compute-number-of-octets ((format flexi-crlf-utf-16-le-format) string start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (string string))
+  (let ((sum 0)
+        (i start))
+    (declare (fixnum i sum))
+    (loop
+     (when (>= i end)
+       (return))
+     (let* ((char-code (char-code (char string i)))
+            (char-length (cond ((= char-code #.(char-code #\Newline)) 4)
+                               ((< char-code #x10000) 2)
+                               (t 4))))
+       (declare (fixnum char-length) (type char-code-integer char-code))
+       (incf sum char-length)
+       (incf i)))
+    sum))
+
+(defmethod compute-number-of-octets ((format flexi-crlf-utf-16-be-format) string start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (string string))
+  (let ((sum 0)
+        (i start))
+    (declare (fixnum i sum))
+    (loop
+     (when (>= i end)
+       (return))
+     (let* ((char-code (char-code (char string i)))
+            (char-length (cond ((= char-code #.(char-code #\Newline)) 4)
+                               ((< char-code #x10000) 2)
+                               (t 4))))
+       (declare (fixnum char-length) (type char-code-integer char-code))
+       (incf sum char-length)
+       (incf i)))
+    sum))
+
+(defmethod compute-number-of-octets ((format flexi-utf-32-format) string start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end))
+  (declare (ignore string))
+  (* 4 (- end start)))
+
+(defmethod compute-number-of-octets ((format flexi-crlf-mixin) string start end)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (fixnum start end) (string string))
+  (+ (call-next-method)
+     (* (case (external-format-name format)
+          (:utf-32 4)
+          (otherwise 1))
+        (count #\Newline string :start start :end end :test #'char=))))
+
+(defgeneric character-length (format char)
+  (declare #.*fixnum-optimize-settings*)
+  (:documentation "Returns the number of octets needed to encode the
+single character CHAR.")
+  (:method (format char)
+   (compute-number-of-octets format (string char) 0 1)))
+
+(defmethod character-length :around ((format flexi-crlf-mixin) (char (eql #\Newline)))
+  (declare #.*fixnum-optimize-settings*)
+  (+ (call-next-method format +cr+)
+     (call-next-method format +lf+)))
+
+(defmethod character-length ((format flexi-8-bit-format) char)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (ignore char))
+  1)
+
+(defmethod character-length ((format flexi-utf-32-format) char)
+  (declare #.*fixnum-optimize-settings*)
+  (declare (ignore char))
+  4)
\ No newline at end of file
diff --git a/deps/flexi-streams/lw-char-stream.lisp b/deps/flexi-streams/lw-char-stream.lisp
new file mode 100644 (file)
index 0000000..de3fcf8
--- /dev/null
@@ -0,0 +1,77 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/lw-char-stream.lisp,v 1.1 2008/05/23 14:43:09 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defclass flexi-char-output-stream (flexi-output-stream)
+  ()
+  (:documentation "This class is for output streams where the
+underlying stream is bivalent but not binary.  It exists solely for
+the purpose of optimizing output to binary streams on LispWorks.  See
+WRITE-BYTE*."))
+
+(defclass flexi-char-input-stream (flexi-input-stream)
+  ()
+  (:documentation "This class is for input streams where the
+underlying stream is bivalent but not binary.  It exists solely for
+the purpose of optimizing input to binary streams on LispWorks.  See
+READ-BYTE*."))
+
+(defclass flexi-char-io-stream (flexi-char-input-stream flexi-char-output-stream flexi-io-stream)
+  ()
+  (:documentation "This class is for bidirectional streams where the
+underlying stream is bivalent but not binary.  It exists solely for
+the purpose of optimizing input and output from/to binary streams on
+LispWorks.  See READ-BYTE* and WRITE-BYTE*."))
+
+(defmethod initialize-instance :after ((flexi-stream flexi-output-stream) &rest initargs)
+  "Might change the class of FLEXI-STREAM for optimization purposes.
+Only needed for LispWorks."
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore initargs))
+  (with-accessors ((stream flexi-stream-stream))
+      flexi-stream
+    (unless (subtypep (stream-element-type stream) 'octet)
+      (change-class flexi-stream
+                    (typecase flexi-stream
+                      (flexi-io-stream 'flexi-char-io-stream)
+                      (otherwise 'flexi-char-output-stream))))))
+
+(defmethod initialize-instance :after ((flexi-stream flexi-input-stream) &rest initargs)
+  "Might change the class of FLEXI-STREAM for optimization purposes.
+Only needed for LispWorks."
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore initargs))
+  (with-accessors ((stream flexi-stream-stream))
+      flexi-stream
+    (unless (subtypep (stream-element-type stream) 'octet)
+      (change-class flexi-stream
+                    (typecase flexi-stream
+                      (flexi-io-stream 'flexi-char-io-stream)
+                      (otherwise 'flexi-char-input-stream))))))
diff --git a/deps/flexi-streams/mapping.lisp b/deps/flexi-streams/mapping.lisp
new file mode 100644 (file)
index 0000000..ee70e50
--- /dev/null
@@ -0,0 +1,81 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/mapping.lisp,v 1.3 2008/05/25 19:07:53 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(deftype octet ()
+  "A shortcut for \(UNSIGNED-BYTE 8)."
+  '(unsigned-byte 8))
+
+(deftype char* ()
+  "Convenience shortcut to paper over the difference between LispWorks
+and the other Lisps."
+  #+:lispworks 'lw:simple-char
+  #-:lispworks 'character)
+
+(deftype string* ()
+  "Convenience shortcut to paper over the difference between LispWorks
+and the other Lisps."
+  #+:lispworks 'lw:text-string
+  #-:lispworks 'string)
+
+(deftype char-code-integer ()
+  "The subtype of integers which can be returned by the function CHAR-CODE."
+  #-:cmu '(integer 0 #.(1- char-code-limit))
+  #+:cmu '(integer 0 65533))
+
+(deftype code-point ()
+  "The subtype of integers that's just big enough to hold all Unicode
+codepoints.
+
+See for example <http://unicode.org/glossary/#C>."
+  '(mod #x110000))
+
+(defmacro defconstant (name value &optional doc)
+  "Make sure VALUE is evaluated only once \(to appease SBCL)."
+  `(cl:defconstant ,name (if (boundp ',name) (symbol-value ',name) ,value)
+     ,@(when doc (list doc))))
+
+(defun invert-table (table)
+  "`Inverts' an array which maps octets to character codes to a hash
+table which maps character codes to octets."
+  (let ((hash (make-hash-table)))
+    (loop for octet from 0
+          for char-code across table
+          unless (= char-code 65533)
+          do (setf (gethash char-code hash) octet))
+    hash))
+
+(defun make-decoding-table (list)
+  "Creates and returns an array which contains the elements in the
+list LIST and has an element type that's suitable for character
+codes."
+  (make-array (length list)
+              :element-type 'char-code-integer
+              :initial-contents list))
\ No newline at end of file
diff --git a/deps/flexi-streams/output.lisp b/deps/flexi-streams/output.lisp
new file mode 100644 (file)
index 0000000..894ae4e
--- /dev/null
@@ -0,0 +1,162 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/output.lisp,v 1.65 2008/05/24 23:15:25 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defgeneric write-byte* (byte stream)
+  (declare #.*standard-optimize-settings*)
+  (:documentation "Writes one byte \(octet) to the underlying stream
+STREAM."))
+
+#-:lispworks
+(defmethod write-byte* (byte (flexi-output-stream flexi-output-stream))  
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((stream flexi-stream-stream))
+      flexi-output-stream
+    (write-byte byte stream)))
+
+#+:lispworks
+(defmethod write-byte* (byte (flexi-output-stream flexi-output-stream))
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((stream flexi-stream-stream))
+      flexi-output-stream
+    (write-byte byte stream)))
+
+#+:lispworks
+(defmethod write-byte* (byte (flexi-output-stream flexi-char-output-stream))
+  "This method is only used for LispWorks bivalent streams which
+aren't binary."
+  (declare #.*standard-optimize-settings*)
+  ;; we use WRITE-SEQUENCE because WRITE-BYTE doesn't work with all
+  ;; bivalent streams in LispWorks (4.4.6)
+  (with-accessors ((stream flexi-stream-stream))
+      flexi-output-stream
+    (write-sequence (make-array 1 :element-type 'octet
+                                :initial-element byte)
+                    stream)
+    byte))
+
+(defmethod stream-write-char ((stream flexi-output-stream) char)
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((external-format flexi-stream-external-format))
+      stream
+    (flet ((writer (octet)
+             (write-byte* octet stream)))
+      (declare (dynamic-extent (function writer)))
+      (char-to-octets external-format char #'writer))))
+
+(defmethod stream-write-char :after ((stream flexi-output-stream) char)
+  (declare #.*standard-optimize-settings*)
+  ;; update the column unless we're in the middle of the line and
+  ;; the current value is NIL
+  (with-accessors ((column flexi-stream-column))
+      stream
+    (cond ((char= char #\Newline) (setq column 0))
+          (column (incf (the integer column))))))
+
+(defmethod stream-clear-output ((flexi-output-stream flexi-output-stream))
+  "Simply calls the corresponding method for the underlying
+output stream."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((stream flexi-stream-stream))
+      flexi-output-stream
+    (clear-output stream)))
+
+(defmethod stream-finish-output ((flexi-output-stream flexi-output-stream))
+  "Simply calls the corresponding method for the underlying
+output stream."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((stream flexi-stream-stream))
+      flexi-output-stream
+    (finish-output stream)))
+
+(defmethod stream-force-output ((flexi-output-stream flexi-output-stream))
+  "Simply calls the corresponding method for the underlying
+output stream."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((stream flexi-stream-stream))
+      flexi-output-stream
+    (force-output stream)))
+
+(defmethod stream-line-column ((flexi-output-stream flexi-output-stream))
+  "Returns the column stored in the COLUMN slot of the
+FLEXI-OUTPUT-STREAM object STREAM."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((column flexi-stream-column))
+      flexi-output-stream
+    column))
+
+(defmethod stream-write-byte ((flexi-output-stream flexi-output-stream) byte)
+  "Writes a byte \(octet) to the underlying stream."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((column flexi-stream-column))
+      flexi-output-stream
+    ;; set column to NIL because we don't know how to handle binary
+    ;; output mixed with character output
+    (setq column nil)
+    (write-byte* byte flexi-output-stream)))
+
+#+:allegro
+(defmethod stream-terpri ((stream flexi-output-stream))
+  "Writes a #\Newline character to the underlying stream."
+  (declare #.*standard-optimize-settings*)
+  ;; needed for AllegroCL - grrr...
+  (stream-write-char stream #\Newline))
+
+(defmethod stream-write-sequence ((flexi-output-stream flexi-output-stream) sequence start end &key)
+  "An optimized version which uses a buffer underneath.  The function
+can accepts characters as well as octets and it decides what to do
+based on the element type of the sequence \(if possible) or on the
+individual elements, i.e. you can mix characters and octets in
+SEQUENCE if you want.  Whether that really works might also depend on
+your Lisp, some of the implementations are more picky than others."
+  (declare #.*standard-optimize-settings*)
+  (declare (fixnum start end))
+  (with-accessors ((column flexi-stream-column)
+                   (external-format flexi-stream-external-format)
+                   (stream flexi-stream-stream))
+      flexi-output-stream
+    (when (>= start end)
+      (return-from stream-write-sequence sequence))
+    (when (and (vectorp sequence)
+               (subtypep (array-element-type sequence) 'integer))
+      ;; if this is pure binary output, just send all the stuff to the
+      ;; underlying stream directly and skip the rest
+      (setq column nil)
+      (return-from stream-write-sequence
+        (write-sequence sequence stream :start start :end end)))
+    ;; otherwise hand over to the external format to do the work
+    (write-sequence* external-format flexi-output-stream sequence start end))
+  sequence)
+
+(defmethod stream-write-string ((stream flexi-output-stream) string
+                                &optional (start 0) (end (length string)))
+  "Simply hands over to the optimized method for STREAM-WRITE-SEQUENCE."
+  (declare #.*standard-optimize-settings*)
+  (stream-write-sequence stream string start (or end (length string))))
diff --git a/deps/flexi-streams/packages.lisp b/deps/flexi-streams/packages.lisp
new file mode 100644 (file)
index 0000000..e012b02
--- /dev/null
@@ -0,0 +1,90 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/packages.lisp,v 1.39 2008/05/30 07:50:31 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(unless (find-symbol (symbol-name :stream-file-position) :trivial-gray-streams)
+  (error "You need a newer version of TRIVIAL-GRAY-STREAMS."))
+
+(defpackage :flexi-streams
+  (:use :cl :trivial-gray-streams)
+  (:nicknames :flex)  
+  (:shadow #+:lispworks :with-accessors
+           :defconstant)
+  (:export :*default-eol-style*
+           :*default-little-endian*
+           :*substitution-char*
+           :accept-overlong-sequence
+           :char-length
+           :external-format-condition
+           :external-format-condition-external-format
+           :external-format-eol-style
+           :external-format-error
+           :external-format-encoding-error
+           :external-format-equal
+           :external-format-id
+           :external-format-little-endian
+           :external-format-name
+           :flexi-input-stream
+           :flexi-output-stream
+           :flexi-io-stream
+           :flexi-stream
+           :flexi-stream-bound
+           :flexi-stream-column
+           :flexi-stream-external-format
+           :flexi-stream-element-type
+           :flexi-stream-element-type-error
+           :flexi-stream-element-type-error-element-type
+           :flexi-stream-error
+           :flexi-stream-out-of-sync-error
+           :flexi-stream-position
+           :flexi-stream-stream
+           :get-output-stream-sequence
+           :in-memory-stream
+           :in-memory-stream-closed-error
+           :in-memory-stream-error
+           :in-memory-stream-position-spec-error
+           :in-memory-stream-position-spec-error-position-spec
+           :in-memory-input-stream
+           :in-memory-output-stream
+           :list-stream
+           :make-external-format
+           :make-in-memory-input-stream
+           :make-in-memory-output-stream
+           :make-flexi-stream
+           :octet
+           :octet-length
+           :octets-to-string
+           :output-stream-sequence-length
+           :peek-byte
+           :string-to-octets
+           :unread-byte
+           :vector-stream
+           :with-input-from-sequence
+           :with-output-to-sequence))
diff --git a/deps/flexi-streams/specials.lisp b/deps/flexi-streams/specials.lisp
new file mode 100644 (file)
index 0000000..3b7a453
--- /dev/null
@@ -0,0 +1,201 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/specials.lisp,v 1.33 2008/05/25 01:40:54 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defvar *standard-optimize-settings*
+  '(optimize
+    speed
+    (safety 0)
+    (space 0)
+    (debug 1)
+    (compilation-speed 0))
+  "The standard optimize settings used by most declaration expressions.")
+
+(defvar *fixnum-optimize-settings*
+  '(optimize
+    speed
+    (safety 0)
+    (space 0)
+    (debug 1)
+    (compilation-speed 0)
+    #+:lispworks (hcl:fixnum-safety 0))
+  "Like *STANDARD-OPTIMIZE-SETTINGS*, but \(on LispWorks) with all
+arithmetic being fixnum arithmetic.")
+
+(defconstant +lf+ (char-code #\Linefeed))
+
+(defconstant +cr+ (char-code #\Return))
+
+(defvar *current-unreader* nil
+  "A unary function which might be called to `unread' a character
+\(i.e. the sequence of octets it represents).
+
+Used by the function OCTETS-TO-CHAR-CODE and must always be bound to a
+suitable functional object when this function is called.")
+
+(defvar +name-map+
+  '((:utf8 . :utf-8)
+    (:utf16 . :utf-16)
+    (:ucs2 . :utf-16)
+    (:ucs-2 . :utf-16)
+    (:unicode . :utf-16)
+    (:utf32 . :utf-32)
+    (:ucs4 . :utf-32)
+    (:ucs-4 . :utf-32)
+    (:ascii . :us-ascii)
+    (:koi8r . :koi8-r)
+    (:latin-1 . :iso-8859-1)
+    (:latin1 . :iso-8859-1)
+    (:latin-2 . :iso-8859-2)
+    (:latin2 . :iso-8859-2)
+    (:latin-3 . :iso-8859-3)
+    (:latin3 . :iso-8859-3)
+    (:latin-4 . :iso-8859-4)
+    (:latin4 . :iso-8859-4)
+    (:cyrillic . :iso-8859-5)
+    (:arabic . :iso-8859-6)
+    (:greek . :iso-8859-7)
+    (:hebrew . :iso-8859-8)
+    (:latin-5 . :iso-8859-9)
+    (:latin5 . :iso-8859-9)
+    (:latin-6 . :iso-8859-10)
+    (:latin6 . :iso-8859-10)
+    (:thai . :iso-8859-11)
+    (:latin-7 . :iso-8859-13)
+    (:latin7 . :iso-8859-13)
+    (:latin-8 . :iso-8859-14)
+    (:latin8 . :iso-8859-14)
+    (:latin-9 . :iso-8859-15)
+    (:latin9 . :iso-8859-15)
+    (:latin-0 . :iso-8859-15)
+    (:latin0 . :iso-8859-15)
+    (:latin-10 . :iso-8859-16)
+    (:latin10 . :iso-8859-16)
+    (:codepage . :code-page)
+    #+(and :lispworks :win32)
+    (win32:code-page . :code-page))
+  "An alist which mapes alternative names for external formats to
+their canonical counterparts.")
+
+(defvar +shortcut-map+
+  '((:ucs-2le . (:ucs-2 :little-endian t))
+    (:ucs-2be . (:ucs-2 :little-endian nil))
+    (:ucs-4le . (:ucs-4 :little-endian t))
+    (:ucs-4be . (:ucs-4 :little-endian nil))
+    (:utf-16le . (:utf-16 :little-endian t))
+    (:utf-16be . (:utf-16 :little-endian nil))
+    (:utf-32le . (:utf-32 :little-endian t))
+    (:utf-32be . (:utf-32 :little-endian nil))
+    (:ibm437 . (:code-page :id 437))
+    (:ibm850 . (:code-page :id 850))
+    (:ibm852 . (:code-page :id 852))
+    (:ibm855 . (:code-page :id 855))
+    (:ibm857 . (:code-page :id 857))
+    (:ibm860 . (:code-page :id 860))
+    (:ibm861 . (:code-page :id 861))
+    (:ibm862 . (:code-page :id 862))
+    (:ibm863 . (:code-page :id 863))
+    (:ibm864 . (:code-page :id 864))
+    (:ibm865 . (:code-page :id 865))
+    (:ibm866 . (:code-page :id 866))
+    (:ibm869 . (:code-page :id 869))
+    (:windows-1250 . (:code-page :id 1250))
+    (:windows-1251 . (:code-page :id 1251))
+    (:windows-1252 . (:code-page :id 1252))
+    (:windows-1253 . (:code-page :id 1253))
+    (:windows-1254 . (:code-page :id 1254))
+    (:windows-1255 . (:code-page :id 1255))
+    (:windows-1256 . (:code-page :id 1256))
+    (:windows-1257 . (:code-page :id 1257))
+    (:windows-1258 . (:code-page :id 1258)))
+  "An alist which maps shortcuts for external formats to their
+long forms.")
+    
+(defvar *default-eol-style*
+  #+:win32 :crlf
+  #-:win32 :lf
+  "The end-of-line style used by external formats if none is
+explicitly given.  Depends on the OS the code is compiled on.")
+
+(defvar *default-little-endian*
+  #+:little-endian t
+  #-:little-endian nil
+  "Whether external formats are little-endian by default
+\(i.e. unless explicitly specified).  Depends on the platform
+the code is compiled on.")
+
+(defvar *substitution-char* nil
+  "If this value is not NIL, it should be a character which is used
+\(as if by a USE-VALUE restart) whenever during reading an error of
+type FLEXI-STREAM-ENCODING-ERROR would have been signalled otherwise.")
+
+(defconstant +iso-8859-hashes+
+  (loop for (name . table) in +iso-8859-tables+
+        collect (cons name (invert-table table)))
+  "An alist which maps names for ISO-8859 encodings to hash
+tables which map character codes to the corresponding octets.")
+
+(defconstant +code-page-hashes+
+  (loop for (id . table) in +code-page-tables+
+        collect (cons id (invert-table table)))
+  "An alist which maps IDs of Windows code pages to hash tables
+which map character codes to the corresponding octets.")
+
+(defconstant +ascii-hash+ (invert-table +ascii-table+)
+  "A hash table which maps US-ASCII character codes to the
+corresponding octets.")
+
+(defconstant +koi8-r-hash+ (invert-table +koi8-r-table+)
+  "A hash table which maps KOI8-R character codes to the
+corresponding octets.")
+
+(defconstant +buffer-size+ 8192
+  "Default size for buffers used for internal purposes.")
+
+(pushnew :flexi-streams *features*)
+
+;; stuff for Nikodemus Siivola's HYPERDOC
+;; see <http://common-lisp.net/project/hyperdoc/>
+;; and <http://www.cliki.net/hyperdoc>
+;; also used by LW-ADD-ONS
+
+(defvar *hyperdoc-base-uri* "http://weitz.de/flexi-streams/")
+
+(let ((exported-symbols-alist
+       (loop for symbol being the external-symbols of :flexi-streams
+             collect (cons symbol
+                           (concatenate 'string
+                                        "#"
+                                        (string-downcase symbol))))))
+  (defun hyperdoc-lookup (symbol type)
+    (declare (ignore type))
+    (cdr (assoc symbol
+                exported-symbols-alist
+                :test #'eq))))
diff --git a/deps/flexi-streams/stream.lisp b/deps/flexi-streams/stream.lisp
new file mode 100644 (file)
index 0000000..cd827fd
--- /dev/null
@@ -0,0 +1,241 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/stream.lisp,v 1.61 2008/05/19 22:32:56 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defclass flexi-stream (trivial-gray-stream-mixin)
+  ((stream :initarg :stream
+           :reader flexi-stream-stream
+           :documentation "The actual stream that's used for
+input and/or output.  It must be capable of reading/writing
+octets with READ-SEQUENCE and/or WRITE-SEQUENCE.")
+   (external-format :initform (make-external-format :iso-8859-1)
+                    :initarg :flexi-stream-external-format
+                    :accessor flexi-stream-external-format
+                    :documentation "The encoding currently used
+by this stream.  Can be changed on the fly.")
+   (element-type :initform 'char*
+                 :initarg :element-type
+                 :accessor flexi-stream-element-type
+                 :documentation "The element type of this stream."))
+  (:documentation "A FLEXI-STREAM object is a stream that's
+`layered' atop an existing binary/bivalent stream in order to
+allow for multi-octet external formats.  FLEXI-STREAM itself is a
+mixin and should not be instantiated."))
+
+(defmethod initialize-instance :after ((flexi-stream flexi-stream) &rest initargs)
+  "Makes sure the EXTERNAL-FORMAT and ELEMENT-TYPE slots contain
+reasonable values."
+  (declare #.*standard-optimize-settings*)
+  (declare (ignore initargs))
+  (with-accessors ((external-format flexi-stream-external-format)
+                   (element-type flexi-stream-element-type))
+      flexi-stream
+    (unless (or (subtypep element-type 'character)
+                (subtypep element-type 'octet))
+      (error 'flexi-stream-element-type-error
+             :element-type element-type
+             :stream flexi-stream))
+    (setq external-format (maybe-convert-external-format external-format))))
+
+(defmethod (setf flexi-stream-external-format) :around (new-value (flexi-stream flexi-stream))
+  "Converts the new value to an EXTERNAL-FORMAT object if
+necessary."
+  (declare #.*standard-optimize-settings*)
+  (call-next-method (maybe-convert-external-format new-value) flexi-stream))
+
+(defmethod (setf flexi-stream-element-type) :before (new-value (flexi-stream flexi-stream))
+  "Checks whether the new value makes sense before it is set."
+  (declare #.*standard-optimize-settings*)
+  (unless (or (subtypep new-value 'character)
+              (type-equal new-value 'octet))
+    (error 'flexi-stream-element-type-error
+           :element-type new-value
+           :stream flexi-stream)))
+
+(defmethod stream-element-type ((stream flexi-stream))
+  "Returns the element type that was provided by the creator of
+the stream."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((element-type flexi-stream-element-type))
+      stream
+    element-type))
+
+(defmethod close ((stream flexi-stream) &key abort)
+  "Closes the flexi stream by closing the underlying `real'
+stream."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((stream flexi-stream-stream))
+      stream
+    (cond ((open-stream-p stream)
+           (close stream :abort abort))
+          (t nil))))
+
+(defmethod open-stream-p ((stream flexi-stream))
+  "A flexi stream is open if its underlying stream is open."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((stream flexi-stream-stream))
+      stream
+    (open-stream-p stream)))
+
+(defmethod stream-file-position ((stream flexi-stream))
+  "Dispatch to method for underlying stream."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((stream flexi-stream-stream))
+      stream
+    (file-position stream)))
+
+(defmethod (setf stream-file-position) (position-spec (stream flexi-stream))
+  "Dispatch to method for underlying stream."
+  (declare #.*standard-optimize-settings*)
+  (with-accessors ((underlying-stream flexi-stream-stream))
+      stream
+    (if (file-position underlying-stream position-spec)
+        (setf (flexi-stream-position stream) (file-position underlying-stream))
+          nil)))
+
+(defclass flexi-output-stream (flexi-stream fundamental-binary-output-stream
+                                            fundamental-character-output-stream)
+  ((column :initform 0
+           :accessor flexi-stream-column
+           :documentation "The current output column.  A
+non-negative integer or NIL."))
+  (:documentation "A FLEXI-OUTPUT-STREAM is a FLEXI-STREAM that
+can actually be instatiated and used for output.  Don't use
+MAKE-INSTANCE to create a new FLEXI-OUTPUT-STREAM but use
+MAKE-FLEXI-STREAM instead."))
+
+#+:cmu
+(defmethod input-stream-p ((stream flexi-output-stream))
+  "Explicitly states whether this is an input stream."
+  (declare #.*standard-optimize-settings*)
+  nil)
+
+(defclass flexi-input-stream (flexi-stream fundamental-binary-input-stream
+                                           fundamental-character-input-stream)
+  ((last-char-code :initform nil
+                   :accessor flexi-stream-last-char-code
+                   :documentation "This slot either holds NIL or the
+last character \(code) read successfully.  This is mainly used for
+UNREAD-CHAR sanity checks.")
+   (last-octet :initform nil
+               :accessor flexi-stream-last-octet
+               :documentation "This slot either holds NIL or the last
+octet read successfully from the stream using a `binary' operation
+such as READ-BYTE.  This is mainly used for UNREAD-BYTE sanity
+checks.")
+   (octet-stack :initform nil
+                :accessor flexi-stream-octet-stack
+                :documentation "A small buffer which holds octets
+that were already read from the underlying stream but not yet
+used to produce characters.  This is mainly used if we have to
+look ahead for a CR/LF line ending.")
+   (position :initform 0
+             :initarg :position
+             :type integer
+             :accessor flexi-stream-position
+             :documentation "The position within the stream where each
+octet read counts as one.")
+   (bound :initform nil
+          :initarg :bound
+          :type (or null integer)
+          :accessor flexi-stream-bound
+          :documentation "When this is not NIL, it must be an integer
+and the stream will behave as if no more data is available as soon as
+POSITION is greater or equal than this value."))
+  (:documentation "A FLEXI-INPUT-STREAM is a FLEXI-STREAM that
+can actually be instatiated and used for input.  Don't use
+MAKE-INSTANCE to create a new FLEXI-INPUT-STREAM but use
+MAKE-FLEXI-STREAM instead."))
+
+#+:cmu
+(defmethod output-stream-p ((stream flexi-input-stream))
+  "Explicitly states whether this is an output stream."
+  (declare #.*standard-optimize-settings*)
+  nil)
+
+(defclass flexi-io-stream (flexi-input-stream flexi-output-stream)
+  ()
+  (:documentation "A FLEXI-IO-STREAM is a FLEXI-STREAM that can
+actually be instatiated and used for input and output.  Don't use
+MAKE-INSTANCE to create a new FLEXI-IO-STREAM but use
+MAKE-FLEXI-STREAM instead."))
+
+#+:cmu
+(defmethod input-stream-p ((stream flexi-io-stream))
+  "Explicitly states whether this is an input stream."
+  (declare #.*standard-optimize-settings*)
+  t)
+
+#+:cmu
+(defmethod output-stream-p ((stream flexi-io-stream))
+  "Explicitly states whether this is an output stream."
+  (declare #.*standard-optimize-settings*)
+  t)
+
+(defun make-flexi-stream (stream &rest args
+                                 &key (external-format (make-external-format :iso-8859-1))
+                                      element-type column position bound)
+  "Creates and returns a new flexi stream.  STREAM must be an open
+binary or `bivalent' stream, i.e. it must be capable of
+reading/writing octets with READ-SEQUENCE and/or WRITE-SEQUENCE.  The
+resulting flexi stream is an input stream if and only if STREAM is an
+input stream.  Likewise, it's an output stream if and only if STREAM
+is an output stream.  The default for ELEMENT-TYPE is LW:SIMPLE-CHAR
+on LispWorks and CHARACTER on other Lisps.  EXTERNAL-FORMAT must be an
+EXTERNAL-FORMAT object or a symbol or a list denoting such an object.
+COLUMN is the initial column of the stream which is either a
+non-negative integer or NIL.  The COLUMN argument must only be used
+for output streams.  POSITION \(only used for input streams) should be
+an integer and it denotes the position the stream is in - it will be
+increased by one for each octet read.  BOUND \(only used for input
+streams) should be NIL or an integer.  If BOUND is not NIL and
+POSITION has gone beyond BOUND, then the stream will behave as if no
+more input is available."
+  (declare #.*standard-optimize-settings*)
+  ;; these arguments are ignored - they are only there to provide a
+  ;; meaningful parameter list for IDEs
+  (declare (ignore element-type column position bound))
+  (unless (and (streamp stream)
+               (open-stream-p stream))
+    (error "~S should have been an open stream." stream))
+  (apply #'make-instance
+         ;; actual type depends on STREAM
+         (cond ((and (input-stream-p stream)
+                     (output-stream-p stream))
+                'flexi-io-stream)
+               ((input-stream-p stream)
+                'flexi-input-stream)
+               ((output-stream-p stream)
+                'flexi-output-stream)
+               (t
+                (error "~S is neither an input nor an output stream." stream)))
+         :stream stream
+         :flexi-stream-external-format external-format
+         (sans args :external-format)))
diff --git a/deps/flexi-streams/strings.lisp b/deps/flexi-streams/strings.lisp
new file mode 100644 (file)
index 0000000..d12ca02
--- /dev/null
@@ -0,0 +1,82 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/strings.lisp,v 1.34 2008/05/26 10:55:08 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+(defun string-to-octets (string &key
+                                (external-format :latin1)
+                                (start 0) (end (length string)))
+  "Converts the Lisp string STRING from START to END to an array of
+octets corresponding to the external format designated by
+EXTERNAL-FORMAT.
+
+In spite of the name, STRING can be any sequence of characters, but
+the function is optimized for strings."
+  (declare #.*standard-optimize-settings*)
+  (setq external-format (maybe-convert-external-format external-format))
+  ;; the external format knows how to do it...
+  (string-to-octets* external-format string start end))
+
+(defun octets-to-string (sequence &key
+                                  (external-format :latin1)
+                                  (start 0) (end (length sequence)))
+  "Converts the Lisp sequence SEQUENCE of octets from START to END to
+a string using the external format designated by EXTERNAL-FORMAT.
+
+This function is optimized for the case of SEQUENCE being a vector.
+Don't use lists if you're in a hurry."
+  (declare #.*standard-optimize-settings*)
+  (declare (fixnum start end))
+  (setq external-format (maybe-convert-external-format external-format))
+  ;; the external format knows how to do it...
+  (octets-to-string* external-format sequence start end))
+
+(defun octet-length (string &key (external-format :latin1) (start 0) (end (length string)))
+  "Returns the length of the substring of STRING from START to END in
+octets if encoded using the external format EXTERNAL-FORMAT.
+
+In spite of the name, STRING can be any sequence of characters, but
+the function is optimized for strings."
+  (declare #.*standard-optimize-settings*)
+  (declare (fixnum start end))
+  (setq external-format (maybe-convert-external-format external-format))
+  (compute-number-of-octets external-format string start end))
+
+(defun char-length (sequence &key (external-format :latin1) (start 0) (end (length sequence)))
+  "Kind of the inverse of OCTET-LENGTH.  Returns the length of the
+subsequence \(of octets) of SEQUENCE from START to END in characters
+if decoded using the external format EXTERNAL-FORMAT.  Note that this
+function doesn't check for the validity of the data in SEQUENCE.
+
+This function is optimized for the case of SEQUENCE being a vector.
+Don't use lists if you're in a hurry."
+  (declare #.*standard-optimize-settings*)
+  (declare (fixnum start end))
+  (setq external-format (maybe-convert-external-format external-format))
+  (compute-number-of-chars external-format sequence start end))
diff --git a/deps/flexi-streams/test/README b/deps/flexi-streams/test/README
new file mode 100644 (file)
index 0000000..9f4eba9
--- /dev/null
@@ -0,0 +1,4 @@
+The reference files in this directory were created/converted using a
+mixture of GNU recode and the native internationalization facilities
+of LispWorks and AllegroCL, i.e. we're not testing FLEXI-STREAMS
+against files created by itself (which would be kind of useless).
\ No newline at end of file
diff --git a/deps/flexi-streams/test/hebrew_latin8_cr.txt b/deps/flexi-streams/test/hebrew_latin8_cr.txt
new file mode 100644 (file)
index 0000000..4017a49
--- /dev/null
@@ -0,0 +1 @@
+:õøàä úàå íéîùä úà íéäìà àøá úéùàøá    à  1\ríåäú éðô-ìò êùçå åäáå åäú äúéä õøàäå       á  2\r:íéîä éðô-ìò úôçøî íéäìà çåøå\r:øåà-éäéå øåà éäé íéäìà øîàéå        â  3\ríéäìà ìãáéå áåè-éë øåàä-úà íéäìà àøéå      ã  4\r:êùçä ïéáå øåàä ïéá\räìéì àø÷ êùçìå íåé øåàì íéäìà àø÷éå    ä  5\r:ãçà íåé ø÷á-éäéå áøò-éäéå\réäéå íéîä êåúá òé÷ø éäé íéäìà øîàéå     å  6\r:íéîì íéî ïéá ìéãáî\røùà íéîä ïéá ìãáéå òé÷øä-úà íéäìà ùòéå         æ  7\ròé÷øì ìòî øùà íéîä ïéáå òé÷øì úçúî\r:ïë-éäéå\rø÷á-éäéå áøò-éäéå íéîù òé÷øì íéäìà àø÷éå       ç  8\r:éðù íåé\ríå÷î-ìà íéîùä úçúî íéîä åå÷é íéäìà øîàéå  è  9\r:ïë-éäéå äùáéä äàøúå ãçà\ràø÷ íéîä äå÷îìå õøà äùáéì íéäìà àø÷éå     é  10\r:áåè-éë íéäìà àøéå íéîé\ròøæ òéøæî áùò àùã õøàä àùãú íéäìà øîàéå   àé  11\rõøàä-ìò åá-åòøæ øùà åðéîì éøô äùò éøô õò\r:ïë-éäéå\rõòå åäðéîì òøæ òéøæî áùò àùã õøàä àöåúå        áé  12\ríéäìà àøéå åäðéîì åá-åòøæ øùà éøô-äùò\r:áåè-éë\r:éùéìù íåé ø÷á-éäéå áøò-éäéå       âé  13\rìéãáäì íéîùä òé÷øá úøàî éäé íéäìà øîàéå  ãé  14\ríéãòåîìå úúàì åéäå äìéìä ïéáå íåéä ïéá\r:íéðùå íéîéìå\rõøàä-ìò øéàäì íéîùä òé÷øá úøåàîì åéäå       åè  15\r:ïë-éäéå\røåàîä-úà íéìãâä úøàîä éðù-úà íéäìà ùòéå         æè  16\rúìùîîì ïè÷ä øåàîä-úàå íåéä úìùîîì ìãâä\r:íéáëåëä úàå äìéìä\røéàäì íéîùä òé÷øá íéäìà íúà ïúéå       æé  17\r:õøàä-ìò\rïéáå øåàä ïéá ìéãáäìå äìéìáå íåéá ìùîìå         çé  18\r:áåè-éë íéäìà àøéå êùçä\r:éòéáø íåé ø÷á-éäéå áøò-éäéå     èé  19\róåòå äéç ùôð õøù íéîä åöøùé íéäìà øîàéå  ë  20\r:íéîùä òé÷ø éðô-ìò õøàä-ìò óôåòé\rùôð-ìë úàå íéìãâä íðéðúä-úà íéäìà àøáéå  àë  21\rúàå íäðéîì íéîä åöøù øùà úùîøä äéçä\r:áåè-éë íéäìà àøéå åäðéîì óðë óåò-ìë\råàìîå åáøå åøô øîàì íéäìà íúà êøáéå     áë  22\r:õøàá áøé óåòäå íéîéá íéîä-úà\r:éùéîç íåé ø÷á-éäéå áøò-éäéå       âë  23\räîäá äðéîì äéç ùôð õøàä àöåú íéäìà øîàéå         ãë  24\r:ïë-éäéå äðéîì õøà-åúéçå ùîøå\räîäáä-úàå äðéîì õøàä úéç-úà íéäìà ùòéå     äë  25\ríéäìà àøéå åäðéîì äîãàä ùîø-ìë úàå äðéîì\r:áåè-éë\råðúåîãë åðîìöá íãà äùòð íéäìà øîàéå     åë  26\räîäááå íéîùä óåòáå íéä úâãá åãøéå\r:õøàä-ìò ùîøä ùîøä-ìëáå õøàä-ìëáå\ràøá íéäìà íìöá åîìöá íãàä-úà íéäìà àøáéå     æë  27\r:íúà àøá äá÷ðå øëæ åúà\råáøå åøô íéäìà íäì øîàéå íéäìà íúà êøáéå  çë  28\róåòáå íéä úâãá åãøå äùáëå õøàä-úà åàìîå\r:õøàä-ìò úùîøä äéç-ìëáå íéîùä\ròøæ áùò-ìë-úà íëì éúúð äðä íéäìà øîàéå     èë  29\råá-øùà õòä-ìë-úàå õøàä-ìë éðô-ìò øùà òøæ\r:äìëàì äéäé íëì òøæ òøæ õò-éøô\rùîåø ìëìå íéîùä óåò-ìëìå õøàä úéç-ìëìå   ì  30\ráùò ÷øé-ìë-úà äéç ùôð åá-øùà õøàä-ìò\r:ïë-éäéå äìëàì\rãàî áåè-äðäå äùò øùà-ìë-úà íéäìà àøéå         àì  31\r:éùùä íåé ø÷á-éäéå áø\r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/hebrew_latin8_crlf.txt b/deps/flexi-streams/test/hebrew_latin8_crlf.txt
new file mode 100644 (file)
index 0000000..06d86d8
--- /dev/null
@@ -0,0 +1,68 @@
+:õøàä úàå íéîùä úà íéäìà àøá úéùàøá    à  1\r
+íåäú éðô-ìò êùçå åäáå åäú äúéä õøàäå   á  2\r
+:íéîä éðô-ìò úôçøî íéäìà çåøå\r
+:øåà-éäéå øåà éäé íéäìà øîàéå  â  3\r
+íéäìà ìãáéå áåè-éë øåàä-úà íéäìà àøéå  ã  4\r
+:êùçä ïéáå øåàä ïéá\r
+äìéì àø÷ êùçìå íåé øåàì íéäìà àø÷éå    ä  5\r
+:ãçà íåé ø÷á-éäéå áøò-éäéå\r
+éäéå íéîä êåúá òé÷ø éäé íéäìà øîàéå    å  6\r
+:íéîì íéî ïéá ìéãáî\r
+øùà íéîä ïéá ìãáéå òé÷øä-úà íéäìà ùòéå         æ  7\r
+òé÷øì ìòî øùà íéîä ïéáå òé÷øì úçúî\r
+:ïë-éäéå\r
+ø÷á-éäéå áøò-éäéå íéîù òé÷øì íéäìà àø÷éå       ç  8\r
+:éðù íåé\r
+íå÷î-ìà íéîùä úçúî íéîä åå÷é íéäìà øîàéå       è  9\r
+:ïë-éäéå äùáéä äàøúå ãçà\r
+àø÷ íéîä äå÷îìå õøà äùáéì íéäìà àø÷éå  é  10\r
+:áåè-éë íéäìà àøéå íéîé\r
+òøæ òéøæî áùò àùã õøàä àùãú íéäìà øîàéå        àé  11\r
+õøàä-ìò åá-åòøæ øùà åðéîì éøô äùò éøô õò\r
+:ïë-éäéå\r
+õòå åäðéîì òøæ òéøæî áùò àùã õøàä àöåúå        áé  12\r
+íéäìà àøéå åäðéîì åá-åòøæ øùà éøô-äùò\r
+:áåè-éë\r
+:éùéìù íåé ø÷á-éäéå áøò-éäéå   âé  13\r
+ìéãáäì íéîùä òé÷øá úøàî éäé íéäìà øîàéå        ãé  14\r
+íéãòåîìå úúàì åéäå äìéìä ïéáå íåéä ïéá\r
+:íéðùå íéîéìå\r
+õøàä-ìò øéàäì íéîùä òé÷øá úøåàîì åéäå  åè  15\r
+:ïë-éäéå\r
+øåàîä-úà íéìãâä úøàîä éðù-úà íéäìà ùòéå        æè  16\r
+úìùîîì ïè÷ä øåàîä-úàå íåéä úìùîîì ìãâä\r
+:íéáëåëä úàå äìéìä\r
+øéàäì íéîùä òé÷øá íéäìà íúà ïúéå       æé  17\r
+:õøàä-ìò\r
+ïéáå øåàä ïéá ìéãáäìå äìéìáå íåéá ìùîìå        çé  18\r
+:áåè-éë íéäìà àøéå êùçä\r
+:éòéáø íåé ø÷á-éäéå áøò-éäéå   èé  19\r
+óåòå äéç ùôð õøù íéîä åöøùé íéäìà øîàéå        ë  20\r
+:íéîùä òé÷ø éðô-ìò õøàä-ìò óôåòé\r
+ùôð-ìë úàå íéìãâä íðéðúä-úà íéäìà àøáéå        àë  21\r
+úàå íäðéîì íéîä åöøù øùà úùîøä äéçä\r
+:áåè-éë íéäìà àøéå åäðéîì óðë óåò-ìë\r
+åàìîå åáøå åøô øîàì íéäìà íúà êøáéå    áë  22\r
+:õøàá áøé óåòäå íéîéá íéîä-úà\r
+:éùéîç íåé ø÷á-éäéå áøò-éäéå   âë  23\r
+äîäá äðéîì äéç ùôð õøàä àöåú íéäìà øîàéå       ãë  24\r
+:ïë-éäéå äðéîì õøà-åúéçå ùîøå\r
+äîäáä-úàå äðéîì õøàä úéç-úà íéäìà ùòéå         äë  25\r
+íéäìà àøéå åäðéîì äîãàä ùîø-ìë úàå äðéîì\r
+:áåè-éë\r
+åðúåîãë åðîìöá íãà äùòð íéäìà øîàéå    åë  26\r
+äîäááå íéîùä óåòáå íéä úâãá åãøéå\r
+:õøàä-ìò ùîøä ùîøä-ìëáå õøàä-ìëáå\r
+àøá íéäìà íìöá åîìöá íãàä-úà íéäìà àøáéå       æë  27\r
+:íúà àøá äá÷ðå øëæ åúà\r
+åáøå åøô íéäìà íäì øîàéå íéäìà íúà êøáéå       çë  28\r
+óåòáå íéä úâãá åãøå äùáëå õøàä-úà åàìîå\r
+:õøàä-ìò úùîøä äéç-ìëáå íéîùä\r
+òøæ áùò-ìë-úà íëì éúúð äðä íéäìà øîàéå         èë  29\r
+åá-øùà õòä-ìë-úàå õøàä-ìë éðô-ìò øùà òøæ\r
+:äìëàì äéäé íëì òøæ òøæ õò-éøô\r
+ùîåø ìëìå íéîùä óåò-ìëìå õøàä úéç-ìëìå         ì  30\r
+áùò ÷øé-ìë-úà äéç ùôð åá-øùà õøàä-ìò\r
+:ïë-éäéå äìëàì\r
+ãàî áåè-äðäå äùò øùà-ìë-úà íéäìà àøéå  àì  31\r
+:éùùä íåé ø÷á-éäéå áø\r
diff --git a/deps/flexi-streams/test/hebrew_latin8_lf.txt b/deps/flexi-streams/test/hebrew_latin8_lf.txt
new file mode 100644 (file)
index 0000000..bcc4c77
--- /dev/null
@@ -0,0 +1,68 @@
+:õøàä úàå íéîùä úà íéäìà àøá úéùàøá    à  1
+íåäú éðô-ìò êùçå åäáå åäú äúéä õøàäå   á  2
+:íéîä éðô-ìò úôçøî íéäìà çåøå
+:øåà-éäéå øåà éäé íéäìà øîàéå  â  3
+íéäìà ìãáéå áåè-éë øåàä-úà íéäìà àøéå  ã  4
+:êùçä ïéáå øåàä ïéá
+äìéì àø÷ êùçìå íåé øåàì íéäìà àø÷éå    ä  5
+:ãçà íåé ø÷á-éäéå áøò-éäéå
+éäéå íéîä êåúá òé÷ø éäé íéäìà øîàéå    å  6
+:íéîì íéî ïéá ìéãáî
+øùà íéîä ïéá ìãáéå òé÷øä-úà íéäìà ùòéå         æ  7
+òé÷øì ìòî øùà íéîä ïéáå òé÷øì úçúî
+:ïë-éäéå
+ø÷á-éäéå áøò-éäéå íéîù òé÷øì íéäìà àø÷éå       ç  8
+:éðù íåé
+íå÷î-ìà íéîùä úçúî íéîä åå÷é íéäìà øîàéå       è  9
+:ïë-éäéå äùáéä äàøúå ãçà
+àø÷ íéîä äå÷îìå õøà äùáéì íéäìà àø÷éå  é  10
+:áåè-éë íéäìà àøéå íéîé
+òøæ òéøæî áùò àùã õøàä àùãú íéäìà øîàéå        àé  11
+õøàä-ìò åá-åòøæ øùà åðéîì éøô äùò éøô õò
+:ïë-éäéå
+õòå åäðéîì òøæ òéøæî áùò àùã õøàä àöåúå        áé  12
+íéäìà àøéå åäðéîì åá-åòøæ øùà éøô-äùò
+:áåè-éë
+:éùéìù íåé ø÷á-éäéå áøò-éäéå   âé  13
+ìéãáäì íéîùä òé÷øá úøàî éäé íéäìà øîàéå        ãé  14
+íéãòåîìå úúàì åéäå äìéìä ïéáå íåéä ïéá
+:íéðùå íéîéìå
+õøàä-ìò øéàäì íéîùä òé÷øá úøåàîì åéäå  åè  15
+:ïë-éäéå
+øåàîä-úà íéìãâä úøàîä éðù-úà íéäìà ùòéå        æè  16
+úìùîîì ïè÷ä øåàîä-úàå íåéä úìùîîì ìãâä
+:íéáëåëä úàå äìéìä
+øéàäì íéîùä òé÷øá íéäìà íúà ïúéå       æé  17
+:õøàä-ìò
+ïéáå øåàä ïéá ìéãáäìå äìéìáå íåéá ìùîìå        çé  18
+:áåè-éë íéäìà àøéå êùçä
+:éòéáø íåé ø÷á-éäéå áøò-éäéå   èé  19
+óåòå äéç ùôð õøù íéîä åöøùé íéäìà øîàéå        ë  20
+:íéîùä òé÷ø éðô-ìò õøàä-ìò óôåòé
+ùôð-ìë úàå íéìãâä íðéðúä-úà íéäìà àøáéå        àë  21
+úàå íäðéîì íéîä åöøù øùà úùîøä äéçä
+:áåè-éë íéäìà àøéå åäðéîì óðë óåò-ìë
+åàìîå åáøå åøô øîàì íéäìà íúà êøáéå    áë  22
+:õøàá áøé óåòäå íéîéá íéîä-úà
+:éùéîç íåé ø÷á-éäéå áøò-éäéå   âë  23
+äîäá äðéîì äéç ùôð õøàä àöåú íéäìà øîàéå       ãë  24
+:ïë-éäéå äðéîì õøà-åúéçå ùîøå
+äîäáä-úàå äðéîì õøàä úéç-úà íéäìà ùòéå         äë  25
+íéäìà àøéå åäðéîì äîãàä ùîø-ìë úàå äðéîì
+:áåè-éë
+åðúåîãë åðîìöá íãà äùòð íéäìà øîàéå    åë  26
+äîäááå íéîùä óåòáå íéä úâãá åãøéå
+:õøàä-ìò ùîøä ùîøä-ìëáå õøàä-ìëáå
+àøá íéäìà íìöá åîìöá íãàä-úà íéäìà àøáéå       æë  27
+:íúà àøá äá÷ðå øëæ åúà
+åáøå åøô íéäìà íäì øîàéå íéäìà íúà êøáéå       çë  28
+óåòáå íéä úâãá åãøå äùáëå õøàä-úà åàìîå
+:õøàä-ìò úùîøä äéç-ìëáå íéîùä
+òøæ áùò-ìë-úà íëì éúúð äðä íéäìà øîàéå         èë  29
+åá-øùà õòä-ìë-úàå õøàä-ìë éðô-ìò øùà òøæ
+:äìëàì äéäé íëì òøæ òøæ õò-éøô
+ùîåø ìëìå íéîùä óåò-ìëìå õøàä úéç-ìëìå         ì  30
+áùò ÷øé-ìë-úà äéç ùôð åá-øùà õøàä-ìò
+:ïë-éäéå äìëàì
+ãàî áåè-äðäå äùò øùà-ìë-úà íéäìà àøéå  àì  31
+:éùùä íåé ø÷á-éäéå áø
diff --git a/deps/flexi-streams/test/hebrew_utf8_cr.txt b/deps/flexi-streams/test/hebrew_utf8_cr.txt
new file mode 100644 (file)
index 0000000..b55e719
--- /dev/null
@@ -0,0 +1 @@
+:ץראה תאו םימשה תא םיהלא ארב תישארב        א  1\rםוהת ינפ-לע ךשחו והבו והת התיה ץראהו         ב  2\r:םימה ינפ-לע תפחרמ םיהלא חורו\r:רוא-יהיו רוא יהי םיהלא רמאיו         ג  3\rםיהלא לדביו בוט-יכ רואה-תא םיהלא אריו       ד  4\r:ךשחה ןיבו רואה ןיב\rהליל ארק ךשחלו םוי רואל םיהלא ארקיו       ה  5\r:דחא םוי רקב-יהיו ברע-יהיו\rיהיו םימה ךותב עיקר יהי םיהלא רמאיו   ו  6\r:םימל םימ ןיב לידבמ\rרשא םימה ןיב לדביו עיקרה-תא םיהלא שעיו  ז  7\rעיקרל לעמ רשא םימה ןיבו עיקרל תחתמ\r:ןכ-יהיו\rרקב-יהיו ברע-יהיו םימש עיקרל םיהלא ארקיו   ח  8\r:ינש םוי\rםוקמ-לא םימשה תחתמ םימה ווקי םיהלא רמאיו  ט  9\r:ןכ-יהיו השביה הארתו דחא\rארק םימה הוקמלו ץרא השביל םיהלא ארקיו  י  10\r:בוט-יכ םיהלא אריו םימי\rערז עירזמ בשע אשד ץראה אשדת םיהלא רמאיו        אי  11\rץראה-לע וב-וערז רשא ונימל ירפ השע ירפ ץע\r:ןכ-יהיו\rץעו והנימל ערז עירזמ בשע אשד ץראה אצותו         בי  12\rםיהלא אריו והנימל וב-וערז רשא ירפ-השע\r:בוט-יכ\r:ישילש םוי רקב-יהיו ברע-יהיו    גי  13\rלידבהל םימשה עיקרב תראמ יהי םיהלא רמאיו       די  14\rםידעומלו תתאל ויהו הלילה ןיבו םויה ןיב\r:םינשו םימילו\rץראה-לע ריאהל םימשה עיקרב תרואמל ויהו   וט  15\r:ןכ-יהיו\rרואמה-תא םילדגה תראמה ינש-תא םיהלא שעיו         זט  16\rתלשממל ןטקה רואמה-תאו םויה תלשממל לדגה\r:םיבכוכה תאו הלילה\rריאהל םימשה עיקרב םיהלא םתא ןתיו   זי  17\r:ץראה-לע\rןיבו רואה ןיב לידבהלו הלילבו םויב לשמלו        חי  18\r:בוט-יכ םיהלא אריו ךשחה\r:יעיבר םוי רקב-יהיו ברע-יהיו   טי  19\rףועו היח שפנ ץרש םימה וצרשי םיהלא רמאיו        כ  20\r:םימשה עיקר ינפ-לע ץראה-לע ףפועי\rשפנ-לכ תאו םילדגה םנינתה-תא םיהלא ארביו        אכ  21\rתאו םהנימל םימה וצרש רשא תשמרה היחה\r:בוט-יכ םיהלא אריו והנימל ףנכ ףוע-לכ\rואלמו וברו ורפ רמאל םיהלא םתא ךרביו     בכ  22\r:ץראב ברי ףועהו םימיב םימה-תא\r:ישימח םוי רקב-יהיו ברע-יהיו        גכ  23\rהמהב הנימל היח שפנ ץראה אצות םיהלא רמאיו      דכ  24\r:ןכ-יהיו הנימל ץרא-ותיחו שמרו\rהמהבה-תאו הנימל ץראה תיח-תא םיהלא שעיו     הכ  25\rםיהלא אריו והנימל המדאה שמר-לכ תאו הנימל\r:בוט-יכ\rונתומדכ ונמלצב םדא השענ םיהלא רמאיו       וכ  26\rהמהבבו םימשה ףועבו םיה תגדב ודריו\r:ץראה-לע שמרה שמרה-לכבו ץראה-לכבו\rארב םיהלא םלצב ומלצב םדאה-תא םיהלא ארביו    זכ  27\r:םתא ארב הבקנו רכז ותא\rוברו ורפ םיהלא םהל רמאיו םיהלא םתא ךרביו      חכ  28\rףועבו םיה תגדב ודרו השבכו ץראה-תא ואלמו\r:ץראה-לע תשמרה היח-לכבו םימשה\rערז בשע-לכ-תא םכל יתתנ הנה םיהלא רמאיו      טכ  29\rוב-רשא ץעה-לכ-תאו ץראה-לכ ינפ-לע רשא ערז\r:הלכאל היהי םכל ערז ערז ץע-ירפ\rשמור לכלו םימשה ףוע-לכלו ץראה תיח-לכלו     ל  30\rבשע קרי-לכ-תא היח שפנ וב-רשא ץראה-לע\r:ןכ-יהיו הלכאל\rדאמ בוט-הנהו השע רשא-לכ-תא םיהלא אריו     אל  31\r:יששה םוי רקב-יהיו בר\r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/hebrew_utf8_crlf.txt b/deps/flexi-streams/test/hebrew_utf8_crlf.txt
new file mode 100644 (file)
index 0000000..c7d27eb
--- /dev/null
@@ -0,0 +1,68 @@
+:ץראה תאו םימשה תא םיהלא ארב תישארב        א  1\r
+םוהת ינפ-לע ךשחו והבו והת התיה ץראהו      ב  2\r
+:םימה ינפ-לע תפחרמ םיהלא חורו\r
+:רוא-יהיו רוא יהי םיהלא רמאיו   ג  3\r
+םיהלא לדביו בוט-יכ רואה-תא םיהלא אריו    ד  4\r
+:ךשחה ןיבו רואה ןיב\r
+הליל ארק ךשחלו םוי רואל םיהלא ארקיו       ה  5\r
+:דחא םוי רקב-יהיו ברע-יהיו\r
+יהיו םימה ךותב עיקר יהי םיהלא רמאיו       ו  6\r
+:םימל םימ ןיב לידבמ\r
+רשא םימה ןיב לדביו עיקרה-תא םיהלא שעיו  ז  7\r
+עיקרל לעמ רשא םימה ןיבו עיקרל תחתמ\r
+:ןכ-יהיו\r
+רקב-יהיו ברע-יהיו םימש עיקרל םיהלא ארקיו      ח  8\r
+:ינש םוי\r
+םוקמ-לא םימשה תחתמ םימה ווקי םיהלא רמאיו      ט  9\r
+:ןכ-יהיו השביה הארתו דחא\r
+ארק םימה הוקמלו ץרא השביל םיהלא ארקיו   י  10\r
+:בוט-יכ םיהלא אריו םימי\r
+ערז עירזמ בשע אשד ץראה אשדת םיהלא רמאיו        אי  11\r
+ץראה-לע וב-וערז רשא ונימל ירפ השע ירפ ץע\r
+:ןכ-יהיו\r
+ץעו והנימל ערז עירזמ בשע אשד ץראה אצותו        בי  12\r
+םיהלא אריו והנימל וב-וערז רשא ירפ-השע\r
+:בוט-יכ\r
+:ישילש םוי רקב-יהיו ברע-יהיו     גי  13\r
+לידבהל םימשה עיקרב תראמ יהי םיהלא רמאיו       די  14\r
+םידעומלו תתאל ויהו הלילה ןיבו םויה ןיב\r
+:םינשו םימילו\r
+ץראה-לע ריאהל םימשה עיקרב תרואמל ויהו   וט  15\r
+:ןכ-יהיו\r
+רואמה-תא םילדגה תראמה ינש-תא םיהלא שעיו        זט  16\r
+תלשממל ןטקה רואמה-תאו םויה תלשממל לדגה\r
+:םיבכוכה תאו הלילה\r
+ריאהל םימשה עיקרב םיהלא םתא ןתיו    זי  17\r
+:ץראה-לע\r
+ןיבו רואה ןיב לידבהלו הלילבו םויב לשמלו       חי  18\r
+:בוט-יכ םיהלא אריו ךשחה\r
+:יעיבר םוי רקב-יהיו ברע-יהיו     טי  19\r
+ףועו היח שפנ ץרש םימה וצרשי םיהלא רמאיו        כ  20\r
+:םימשה עיקר ינפ-לע ץראה-לע ףפועי\r
+שפנ-לכ תאו םילדגה םנינתה-תא םיהלא ארביו        אכ  21\r
+תאו םהנימל םימה וצרש רשא תשמרה היחה\r
+:בוט-יכ םיהלא אריו והנימל ףנכ ףוע-לכ\r
+ואלמו וברו ורפ רמאל םיהלא םתא ךרביו       בכ  22\r
+:ץראב ברי ףועהו םימיב םימה-תא\r
+:ישימח םוי רקב-יהיו ברע-יהיו     גכ  23\r
+המהב הנימל היח שפנ ץראה אצות םיהלא רמאיו      דכ  24\r
+:ןכ-יהיו הנימל ץרא-ותיחו שמרו\r
+המהבה-תאו הנימל ץראה תיח-תא םיהלא שעיו  הכ  25\r
+םיהלא אריו והנימל המדאה שמר-לכ תאו הנימל\r
+:בוט-יכ\r
+ונתומדכ ונמלצב םדא השענ םיהלא רמאיו      וכ  26\r
+המהבבו םימשה ףועבו םיה תגדב ודריו\r
+:ץראה-לע שמרה שמרה-לכבו ץראה-לכבו\r
+ארב םיהלא םלצב ומלצב םדאה-תא םיהלא ארביו      זכ  27\r
+:םתא ארב הבקנו רכז ותא\r
+וברו ורפ םיהלא םהל רמאיו םיהלא םתא ךרביו      חכ  28\r
+ףועבו םיה תגדב ודרו השבכו ץראה-תא ואלמו\r
+:ץראה-לע תשמרה היח-לכבו םימשה\r
+ערז בשע-לכ-תא םכל יתתנ הנה םיהלא רמאיו   טכ  29\r
+וב-רשא ץעה-לכ-תאו ץראה-לכ ינפ-לע רשא ערז\r
+:הלכאל היהי םכל ערז ערז ץע-ירפ\r
+שמור לכלו םימשה ףוע-לכלו ץראה תיח-לכלו  ל  30\r
+בשע קרי-לכ-תא היח שפנ וב-רשא ץראה-לע\r
+:ןכ-יהיו הלכאל\r
+דאמ בוט-הנהו השע רשא-לכ-תא םיהלא אריו     אל  31\r
+:יששה םוי רקב-יהיו בר\r
diff --git a/deps/flexi-streams/test/hebrew_utf8_lf.txt b/deps/flexi-streams/test/hebrew_utf8_lf.txt
new file mode 100644 (file)
index 0000000..1746d16
--- /dev/null
@@ -0,0 +1,68 @@
+:ץראה תאו םימשה תא םיהלא ארב תישארב        א  1
+םוהת ינפ-לע ךשחו והבו והת התיה ץראהו      ב  2
+:םימה ינפ-לע תפחרמ םיהלא חורו
+:רוא-יהיו רוא יהי םיהלא רמאיו   ג  3
+םיהלא לדביו בוט-יכ רואה-תא םיהלא אריו    ד  4
+:ךשחה ןיבו רואה ןיב
+הליל ארק ךשחלו םוי רואל םיהלא ארקיו       ה  5
+:דחא םוי רקב-יהיו ברע-יהיו
+יהיו םימה ךותב עיקר יהי םיהלא רמאיו       ו  6
+:םימל םימ ןיב לידבמ
+רשא םימה ןיב לדביו עיקרה-תא םיהלא שעיו  ז  7
+עיקרל לעמ רשא םימה ןיבו עיקרל תחתמ
+:ןכ-יהיו
+רקב-יהיו ברע-יהיו םימש עיקרל םיהלא ארקיו      ח  8
+:ינש םוי
+םוקמ-לא םימשה תחתמ םימה ווקי םיהלא רמאיו      ט  9
+:ןכ-יהיו השביה הארתו דחא
+ארק םימה הוקמלו ץרא השביל םיהלא ארקיו   י  10
+:בוט-יכ םיהלא אריו םימי
+ערז עירזמ בשע אשד ץראה אשדת םיהלא רמאיו        אי  11
+ץראה-לע וב-וערז רשא ונימל ירפ השע ירפ ץע
+:ןכ-יהיו
+ץעו והנימל ערז עירזמ בשע אשד ץראה אצותו        בי  12
+םיהלא אריו והנימל וב-וערז רשא ירפ-השע
+:בוט-יכ
+:ישילש םוי רקב-יהיו ברע-יהיו     גי  13
+לידבהל םימשה עיקרב תראמ יהי םיהלא רמאיו       די  14
+םידעומלו תתאל ויהו הלילה ןיבו םויה ןיב
+:םינשו םימילו
+ץראה-לע ריאהל םימשה עיקרב תרואמל ויהו   וט  15
+:ןכ-יהיו
+רואמה-תא םילדגה תראמה ינש-תא םיהלא שעיו        זט  16
+תלשממל ןטקה רואמה-תאו םויה תלשממל לדגה
+:םיבכוכה תאו הלילה
+ריאהל םימשה עיקרב םיהלא םתא ןתיו    זי  17
+:ץראה-לע
+ןיבו רואה ןיב לידבהלו הלילבו םויב לשמלו       חי  18
+:בוט-יכ םיהלא אריו ךשחה
+:יעיבר םוי רקב-יהיו ברע-יהיו     טי  19
+ףועו היח שפנ ץרש םימה וצרשי םיהלא רמאיו        כ  20
+:םימשה עיקר ינפ-לע ץראה-לע ףפועי
+שפנ-לכ תאו םילדגה םנינתה-תא םיהלא ארביו        אכ  21
+תאו םהנימל םימה וצרש רשא תשמרה היחה
+:בוט-יכ םיהלא אריו והנימל ףנכ ףוע-לכ
+ואלמו וברו ורפ רמאל םיהלא םתא ךרביו       בכ  22
+:ץראב ברי ףועהו םימיב םימה-תא
+:ישימח םוי רקב-יהיו ברע-יהיו     גכ  23
+המהב הנימל היח שפנ ץראה אצות םיהלא רמאיו      דכ  24
+:ןכ-יהיו הנימל ץרא-ותיחו שמרו
+המהבה-תאו הנימל ץראה תיח-תא םיהלא שעיו  הכ  25
+םיהלא אריו והנימל המדאה שמר-לכ תאו הנימל
+:בוט-יכ
+ונתומדכ ונמלצב םדא השענ םיהלא רמאיו      וכ  26
+המהבבו םימשה ףועבו םיה תגדב ודריו
+:ץראה-לע שמרה שמרה-לכבו ץראה-לכבו
+ארב םיהלא םלצב ומלצב םדאה-תא םיהלא ארביו      זכ  27
+:םתא ארב הבקנו רכז ותא
+וברו ורפ םיהלא םהל רמאיו םיהלא םתא ךרביו      חכ  28
+ףועבו םיה תגדב ודרו השבכו ץראה-תא ואלמו
+:ץראה-לע תשמרה היח-לכבו םימשה
+ערז בשע-לכ-תא םכל יתתנ הנה םיהלא רמאיו   טכ  29
+וב-רשא ץעה-לכ-תאו ץראה-לכ ינפ-לע רשא ערז
+:הלכאל היהי םכל ערז ערז ץע-ירפ
+שמור לכלו םימשה ףוע-לכלו ץראה תיח-לכלו  ל  30
+בשע קרי-לכ-תא היח שפנ וב-רשא ץראה-לע
+:ןכ-יהיו הלכאל
+דאמ בוט-הנהו השע רשא-לכ-תא םיהלא אריו     אל  31
+:יששה םוי רקב-יהיו בר
diff --git a/deps/flexi-streams/test/kafka_cp1252_cr.txt b/deps/flexi-streams/test/kafka_cp1252_cr.txt
new file mode 100644 (file)
index 0000000..41f1bc4
--- /dev/null
@@ -0,0 +1 @@
+Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Er lag auf seinem panzerartig harten Rücken und sah, wenn er den Kopf ein wenig hob, seinen gewölbten, braunen, von bogenförmigen Versteifungen geteilten Bauch, auf dessen Höhe sich die Bettdecke, zum gänzlichen Niedergleiten bereit, kaum noch erhalten konnte. Seine vielen, im Vergleich zu seinem sonstigen Umfang kläglich dünnen Beine flimmerten ihm hilflos vor den Augen.\r\r»Was ist mit mir geschehen?«, dachte er. Es war kein Traum. Sein Zimmer, ein richtiges, nur etwas zu kleines Menschenzimmer, lag ruhig zwischen den vier wohlbekannten Wänden. Über dem Tisch, auf dem eine auseinandergepackte Musterkollektion von Tuchwaren ausgebreitet war - Samsa war Reisender - hing das Bild, das er vor kurzem aus einer illustrierten Zeitschrift ausgeschnitten und in einem hübschen, vergoldeten Rahmen untergebracht hatte. Es stellte eine Dame dar, die mit einem Pelzhut und einer Pelzboa versehen, aufrecht dasaß und einen schweren Pelzmuff, in dem ihr ganzer Unterarm verschwunden war, dem Beschauer entgegenhob.\r\rGregors Blick richtete sich dann zum Fenster, und das trübe Wetter - man hörte Regentropfen auf das Fensterblech aufschlagen - machte ihn ganz melancholisch. »Wie wäre es, wenn ich noch ein wenig weiterschliefe und alle Narrheiten vergäße«, dachte er, aber das war gänzlich undurchführbar, denn er war gewöhnt, auf der rechten Seite zu schlafen, konnte sich aber in seinem gegenwärtigen Zustand nicht in diese Lage bringen. Mit welcher Kraft er sich auch auf die rechte Seite warf, immer wieder schaukelte er in die Rückenlage zurück. Er versuchte es wohl hundertmal, schloß die Augen, um die zappelnden Beine nicht sehen zu müssen, und ließ erst ab, als er in der Seite einen noch nie gefühlten, leichten, dumpfen Schmerz zu fühlen begann.\r\r»Ach Gott«, dachte er, »was für einen anstrengenden Beruf habe ich gewählt! Tag aus, Tag ein auf der Reise. Die geschäftlichen Aufregungen sind viel größer, als im eigentlichen Geschäft zu Hause, und außerdem ist mir noch diese Plage des Reisens auferlegt, die Sorgen um die Zuganschlüsse, das unregelmäßige, schlechte Essen, ein immer wechselnder, nie andauernder, nie herzlich werdender menschlicher Verkehr. Der Teufel soll das alles holen!« Er fühlte ein leichtes Jucken oben auf dem Bauch; schob sich auf dem Rücken langsam näher zum Bettpfosten, um den Kopf besser heben zu können; fand die juckende Stelle, die mit lauter kleinen weißen Pünktchen besetzt war, die er nicht zu beurteilen verstand; und wollte mit einem Bein die Stelle betasten, zog es aber gleich zurück, denn bei der Berührung umwehten ihn Kälteschauer.\r\rEr glitt wieder in seine frühere Lage zurück. »Dies frühzeitige Aufstehen«, dachte er, »macht einen ganz blödsinnig. Der Mensch muß seinen Schlaf haben. Andere Reisende leben wie Haremsfrauen. Wenn ich zum Beispiel im Laufe des Vormittags ins Gasthaus zurückgehe, um die erlangten Aufträge zu überschreiben, sitzen diese Herren erst beim Frühstück. Das sollte ich bei meinem Chef versuchen; ich würde auf der Stelle hinausfliegen. Wer weiß übrigens, ob das nicht sehr gut für mich wäre. Wenn ich mich nicht wegen meiner Eltern zurückhielte, ich hätte längst gekündigt, ich wäre vor den Chef hin getreten und hätte ihm meine Meinung von Grund des Herzens aus gesagt. Vom Pult hätte er fallen müssen! Es ist auch eine sonderbare Art, sich auf das Pult zu setzen und von der Höhe herab mit dem Angestellten zu reden, der überdies wegen der Schwerhörigkeit des Chefs ganz nahe herantreten muß. Nun, die Hoffnung ist noch nicht gänzlich aufgegeben; habe ich einmal das Geld beisammen, um die Schuld der Eltern an ihn abzuzahlen - es dürfte noch fünf bis sechs Jahre dauern - , mache ich die Sache unbedingt. Dann wird der große Schnitt gemacht. Vorläufig allerdings muß ich aufstehen, denn mein Zug fährt um fünf.«\r\rUnd er sah zur Weckuhr hinüber, die auf dem Kasten tickte. »Himmlischer Vater!«, dachte er. Es war halb sieben Uhr, und die Zeiger gingen ruhig vorwärts, es war sogar halb vorüber, es näherte sich schon dreiviertel. Sollte der Wecker nicht geläutet haben? Man sah vom Bett aus, daß er auf vier Uhr richtig eingestellt war; gewiß hatte er auch geläutet. Ja, aber war es möglich, dieses möbelerschütternde Läuten ruhig zu verschlafen? Nun, ruhig hatte er ja nicht geschlafen, aber wahrscheinlich desto fester. Was aber sollte er jetzt tun? Der nächste Zug ging um sieben Uhr; um den einzuholen, hätte er sich unsinnig beeilen müssen, und die Kollektion war noch nicht eingepackt, und er selbst fühlte sich durchaus nicht besonders frisch und beweglich. Und selbst wenn er den Zug einholte, ein Donnerwetter des Chefs war nicht zu vermeiden, denn der Geschäftsdiener hatte beim Fünfuhrzug gewartet und die Meldung von seiner Versäumnis längst erstattet. Es war eine Kreatur des Chefs, ohne Rückgrat und Verstand. Wie nun, wenn er sich krank meldete? Das wäre aber äußerst peinlich und verdächtig, denn Gregor war während seines fünfjährigen Dienstes noch nicht einmal krank gewesen. Gewiß würde der Chef mit dem Krankenkassenarzt kommen, würde den Eltern wegen des faulen Sohnes Vorwürfe machen und alle Einwände durch den Hinweis auf den Krankenkassenarzt abschneiden, für den es ja überhaupt nur ganz gesunde, aber arbeitsscheue Menschen gibt. Und hätte er übrigens in diesem Falle so ganz unrecht? Gregor fühlte sich tatsächlich, abgesehen von einer nach dem langen Schlaf wirklich überflüssigen Schläfrigkeit, ganz wohl und hatte sogar einen besonders kräftigen Hunger. \r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/kafka_cp1252_crlf.txt b/deps/flexi-streams/test/kafka_cp1252_crlf.txt
new file mode 100644 (file)
index 0000000..4fefd15
--- /dev/null
@@ -0,0 +1,11 @@
+Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Er lag auf seinem panzerartig harten Rücken und sah, wenn er den Kopf ein wenig hob, seinen gewölbten, braunen, von bogenförmigen Versteifungen geteilten Bauch, auf dessen Höhe sich die Bettdecke, zum gänzlichen Niedergleiten bereit, kaum noch erhalten konnte. Seine vielen, im Vergleich zu seinem sonstigen Umfang kläglich dünnen Beine flimmerten ihm hilflos vor den Augen.\r
+\r
+»Was ist mit mir geschehen?«, dachte er. Es war kein Traum. Sein Zimmer, ein richtiges, nur etwas zu kleines Menschenzimmer, lag ruhig zwischen den vier wohlbekannten Wänden. Über dem Tisch, auf dem eine auseinandergepackte Musterkollektion von Tuchwaren ausgebreitet war - Samsa war Reisender - hing das Bild, das er vor kurzem aus einer illustrierten Zeitschrift ausgeschnitten und in einem hübschen, vergoldeten Rahmen untergebracht hatte. Es stellte eine Dame dar, die mit einem Pelzhut und einer Pelzboa versehen, aufrecht dasaß und einen schweren Pelzmuff, in dem ihr ganzer Unterarm verschwunden war, dem Beschauer entgegenhob.\r
+\r
+Gregors Blick richtete sich dann zum Fenster, und das trübe Wetter - man hörte Regentropfen auf das Fensterblech aufschlagen - machte ihn ganz melancholisch. »Wie wäre es, wenn ich noch ein wenig weiterschliefe und alle Narrheiten vergäße«, dachte er, aber das war gänzlich undurchführbar, denn er war gewöhnt, auf der rechten Seite zu schlafen, konnte sich aber in seinem gegenwärtigen Zustand nicht in diese Lage bringen. Mit welcher Kraft er sich auch auf die rechte Seite warf, immer wieder schaukelte er in die Rückenlage zurück. Er versuchte es wohl hundertmal, schloß die Augen, um die zappelnden Beine nicht sehen zu müssen, und ließ erst ab, als er in der Seite einen noch nie gefühlten, leichten, dumpfen Schmerz zu fühlen begann.\r
+\r
+»Ach Gott«, dachte er, »was für einen anstrengenden Beruf habe ich gewählt! Tag aus, Tag ein auf der Reise. Die geschäftlichen Aufregungen sind viel größer, als im eigentlichen Geschäft zu Hause, und außerdem ist mir noch diese Plage des Reisens auferlegt, die Sorgen um die Zuganschlüsse, das unregelmäßige, schlechte Essen, ein immer wechselnder, nie andauernder, nie herzlich werdender menschlicher Verkehr. Der Teufel soll das alles holen!« Er fühlte ein leichtes Jucken oben auf dem Bauch; schob sich auf dem Rücken langsam näher zum Bettpfosten, um den Kopf besser heben zu können; fand die juckende Stelle, die mit lauter kleinen weißen Pünktchen besetzt war, die er nicht zu beurteilen verstand; und wollte mit einem Bein die Stelle betasten, zog es aber gleich zurück, denn bei der Berührung umwehten ihn Kälteschauer.\r
+\r
+Er glitt wieder in seine frühere Lage zurück. »Dies frühzeitige Aufstehen«, dachte er, »macht einen ganz blödsinnig. Der Mensch muß seinen Schlaf haben. Andere Reisende leben wie Haremsfrauen. Wenn ich zum Beispiel im Laufe des Vormittags ins Gasthaus zurückgehe, um die erlangten Aufträge zu überschreiben, sitzen diese Herren erst beim Frühstück. Das sollte ich bei meinem Chef versuchen; ich würde auf der Stelle hinausfliegen. Wer weiß übrigens, ob das nicht sehr gut für mich wäre. Wenn ich mich nicht wegen meiner Eltern zurückhielte, ich hätte längst gekündigt, ich wäre vor den Chef hin getreten und hätte ihm meine Meinung von Grund des Herzens aus gesagt. Vom Pult hätte er fallen müssen! Es ist auch eine sonderbare Art, sich auf das Pult zu setzen und von der Höhe herab mit dem Angestellten zu reden, der überdies wegen der Schwerhörigkeit des Chefs ganz nahe herantreten muß. Nun, die Hoffnung ist noch nicht gänzlich aufgegeben; habe ich einmal das Geld beisammen, um die Schuld der Eltern an ihn abzuzahlen - es dürfte noch fünf bis sechs Jahre dauern - , mache ich die Sache unbedingt. Dann wird der große Schnitt gemacht. Vorläufig allerdings muß ich aufstehen, denn mein Zug fährt um fünf.«\r
+\r
+Und er sah zur Weckuhr hinüber, die auf dem Kasten tickte. »Himmlischer Vater!«, dachte er. Es war halb sieben Uhr, und die Zeiger gingen ruhig vorwärts, es war sogar halb vorüber, es näherte sich schon dreiviertel. Sollte der Wecker nicht geläutet haben? Man sah vom Bett aus, daß er auf vier Uhr richtig eingestellt war; gewiß hatte er auch geläutet. Ja, aber war es möglich, dieses möbelerschütternde Läuten ruhig zu verschlafen? Nun, ruhig hatte er ja nicht geschlafen, aber wahrscheinlich desto fester. Was aber sollte er jetzt tun? Der nächste Zug ging um sieben Uhr; um den einzuholen, hätte er sich unsinnig beeilen müssen, und die Kollektion war noch nicht eingepackt, und er selbst fühlte sich durchaus nicht besonders frisch und beweglich. Und selbst wenn er den Zug einholte, ein Donnerwetter des Chefs war nicht zu vermeiden, denn der Geschäftsdiener hatte beim Fünfuhrzug gewartet und die Meldung von seiner Versäumnis längst erstattet. Es war eine Kreatur des Chefs, ohne Rückgrat und Verstand. Wie nun, wenn er sich krank meldete? Das wäre aber äußerst peinlich und verdächtig, denn Gregor war während seines fünfjährigen Dienstes noch nicht einmal krank gewesen. Gewiß würde der Chef mit dem Krankenkassenarzt kommen, würde den Eltern wegen des faulen Sohnes Vorwürfe machen und alle Einwände durch den Hinweis auf den Krankenkassenarzt abschneiden, für den es ja überhaupt nur ganz gesunde, aber arbeitsscheue Menschen gibt. Und hätte er übrigens in diesem Falle so ganz unrecht? Gregor fühlte sich tatsächlich, abgesehen von einer nach dem langen Schlaf wirklich überflüssigen Schläfrigkeit, ganz wohl und hatte sogar einen besonders kräftigen Hunger. \r
diff --git a/deps/flexi-streams/test/kafka_cp1252_lf.txt b/deps/flexi-streams/test/kafka_cp1252_lf.txt
new file mode 100644 (file)
index 0000000..82dbb83
--- /dev/null
@@ -0,0 +1,11 @@
+Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Er lag auf seinem panzerartig harten Rücken und sah, wenn er den Kopf ein wenig hob, seinen gewölbten, braunen, von bogenförmigen Versteifungen geteilten Bauch, auf dessen Höhe sich die Bettdecke, zum gänzlichen Niedergleiten bereit, kaum noch erhalten konnte. Seine vielen, im Vergleich zu seinem sonstigen Umfang kläglich dünnen Beine flimmerten ihm hilflos vor den Augen.
+
+»Was ist mit mir geschehen?«, dachte er. Es war kein Traum. Sein Zimmer, ein richtiges, nur etwas zu kleines Menschenzimmer, lag ruhig zwischen den vier wohlbekannten Wänden. Über dem Tisch, auf dem eine auseinandergepackte Musterkollektion von Tuchwaren ausgebreitet war - Samsa war Reisender - hing das Bild, das er vor kurzem aus einer illustrierten Zeitschrift ausgeschnitten und in einem hübschen, vergoldeten Rahmen untergebracht hatte. Es stellte eine Dame dar, die mit einem Pelzhut und einer Pelzboa versehen, aufrecht dasaß und einen schweren Pelzmuff, in dem ihr ganzer Unterarm verschwunden war, dem Beschauer entgegenhob.
+
+Gregors Blick richtete sich dann zum Fenster, und das trübe Wetter - man hörte Regentropfen auf das Fensterblech aufschlagen - machte ihn ganz melancholisch. »Wie wäre es, wenn ich noch ein wenig weiterschliefe und alle Narrheiten vergäße«, dachte er, aber das war gänzlich undurchführbar, denn er war gewöhnt, auf der rechten Seite zu schlafen, konnte sich aber in seinem gegenwärtigen Zustand nicht in diese Lage bringen. Mit welcher Kraft er sich auch auf die rechte Seite warf, immer wieder schaukelte er in die Rückenlage zurück. Er versuchte es wohl hundertmal, schloß die Augen, um die zappelnden Beine nicht sehen zu müssen, und ließ erst ab, als er in der Seite einen noch nie gefühlten, leichten, dumpfen Schmerz zu fühlen begann.
+
+»Ach Gott«, dachte er, »was für einen anstrengenden Beruf habe ich gewählt! Tag aus, Tag ein auf der Reise. Die geschäftlichen Aufregungen sind viel größer, als im eigentlichen Geschäft zu Hause, und außerdem ist mir noch diese Plage des Reisens auferlegt, die Sorgen um die Zuganschlüsse, das unregelmäßige, schlechte Essen, ein immer wechselnder, nie andauernder, nie herzlich werdender menschlicher Verkehr. Der Teufel soll das alles holen!« Er fühlte ein leichtes Jucken oben auf dem Bauch; schob sich auf dem Rücken langsam näher zum Bettpfosten, um den Kopf besser heben zu können; fand die juckende Stelle, die mit lauter kleinen weißen Pünktchen besetzt war, die er nicht zu beurteilen verstand; und wollte mit einem Bein die Stelle betasten, zog es aber gleich zurück, denn bei der Berührung umwehten ihn Kälteschauer.
+
+Er glitt wieder in seine frühere Lage zurück. »Dies frühzeitige Aufstehen«, dachte er, »macht einen ganz blödsinnig. Der Mensch muß seinen Schlaf haben. Andere Reisende leben wie Haremsfrauen. Wenn ich zum Beispiel im Laufe des Vormittags ins Gasthaus zurückgehe, um die erlangten Aufträge zu überschreiben, sitzen diese Herren erst beim Frühstück. Das sollte ich bei meinem Chef versuchen; ich würde auf der Stelle hinausfliegen. Wer weiß übrigens, ob das nicht sehr gut für mich wäre. Wenn ich mich nicht wegen meiner Eltern zurückhielte, ich hätte längst gekündigt, ich wäre vor den Chef hin getreten und hätte ihm meine Meinung von Grund des Herzens aus gesagt. Vom Pult hätte er fallen müssen! Es ist auch eine sonderbare Art, sich auf das Pult zu setzen und von der Höhe herab mit dem Angestellten zu reden, der überdies wegen der Schwerhörigkeit des Chefs ganz nahe herantreten muß. Nun, die Hoffnung ist noch nicht gänzlich aufgegeben; habe ich einmal das Geld beisammen, um die Schuld der Eltern an ihn abzuzahlen - es dürfte noch fünf bis sechs Jahre dauern - , mache ich die Sache unbedingt. Dann wird der große Schnitt gemacht. Vorläufig allerdings muß ich aufstehen, denn mein Zug fährt um fünf.«
+
+Und er sah zur Weckuhr hinüber, die auf dem Kasten tickte. »Himmlischer Vater!«, dachte er. Es war halb sieben Uhr, und die Zeiger gingen ruhig vorwärts, es war sogar halb vorüber, es näherte sich schon dreiviertel. Sollte der Wecker nicht geläutet haben? Man sah vom Bett aus, daß er auf vier Uhr richtig eingestellt war; gewiß hatte er auch geläutet. Ja, aber war es möglich, dieses möbelerschütternde Läuten ruhig zu verschlafen? Nun, ruhig hatte er ja nicht geschlafen, aber wahrscheinlich desto fester. Was aber sollte er jetzt tun? Der nächste Zug ging um sieben Uhr; um den einzuholen, hätte er sich unsinnig beeilen müssen, und die Kollektion war noch nicht eingepackt, und er selbst fühlte sich durchaus nicht besonders frisch und beweglich. Und selbst wenn er den Zug einholte, ein Donnerwetter des Chefs war nicht zu vermeiden, denn der Geschäftsdiener hatte beim Fünfuhrzug gewartet und die Meldung von seiner Versäumnis längst erstattet. Es war eine Kreatur des Chefs, ohne Rückgrat und Verstand. Wie nun, wenn er sich krank meldete? Das wäre aber äußerst peinlich und verdächtig, denn Gregor war während seines fünfjährigen Dienstes noch nicht einmal krank gewesen. Gewiß würde der Chef mit dem Krankenkassenarzt kommen, würde den Eltern wegen des faulen Sohnes Vorwürfe machen und alle Einwände durch den Hinweis auf den Krankenkassenarzt abschneiden, für den es ja überhaupt nur ganz gesunde, aber arbeitsscheue Menschen gibt. Und hätte er übrigens in diesem Falle so ganz unrecht? Gregor fühlte sich tatsächlich, abgesehen von einer nach dem langen Schlaf wirklich überflüssigen Schläfrigkeit, ganz wohl und hatte sogar einen besonders kräftigen Hunger. 
diff --git a/deps/flexi-streams/test/kafka_latin1_cr.txt b/deps/flexi-streams/test/kafka_latin1_cr.txt
new file mode 100644 (file)
index 0000000..41f1bc4
--- /dev/null
@@ -0,0 +1 @@
+Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Er lag auf seinem panzerartig harten Rücken und sah, wenn er den Kopf ein wenig hob, seinen gewölbten, braunen, von bogenförmigen Versteifungen geteilten Bauch, auf dessen Höhe sich die Bettdecke, zum gänzlichen Niedergleiten bereit, kaum noch erhalten konnte. Seine vielen, im Vergleich zu seinem sonstigen Umfang kläglich dünnen Beine flimmerten ihm hilflos vor den Augen.\r\r»Was ist mit mir geschehen?«, dachte er. Es war kein Traum. Sein Zimmer, ein richtiges, nur etwas zu kleines Menschenzimmer, lag ruhig zwischen den vier wohlbekannten Wänden. Über dem Tisch, auf dem eine auseinandergepackte Musterkollektion von Tuchwaren ausgebreitet war - Samsa war Reisender - hing das Bild, das er vor kurzem aus einer illustrierten Zeitschrift ausgeschnitten und in einem hübschen, vergoldeten Rahmen untergebracht hatte. Es stellte eine Dame dar, die mit einem Pelzhut und einer Pelzboa versehen, aufrecht dasaß und einen schweren Pelzmuff, in dem ihr ganzer Unterarm verschwunden war, dem Beschauer entgegenhob.\r\rGregors Blick richtete sich dann zum Fenster, und das trübe Wetter - man hörte Regentropfen auf das Fensterblech aufschlagen - machte ihn ganz melancholisch. »Wie wäre es, wenn ich noch ein wenig weiterschliefe und alle Narrheiten vergäße«, dachte er, aber das war gänzlich undurchführbar, denn er war gewöhnt, auf der rechten Seite zu schlafen, konnte sich aber in seinem gegenwärtigen Zustand nicht in diese Lage bringen. Mit welcher Kraft er sich auch auf die rechte Seite warf, immer wieder schaukelte er in die Rückenlage zurück. Er versuchte es wohl hundertmal, schloß die Augen, um die zappelnden Beine nicht sehen zu müssen, und ließ erst ab, als er in der Seite einen noch nie gefühlten, leichten, dumpfen Schmerz zu fühlen begann.\r\r»Ach Gott«, dachte er, »was für einen anstrengenden Beruf habe ich gewählt! Tag aus, Tag ein auf der Reise. Die geschäftlichen Aufregungen sind viel größer, als im eigentlichen Geschäft zu Hause, und außerdem ist mir noch diese Plage des Reisens auferlegt, die Sorgen um die Zuganschlüsse, das unregelmäßige, schlechte Essen, ein immer wechselnder, nie andauernder, nie herzlich werdender menschlicher Verkehr. Der Teufel soll das alles holen!« Er fühlte ein leichtes Jucken oben auf dem Bauch; schob sich auf dem Rücken langsam näher zum Bettpfosten, um den Kopf besser heben zu können; fand die juckende Stelle, die mit lauter kleinen weißen Pünktchen besetzt war, die er nicht zu beurteilen verstand; und wollte mit einem Bein die Stelle betasten, zog es aber gleich zurück, denn bei der Berührung umwehten ihn Kälteschauer.\r\rEr glitt wieder in seine frühere Lage zurück. »Dies frühzeitige Aufstehen«, dachte er, »macht einen ganz blödsinnig. Der Mensch muß seinen Schlaf haben. Andere Reisende leben wie Haremsfrauen. Wenn ich zum Beispiel im Laufe des Vormittags ins Gasthaus zurückgehe, um die erlangten Aufträge zu überschreiben, sitzen diese Herren erst beim Frühstück. Das sollte ich bei meinem Chef versuchen; ich würde auf der Stelle hinausfliegen. Wer weiß übrigens, ob das nicht sehr gut für mich wäre. Wenn ich mich nicht wegen meiner Eltern zurückhielte, ich hätte längst gekündigt, ich wäre vor den Chef hin getreten und hätte ihm meine Meinung von Grund des Herzens aus gesagt. Vom Pult hätte er fallen müssen! Es ist auch eine sonderbare Art, sich auf das Pult zu setzen und von der Höhe herab mit dem Angestellten zu reden, der überdies wegen der Schwerhörigkeit des Chefs ganz nahe herantreten muß. Nun, die Hoffnung ist noch nicht gänzlich aufgegeben; habe ich einmal das Geld beisammen, um die Schuld der Eltern an ihn abzuzahlen - es dürfte noch fünf bis sechs Jahre dauern - , mache ich die Sache unbedingt. Dann wird der große Schnitt gemacht. Vorläufig allerdings muß ich aufstehen, denn mein Zug fährt um fünf.«\r\rUnd er sah zur Weckuhr hinüber, die auf dem Kasten tickte. »Himmlischer Vater!«, dachte er. Es war halb sieben Uhr, und die Zeiger gingen ruhig vorwärts, es war sogar halb vorüber, es näherte sich schon dreiviertel. Sollte der Wecker nicht geläutet haben? Man sah vom Bett aus, daß er auf vier Uhr richtig eingestellt war; gewiß hatte er auch geläutet. Ja, aber war es möglich, dieses möbelerschütternde Läuten ruhig zu verschlafen? Nun, ruhig hatte er ja nicht geschlafen, aber wahrscheinlich desto fester. Was aber sollte er jetzt tun? Der nächste Zug ging um sieben Uhr; um den einzuholen, hätte er sich unsinnig beeilen müssen, und die Kollektion war noch nicht eingepackt, und er selbst fühlte sich durchaus nicht besonders frisch und beweglich. Und selbst wenn er den Zug einholte, ein Donnerwetter des Chefs war nicht zu vermeiden, denn der Geschäftsdiener hatte beim Fünfuhrzug gewartet und die Meldung von seiner Versäumnis längst erstattet. Es war eine Kreatur des Chefs, ohne Rückgrat und Verstand. Wie nun, wenn er sich krank meldete? Das wäre aber äußerst peinlich und verdächtig, denn Gregor war während seines fünfjährigen Dienstes noch nicht einmal krank gewesen. Gewiß würde der Chef mit dem Krankenkassenarzt kommen, würde den Eltern wegen des faulen Sohnes Vorwürfe machen und alle Einwände durch den Hinweis auf den Krankenkassenarzt abschneiden, für den es ja überhaupt nur ganz gesunde, aber arbeitsscheue Menschen gibt. Und hätte er übrigens in diesem Falle so ganz unrecht? Gregor fühlte sich tatsächlich, abgesehen von einer nach dem langen Schlaf wirklich überflüssigen Schläfrigkeit, ganz wohl und hatte sogar einen besonders kräftigen Hunger. \r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/kafka_latin1_crlf.txt b/deps/flexi-streams/test/kafka_latin1_crlf.txt
new file mode 100644 (file)
index 0000000..4fefd15
--- /dev/null
@@ -0,0 +1,11 @@
+Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Er lag auf seinem panzerartig harten Rücken und sah, wenn er den Kopf ein wenig hob, seinen gewölbten, braunen, von bogenförmigen Versteifungen geteilten Bauch, auf dessen Höhe sich die Bettdecke, zum gänzlichen Niedergleiten bereit, kaum noch erhalten konnte. Seine vielen, im Vergleich zu seinem sonstigen Umfang kläglich dünnen Beine flimmerten ihm hilflos vor den Augen.\r
+\r
+»Was ist mit mir geschehen?«, dachte er. Es war kein Traum. Sein Zimmer, ein richtiges, nur etwas zu kleines Menschenzimmer, lag ruhig zwischen den vier wohlbekannten Wänden. Über dem Tisch, auf dem eine auseinandergepackte Musterkollektion von Tuchwaren ausgebreitet war - Samsa war Reisender - hing das Bild, das er vor kurzem aus einer illustrierten Zeitschrift ausgeschnitten und in einem hübschen, vergoldeten Rahmen untergebracht hatte. Es stellte eine Dame dar, die mit einem Pelzhut und einer Pelzboa versehen, aufrecht dasaß und einen schweren Pelzmuff, in dem ihr ganzer Unterarm verschwunden war, dem Beschauer entgegenhob.\r
+\r
+Gregors Blick richtete sich dann zum Fenster, und das trübe Wetter - man hörte Regentropfen auf das Fensterblech aufschlagen - machte ihn ganz melancholisch. »Wie wäre es, wenn ich noch ein wenig weiterschliefe und alle Narrheiten vergäße«, dachte er, aber das war gänzlich undurchführbar, denn er war gewöhnt, auf der rechten Seite zu schlafen, konnte sich aber in seinem gegenwärtigen Zustand nicht in diese Lage bringen. Mit welcher Kraft er sich auch auf die rechte Seite warf, immer wieder schaukelte er in die Rückenlage zurück. Er versuchte es wohl hundertmal, schloß die Augen, um die zappelnden Beine nicht sehen zu müssen, und ließ erst ab, als er in der Seite einen noch nie gefühlten, leichten, dumpfen Schmerz zu fühlen begann.\r
+\r
+»Ach Gott«, dachte er, »was für einen anstrengenden Beruf habe ich gewählt! Tag aus, Tag ein auf der Reise. Die geschäftlichen Aufregungen sind viel größer, als im eigentlichen Geschäft zu Hause, und außerdem ist mir noch diese Plage des Reisens auferlegt, die Sorgen um die Zuganschlüsse, das unregelmäßige, schlechte Essen, ein immer wechselnder, nie andauernder, nie herzlich werdender menschlicher Verkehr. Der Teufel soll das alles holen!« Er fühlte ein leichtes Jucken oben auf dem Bauch; schob sich auf dem Rücken langsam näher zum Bettpfosten, um den Kopf besser heben zu können; fand die juckende Stelle, die mit lauter kleinen weißen Pünktchen besetzt war, die er nicht zu beurteilen verstand; und wollte mit einem Bein die Stelle betasten, zog es aber gleich zurück, denn bei der Berührung umwehten ihn Kälteschauer.\r
+\r
+Er glitt wieder in seine frühere Lage zurück. »Dies frühzeitige Aufstehen«, dachte er, »macht einen ganz blödsinnig. Der Mensch muß seinen Schlaf haben. Andere Reisende leben wie Haremsfrauen. Wenn ich zum Beispiel im Laufe des Vormittags ins Gasthaus zurückgehe, um die erlangten Aufträge zu überschreiben, sitzen diese Herren erst beim Frühstück. Das sollte ich bei meinem Chef versuchen; ich würde auf der Stelle hinausfliegen. Wer weiß übrigens, ob das nicht sehr gut für mich wäre. Wenn ich mich nicht wegen meiner Eltern zurückhielte, ich hätte längst gekündigt, ich wäre vor den Chef hin getreten und hätte ihm meine Meinung von Grund des Herzens aus gesagt. Vom Pult hätte er fallen müssen! Es ist auch eine sonderbare Art, sich auf das Pult zu setzen und von der Höhe herab mit dem Angestellten zu reden, der überdies wegen der Schwerhörigkeit des Chefs ganz nahe herantreten muß. Nun, die Hoffnung ist noch nicht gänzlich aufgegeben; habe ich einmal das Geld beisammen, um die Schuld der Eltern an ihn abzuzahlen - es dürfte noch fünf bis sechs Jahre dauern - , mache ich die Sache unbedingt. Dann wird der große Schnitt gemacht. Vorläufig allerdings muß ich aufstehen, denn mein Zug fährt um fünf.«\r
+\r
+Und er sah zur Weckuhr hinüber, die auf dem Kasten tickte. »Himmlischer Vater!«, dachte er. Es war halb sieben Uhr, und die Zeiger gingen ruhig vorwärts, es war sogar halb vorüber, es näherte sich schon dreiviertel. Sollte der Wecker nicht geläutet haben? Man sah vom Bett aus, daß er auf vier Uhr richtig eingestellt war; gewiß hatte er auch geläutet. Ja, aber war es möglich, dieses möbelerschütternde Läuten ruhig zu verschlafen? Nun, ruhig hatte er ja nicht geschlafen, aber wahrscheinlich desto fester. Was aber sollte er jetzt tun? Der nächste Zug ging um sieben Uhr; um den einzuholen, hätte er sich unsinnig beeilen müssen, und die Kollektion war noch nicht eingepackt, und er selbst fühlte sich durchaus nicht besonders frisch und beweglich. Und selbst wenn er den Zug einholte, ein Donnerwetter des Chefs war nicht zu vermeiden, denn der Geschäftsdiener hatte beim Fünfuhrzug gewartet und die Meldung von seiner Versäumnis längst erstattet. Es war eine Kreatur des Chefs, ohne Rückgrat und Verstand. Wie nun, wenn er sich krank meldete? Das wäre aber äußerst peinlich und verdächtig, denn Gregor war während seines fünfjährigen Dienstes noch nicht einmal krank gewesen. Gewiß würde der Chef mit dem Krankenkassenarzt kommen, würde den Eltern wegen des faulen Sohnes Vorwürfe machen und alle Einwände durch den Hinweis auf den Krankenkassenarzt abschneiden, für den es ja überhaupt nur ganz gesunde, aber arbeitsscheue Menschen gibt. Und hätte er übrigens in diesem Falle so ganz unrecht? Gregor fühlte sich tatsächlich, abgesehen von einer nach dem langen Schlaf wirklich überflüssigen Schläfrigkeit, ganz wohl und hatte sogar einen besonders kräftigen Hunger. \r
diff --git a/deps/flexi-streams/test/kafka_latin1_lf.txt b/deps/flexi-streams/test/kafka_latin1_lf.txt
new file mode 100644 (file)
index 0000000..82dbb83
--- /dev/null
@@ -0,0 +1,11 @@
+Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Er lag auf seinem panzerartig harten Rücken und sah, wenn er den Kopf ein wenig hob, seinen gewölbten, braunen, von bogenförmigen Versteifungen geteilten Bauch, auf dessen Höhe sich die Bettdecke, zum gänzlichen Niedergleiten bereit, kaum noch erhalten konnte. Seine vielen, im Vergleich zu seinem sonstigen Umfang kläglich dünnen Beine flimmerten ihm hilflos vor den Augen.
+
+»Was ist mit mir geschehen?«, dachte er. Es war kein Traum. Sein Zimmer, ein richtiges, nur etwas zu kleines Menschenzimmer, lag ruhig zwischen den vier wohlbekannten Wänden. Über dem Tisch, auf dem eine auseinandergepackte Musterkollektion von Tuchwaren ausgebreitet war - Samsa war Reisender - hing das Bild, das er vor kurzem aus einer illustrierten Zeitschrift ausgeschnitten und in einem hübschen, vergoldeten Rahmen untergebracht hatte. Es stellte eine Dame dar, die mit einem Pelzhut und einer Pelzboa versehen, aufrecht dasaß und einen schweren Pelzmuff, in dem ihr ganzer Unterarm verschwunden war, dem Beschauer entgegenhob.
+
+Gregors Blick richtete sich dann zum Fenster, und das trübe Wetter - man hörte Regentropfen auf das Fensterblech aufschlagen - machte ihn ganz melancholisch. »Wie wäre es, wenn ich noch ein wenig weiterschliefe und alle Narrheiten vergäße«, dachte er, aber das war gänzlich undurchführbar, denn er war gewöhnt, auf der rechten Seite zu schlafen, konnte sich aber in seinem gegenwärtigen Zustand nicht in diese Lage bringen. Mit welcher Kraft er sich auch auf die rechte Seite warf, immer wieder schaukelte er in die Rückenlage zurück. Er versuchte es wohl hundertmal, schloß die Augen, um die zappelnden Beine nicht sehen zu müssen, und ließ erst ab, als er in der Seite einen noch nie gefühlten, leichten, dumpfen Schmerz zu fühlen begann.
+
+»Ach Gott«, dachte er, »was für einen anstrengenden Beruf habe ich gewählt! Tag aus, Tag ein auf der Reise. Die geschäftlichen Aufregungen sind viel größer, als im eigentlichen Geschäft zu Hause, und außerdem ist mir noch diese Plage des Reisens auferlegt, die Sorgen um die Zuganschlüsse, das unregelmäßige, schlechte Essen, ein immer wechselnder, nie andauernder, nie herzlich werdender menschlicher Verkehr. Der Teufel soll das alles holen!« Er fühlte ein leichtes Jucken oben auf dem Bauch; schob sich auf dem Rücken langsam näher zum Bettpfosten, um den Kopf besser heben zu können; fand die juckende Stelle, die mit lauter kleinen weißen Pünktchen besetzt war, die er nicht zu beurteilen verstand; und wollte mit einem Bein die Stelle betasten, zog es aber gleich zurück, denn bei der Berührung umwehten ihn Kälteschauer.
+
+Er glitt wieder in seine frühere Lage zurück. »Dies frühzeitige Aufstehen«, dachte er, »macht einen ganz blödsinnig. Der Mensch muß seinen Schlaf haben. Andere Reisende leben wie Haremsfrauen. Wenn ich zum Beispiel im Laufe des Vormittags ins Gasthaus zurückgehe, um die erlangten Aufträge zu überschreiben, sitzen diese Herren erst beim Frühstück. Das sollte ich bei meinem Chef versuchen; ich würde auf der Stelle hinausfliegen. Wer weiß übrigens, ob das nicht sehr gut für mich wäre. Wenn ich mich nicht wegen meiner Eltern zurückhielte, ich hätte längst gekündigt, ich wäre vor den Chef hin getreten und hätte ihm meine Meinung von Grund des Herzens aus gesagt. Vom Pult hätte er fallen müssen! Es ist auch eine sonderbare Art, sich auf das Pult zu setzen und von der Höhe herab mit dem Angestellten zu reden, der überdies wegen der Schwerhörigkeit des Chefs ganz nahe herantreten muß. Nun, die Hoffnung ist noch nicht gänzlich aufgegeben; habe ich einmal das Geld beisammen, um die Schuld der Eltern an ihn abzuzahlen - es dürfte noch fünf bis sechs Jahre dauern - , mache ich die Sache unbedingt. Dann wird der große Schnitt gemacht. Vorläufig allerdings muß ich aufstehen, denn mein Zug fährt um fünf.«
+
+Und er sah zur Weckuhr hinüber, die auf dem Kasten tickte. »Himmlischer Vater!«, dachte er. Es war halb sieben Uhr, und die Zeiger gingen ruhig vorwärts, es war sogar halb vorüber, es näherte sich schon dreiviertel. Sollte der Wecker nicht geläutet haben? Man sah vom Bett aus, daß er auf vier Uhr richtig eingestellt war; gewiß hatte er auch geläutet. Ja, aber war es möglich, dieses möbelerschütternde Läuten ruhig zu verschlafen? Nun, ruhig hatte er ja nicht geschlafen, aber wahrscheinlich desto fester. Was aber sollte er jetzt tun? Der nächste Zug ging um sieben Uhr; um den einzuholen, hätte er sich unsinnig beeilen müssen, und die Kollektion war noch nicht eingepackt, und er selbst fühlte sich durchaus nicht besonders frisch und beweglich. Und selbst wenn er den Zug einholte, ein Donnerwetter des Chefs war nicht zu vermeiden, denn der Geschäftsdiener hatte beim Fünfuhrzug gewartet und die Meldung von seiner Versäumnis längst erstattet. Es war eine Kreatur des Chefs, ohne Rückgrat und Verstand. Wie nun, wenn er sich krank meldete? Das wäre aber äußerst peinlich und verdächtig, denn Gregor war während seines fünfjährigen Dienstes noch nicht einmal krank gewesen. Gewiß würde der Chef mit dem Krankenkassenarzt kommen, würde den Eltern wegen des faulen Sohnes Vorwürfe machen und alle Einwände durch den Hinweis auf den Krankenkassenarzt abschneiden, für den es ja überhaupt nur ganz gesunde, aber arbeitsscheue Menschen gibt. Und hätte er übrigens in diesem Falle so ganz unrecht? Gregor fühlte sich tatsächlich, abgesehen von einer nach dem langen Schlaf wirklich überflüssigen Schläfrigkeit, ganz wohl und hatte sogar einen besonders kräftigen Hunger. 
diff --git a/deps/flexi-streams/test/kafka_utf8_cr.txt b/deps/flexi-streams/test/kafka_utf8_cr.txt
new file mode 100644 (file)
index 0000000..a2e933c
--- /dev/null
@@ -0,0 +1 @@
+Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Er lag auf seinem panzerartig harten Rücken und sah, wenn er den Kopf ein wenig hob, seinen gewölbten, braunen, von bogenförmigen Versteifungen geteilten Bauch, auf dessen Höhe sich die Bettdecke, zum gänzlichen Niedergleiten bereit, kaum noch erhalten konnte. Seine vielen, im Vergleich zu seinem sonstigen Umfang kläglich dünnen Beine flimmerten ihm hilflos vor den Augen.\r\r»Was ist mit mir geschehen?«, dachte er. Es war kein Traum. Sein Zimmer, ein richtiges, nur etwas zu kleines Menschenzimmer, lag ruhig zwischen den vier wohlbekannten Wänden. Über dem Tisch, auf dem eine auseinandergepackte Musterkollektion von Tuchwaren ausgebreitet war - Samsa war Reisender - hing das Bild, das er vor kurzem aus einer illustrierten Zeitschrift ausgeschnitten und in einem hübschen, vergoldeten Rahmen untergebracht hatte. Es stellte eine Dame dar, die mit einem Pelzhut und einer Pelzboa versehen, aufrecht dasaß und einen schweren Pelzmuff, in dem ihr ganzer Unterarm verschwunden war, dem Beschauer entgegenhob.\r\rGregors Blick richtete sich dann zum Fenster, und das trübe Wetter - man hörte Regentropfen auf das Fensterblech aufschlagen - machte ihn ganz melancholisch. »Wie wäre es, wenn ich noch ein wenig weiterschliefe und alle Narrheiten vergäße«, dachte er, aber das war gänzlich undurchführbar, denn er war gewöhnt, auf der rechten Seite zu schlafen, konnte sich aber in seinem gegenwärtigen Zustand nicht in diese Lage bringen. Mit welcher Kraft er sich auch auf die rechte Seite warf, immer wieder schaukelte er in die Rückenlage zurück. Er versuchte es wohl hundertmal, schloß die Augen, um die zappelnden Beine nicht sehen zu müssen, und ließ erst ab, als er in der Seite einen noch nie gefühlten, leichten, dumpfen Schmerz zu fühlen begann.\r\r»Ach Gott«, dachte er, »was für einen anstrengenden Beruf habe ich gewählt! Tag aus, Tag ein auf der Reise. Die geschäftlichen Aufregungen sind viel größer, als im eigentlichen Geschäft zu Hause, und außerdem ist mir noch diese Plage des Reisens auferlegt, die Sorgen um die Zuganschlüsse, das unregelmäßige, schlechte Essen, ein immer wechselnder, nie andauernder, nie herzlich werdender menschlicher Verkehr. Der Teufel soll das alles holen!« Er fühlte ein leichtes Jucken oben auf dem Bauch; schob sich auf dem Rücken langsam näher zum Bettpfosten, um den Kopf besser heben zu können; fand die juckende Stelle, die mit lauter kleinen weißen Pünktchen besetzt war, die er nicht zu beurteilen verstand; und wollte mit einem Bein die Stelle betasten, zog es aber gleich zurück, denn bei der Berührung umwehten ihn Kälteschauer.\r\rEr glitt wieder in seine frühere Lage zurück. »Dies frühzeitige Aufstehen«, dachte er, »macht einen ganz blödsinnig. Der Mensch muß seinen Schlaf haben. Andere Reisende leben wie Haremsfrauen. Wenn ich zum Beispiel im Laufe des Vormittags ins Gasthaus zurückgehe, um die erlangten Aufträge zu überschreiben, sitzen diese Herren erst beim Frühstück. Das sollte ich bei meinem Chef versuchen; ich würde auf der Stelle hinausfliegen. Wer weiß übrigens, ob das nicht sehr gut für mich wäre. Wenn ich mich nicht wegen meiner Eltern zurückhielte, ich hätte längst gekündigt, ich wäre vor den Chef hin getreten und hätte ihm meine Meinung von Grund des Herzens aus gesagt. Vom Pult hätte er fallen müssen! Es ist auch eine sonderbare Art, sich auf das Pult zu setzen und von der Höhe herab mit dem Angestellten zu reden, der überdies wegen der Schwerhörigkeit des Chefs ganz nahe herantreten muß. Nun, die Hoffnung ist noch nicht gänzlich aufgegeben; habe ich einmal das Geld beisammen, um die Schuld der Eltern an ihn abzuzahlen - es dürfte noch fünf bis sechs Jahre dauern - , mache ich die Sache unbedingt. Dann wird der große Schnitt gemacht. Vorläufig allerdings muß ich aufstehen, denn mein Zug fährt um fünf.«\r\rUnd er sah zur Weckuhr hinüber, die auf dem Kasten tickte. »Himmlischer Vater!«, dachte er. Es war halb sieben Uhr, und die Zeiger gingen ruhig vorwärts, es war sogar halb vorüber, es näherte sich schon dreiviertel. Sollte der Wecker nicht geläutet haben? Man sah vom Bett aus, daß er auf vier Uhr richtig eingestellt war; gewiß hatte er auch geläutet. Ja, aber war es möglich, dieses möbelerschütternde Läuten ruhig zu verschlafen? Nun, ruhig hatte er ja nicht geschlafen, aber wahrscheinlich desto fester. Was aber sollte er jetzt tun? Der nächste Zug ging um sieben Uhr; um den einzuholen, hätte er sich unsinnig beeilen müssen, und die Kollektion war noch nicht eingepackt, und er selbst fühlte sich durchaus nicht besonders frisch und beweglich. Und selbst wenn er den Zug einholte, ein Donnerwetter des Chefs war nicht zu vermeiden, denn der Geschäftsdiener hatte beim Fünfuhrzug gewartet und die Meldung von seiner Versäumnis längst erstattet. Es war eine Kreatur des Chefs, ohne Rückgrat und Verstand. Wie nun, wenn er sich krank meldete? Das wäre aber äußerst peinlich und verdächtig, denn Gregor war während seines fünfjährigen Dienstes noch nicht einmal krank gewesen. Gewiß würde der Chef mit dem Krankenkassenarzt kommen, würde den Eltern wegen des faulen Sohnes Vorwürfe machen und alle Einwände durch den Hinweis auf den Krankenkassenarzt abschneiden, für den es ja überhaupt nur ganz gesunde, aber arbeitsscheue Menschen gibt. Und hätte er übrigens in diesem Falle so ganz unrecht? Gregor fühlte sich tatsächlich, abgesehen von einer nach dem langen Schlaf wirklich überflüssigen Schläfrigkeit, ganz wohl und hatte sogar einen besonders kräftigen Hunger. \r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/kafka_utf8_crlf.txt b/deps/flexi-streams/test/kafka_utf8_crlf.txt
new file mode 100644 (file)
index 0000000..eca3fe5
--- /dev/null
@@ -0,0 +1,11 @@
+Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Er lag auf seinem panzerartig harten Rücken und sah, wenn er den Kopf ein wenig hob, seinen gewölbten, braunen, von bogenförmigen Versteifungen geteilten Bauch, auf dessen Höhe sich die Bettdecke, zum gänzlichen Niedergleiten bereit, kaum noch erhalten konnte. Seine vielen, im Vergleich zu seinem sonstigen Umfang kläglich dünnen Beine flimmerten ihm hilflos vor den Augen.\r
+\r
+»Was ist mit mir geschehen?«, dachte er. Es war kein Traum. Sein Zimmer, ein richtiges, nur etwas zu kleines Menschenzimmer, lag ruhig zwischen den vier wohlbekannten Wänden. Über dem Tisch, auf dem eine auseinandergepackte Musterkollektion von Tuchwaren ausgebreitet war - Samsa war Reisender - hing das Bild, das er vor kurzem aus einer illustrierten Zeitschrift ausgeschnitten und in einem hübschen, vergoldeten Rahmen untergebracht hatte. Es stellte eine Dame dar, die mit einem Pelzhut und einer Pelzboa versehen, aufrecht dasaß und einen schweren Pelzmuff, in dem ihr ganzer Unterarm verschwunden war, dem Beschauer entgegenhob.\r
+\r
+Gregors Blick richtete sich dann zum Fenster, und das trübe Wetter - man hörte Regentropfen auf das Fensterblech aufschlagen - machte ihn ganz melancholisch. »Wie wäre es, wenn ich noch ein wenig weiterschliefe und alle Narrheiten vergäße«, dachte er, aber das war gänzlich undurchführbar, denn er war gewöhnt, auf der rechten Seite zu schlafen, konnte sich aber in seinem gegenwärtigen Zustand nicht in diese Lage bringen. Mit welcher Kraft er sich auch auf die rechte Seite warf, immer wieder schaukelte er in die Rückenlage zurück. Er versuchte es wohl hundertmal, schloß die Augen, um die zappelnden Beine nicht sehen zu müssen, und ließ erst ab, als er in der Seite einen noch nie gefühlten, leichten, dumpfen Schmerz zu fühlen begann.\r
+\r
+»Ach Gott«, dachte er, »was für einen anstrengenden Beruf habe ich gewählt! Tag aus, Tag ein auf der Reise. Die geschäftlichen Aufregungen sind viel größer, als im eigentlichen Geschäft zu Hause, und außerdem ist mir noch diese Plage des Reisens auferlegt, die Sorgen um die Zuganschlüsse, das unregelmäßige, schlechte Essen, ein immer wechselnder, nie andauernder, nie herzlich werdender menschlicher Verkehr. Der Teufel soll das alles holen!« Er fühlte ein leichtes Jucken oben auf dem Bauch; schob sich auf dem Rücken langsam näher zum Bettpfosten, um den Kopf besser heben zu können; fand die juckende Stelle, die mit lauter kleinen weißen Pünktchen besetzt war, die er nicht zu beurteilen verstand; und wollte mit einem Bein die Stelle betasten, zog es aber gleich zurück, denn bei der Berührung umwehten ihn Kälteschauer.\r
+\r
+Er glitt wieder in seine frühere Lage zurück. »Dies frühzeitige Aufstehen«, dachte er, »macht einen ganz blödsinnig. Der Mensch muß seinen Schlaf haben. Andere Reisende leben wie Haremsfrauen. Wenn ich zum Beispiel im Laufe des Vormittags ins Gasthaus zurückgehe, um die erlangten Aufträge zu überschreiben, sitzen diese Herren erst beim Frühstück. Das sollte ich bei meinem Chef versuchen; ich würde auf der Stelle hinausfliegen. Wer weiß übrigens, ob das nicht sehr gut für mich wäre. Wenn ich mich nicht wegen meiner Eltern zurückhielte, ich hätte längst gekündigt, ich wäre vor den Chef hin getreten und hätte ihm meine Meinung von Grund des Herzens aus gesagt. Vom Pult hätte er fallen müssen! Es ist auch eine sonderbare Art, sich auf das Pult zu setzen und von der Höhe herab mit dem Angestellten zu reden, der überdies wegen der Schwerhörigkeit des Chefs ganz nahe herantreten muß. Nun, die Hoffnung ist noch nicht gänzlich aufgegeben; habe ich einmal das Geld beisammen, um die Schuld der Eltern an ihn abzuzahlen - es dürfte noch fünf bis sechs Jahre dauern - , mache ich die Sache unbedingt. Dann wird der große Schnitt gemacht. Vorläufig allerdings muß ich aufstehen, denn mein Zug fährt um fünf.«\r
+\r
+Und er sah zur Weckuhr hinüber, die auf dem Kasten tickte. »Himmlischer Vater!«, dachte er. Es war halb sieben Uhr, und die Zeiger gingen ruhig vorwärts, es war sogar halb vorüber, es näherte sich schon dreiviertel. Sollte der Wecker nicht geläutet haben? Man sah vom Bett aus, daß er auf vier Uhr richtig eingestellt war; gewiß hatte er auch geläutet. Ja, aber war es möglich, dieses möbelerschütternde Läuten ruhig zu verschlafen? Nun, ruhig hatte er ja nicht geschlafen, aber wahrscheinlich desto fester. Was aber sollte er jetzt tun? Der nächste Zug ging um sieben Uhr; um den einzuholen, hätte er sich unsinnig beeilen müssen, und die Kollektion war noch nicht eingepackt, und er selbst fühlte sich durchaus nicht besonders frisch und beweglich. Und selbst wenn er den Zug einholte, ein Donnerwetter des Chefs war nicht zu vermeiden, denn der Geschäftsdiener hatte beim Fünfuhrzug gewartet und die Meldung von seiner Versäumnis längst erstattet. Es war eine Kreatur des Chefs, ohne Rückgrat und Verstand. Wie nun, wenn er sich krank meldete? Das wäre aber äußerst peinlich und verdächtig, denn Gregor war während seines fünfjährigen Dienstes noch nicht einmal krank gewesen. Gewiß würde der Chef mit dem Krankenkassenarzt kommen, würde den Eltern wegen des faulen Sohnes Vorwürfe machen und alle Einwände durch den Hinweis auf den Krankenkassenarzt abschneiden, für den es ja überhaupt nur ganz gesunde, aber arbeitsscheue Menschen gibt. Und hätte er übrigens in diesem Falle so ganz unrecht? Gregor fühlte sich tatsächlich, abgesehen von einer nach dem langen Schlaf wirklich überflüssigen Schläfrigkeit, ganz wohl und hatte sogar einen besonders kräftigen Hunger. \r
diff --git a/deps/flexi-streams/test/kafka_utf8_lf.txt b/deps/flexi-streams/test/kafka_utf8_lf.txt
new file mode 100644 (file)
index 0000000..afa3364
--- /dev/null
@@ -0,0 +1,11 @@
+Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte, fand er sich in seinem Bett zu einem ungeheueren Ungeziefer verwandelt. Er lag auf seinem panzerartig harten Rücken und sah, wenn er den Kopf ein wenig hob, seinen gewölbten, braunen, von bogenförmigen Versteifungen geteilten Bauch, auf dessen Höhe sich die Bettdecke, zum gänzlichen Niedergleiten bereit, kaum noch erhalten konnte. Seine vielen, im Vergleich zu seinem sonstigen Umfang kläglich dünnen Beine flimmerten ihm hilflos vor den Augen.
+
+»Was ist mit mir geschehen?«, dachte er. Es war kein Traum. Sein Zimmer, ein richtiges, nur etwas zu kleines Menschenzimmer, lag ruhig zwischen den vier wohlbekannten Wänden. Über dem Tisch, auf dem eine auseinandergepackte Musterkollektion von Tuchwaren ausgebreitet war - Samsa war Reisender - hing das Bild, das er vor kurzem aus einer illustrierten Zeitschrift ausgeschnitten und in einem hübschen, vergoldeten Rahmen untergebracht hatte. Es stellte eine Dame dar, die mit einem Pelzhut und einer Pelzboa versehen, aufrecht dasaß und einen schweren Pelzmuff, in dem ihr ganzer Unterarm verschwunden war, dem Beschauer entgegenhob.
+
+Gregors Blick richtete sich dann zum Fenster, und das trübe Wetter - man hörte Regentropfen auf das Fensterblech aufschlagen - machte ihn ganz melancholisch. »Wie wäre es, wenn ich noch ein wenig weiterschliefe und alle Narrheiten vergäße«, dachte er, aber das war gänzlich undurchführbar, denn er war gewöhnt, auf der rechten Seite zu schlafen, konnte sich aber in seinem gegenwärtigen Zustand nicht in diese Lage bringen. Mit welcher Kraft er sich auch auf die rechte Seite warf, immer wieder schaukelte er in die Rückenlage zurück. Er versuchte es wohl hundertmal, schloß die Augen, um die zappelnden Beine nicht sehen zu müssen, und ließ erst ab, als er in der Seite einen noch nie gefühlten, leichten, dumpfen Schmerz zu fühlen begann.
+
+»Ach Gott«, dachte er, »was für einen anstrengenden Beruf habe ich gewählt! Tag aus, Tag ein auf der Reise. Die geschäftlichen Aufregungen sind viel größer, als im eigentlichen Geschäft zu Hause, und außerdem ist mir noch diese Plage des Reisens auferlegt, die Sorgen um die Zuganschlüsse, das unregelmäßige, schlechte Essen, ein immer wechselnder, nie andauernder, nie herzlich werdender menschlicher Verkehr. Der Teufel soll das alles holen!« Er fühlte ein leichtes Jucken oben auf dem Bauch; schob sich auf dem Rücken langsam näher zum Bettpfosten, um den Kopf besser heben zu können; fand die juckende Stelle, die mit lauter kleinen weißen Pünktchen besetzt war, die er nicht zu beurteilen verstand; und wollte mit einem Bein die Stelle betasten, zog es aber gleich zurück, denn bei der Berührung umwehten ihn Kälteschauer.
+
+Er glitt wieder in seine frühere Lage zurück. »Dies frühzeitige Aufstehen«, dachte er, »macht einen ganz blödsinnig. Der Mensch muß seinen Schlaf haben. Andere Reisende leben wie Haremsfrauen. Wenn ich zum Beispiel im Laufe des Vormittags ins Gasthaus zurückgehe, um die erlangten Aufträge zu überschreiben, sitzen diese Herren erst beim Frühstück. Das sollte ich bei meinem Chef versuchen; ich würde auf der Stelle hinausfliegen. Wer weiß übrigens, ob das nicht sehr gut für mich wäre. Wenn ich mich nicht wegen meiner Eltern zurückhielte, ich hätte längst gekündigt, ich wäre vor den Chef hin getreten und hätte ihm meine Meinung von Grund des Herzens aus gesagt. Vom Pult hätte er fallen müssen! Es ist auch eine sonderbare Art, sich auf das Pult zu setzen und von der Höhe herab mit dem Angestellten zu reden, der überdies wegen der Schwerhörigkeit des Chefs ganz nahe herantreten muß. Nun, die Hoffnung ist noch nicht gänzlich aufgegeben; habe ich einmal das Geld beisammen, um die Schuld der Eltern an ihn abzuzahlen - es dürfte noch fünf bis sechs Jahre dauern - , mache ich die Sache unbedingt. Dann wird der große Schnitt gemacht. Vorläufig allerdings muß ich aufstehen, denn mein Zug fährt um fünf.«
+
+Und er sah zur Weckuhr hinüber, die auf dem Kasten tickte. »Himmlischer Vater!«, dachte er. Es war halb sieben Uhr, und die Zeiger gingen ruhig vorwärts, es war sogar halb vorüber, es näherte sich schon dreiviertel. Sollte der Wecker nicht geläutet haben? Man sah vom Bett aus, daß er auf vier Uhr richtig eingestellt war; gewiß hatte er auch geläutet. Ja, aber war es möglich, dieses möbelerschütternde Läuten ruhig zu verschlafen? Nun, ruhig hatte er ja nicht geschlafen, aber wahrscheinlich desto fester. Was aber sollte er jetzt tun? Der nächste Zug ging um sieben Uhr; um den einzuholen, hätte er sich unsinnig beeilen müssen, und die Kollektion war noch nicht eingepackt, und er selbst fühlte sich durchaus nicht besonders frisch und beweglich. Und selbst wenn er den Zug einholte, ein Donnerwetter des Chefs war nicht zu vermeiden, denn der Geschäftsdiener hatte beim Fünfuhrzug gewartet und die Meldung von seiner Versäumnis längst erstattet. Es war eine Kreatur des Chefs, ohne Rückgrat und Verstand. Wie nun, wenn er sich krank meldete? Das wäre aber äußerst peinlich und verdächtig, denn Gregor war während seines fünfjährigen Dienstes noch nicht einmal krank gewesen. Gewiß würde der Chef mit dem Krankenkassenarzt kommen, würde den Eltern wegen des faulen Sohnes Vorwürfe machen und alle Einwände durch den Hinweis auf den Krankenkassenarzt abschneiden, für den es ja überhaupt nur ganz gesunde, aber arbeitsscheue Menschen gibt. Und hätte er übrigens in diesem Falle so ganz unrecht? Gregor fühlte sich tatsächlich, abgesehen von einer nach dem langen Schlaf wirklich überflüssigen Schläfrigkeit, ganz wohl und hatte sogar einen besonders kräftigen Hunger. 
diff --git a/deps/flexi-streams/test/packages.lisp b/deps/flexi-streams/test/packages.lisp
new file mode 100644 (file)
index 0000000..0953508
--- /dev/null
@@ -0,0 +1,41 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/test/packages.lisp,v 1.8 2008/08/01 10:12:43 edi Exp $
+
+;;; Copyright (c) 2006-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(defpackage :flexi-streams-test
+  (:use :cl :flexi-streams)
+  (:import-from :flexi-streams
+                :with-unique-names
+                :with-rebinding
+                :char*
+                :normalize-external-format
+                :+name-map+
+                :+shortcut-map+)
+  (:export :run-all-tests))
diff --git a/deps/flexi-streams/test/russian_koi8r_cr.txt b/deps/flexi-streams/test/russian_koi8r_cr.txt
new file mode 100644 (file)
index 0000000..4a6bf9e
--- /dev/null
@@ -0,0 +1 @@
+úÁÒÅÇÉÓÔÒÉÒÕÊÔÅÓØ ÓÅÊÞÁÓ ÎÁ äÅÓÑÔÕÀ íÅÖÄÕÎÁÒÏÄÎÕÀ ëÏÎÆÅÒÅÎÃÉÀ ÐÏ\rUnicode, ËÏÔÏÒÁÑ ÓÏÓÔÏÉÔÓÑ 10-12 ÍÁÒÔÁ 1997 ÇÏÄÁ × íÁÊÎÃÅ × çÅÒÍÁÎÉÉ.\rëÏÎÆÅÒÅÎÃÉÑ ÓÏÂÅÒÅÔ ÛÉÒÏËÉÊ ËÒÕÇ ÜËÓÐÅÒÔÏ× ÐÏ  ×ÏÐÒÏÓÁÍ ÇÌÏÂÁÌØÎÏÇÏ\réÎÔÅÒÎÅÔÁ É Unicode, ÌÏËÁÌÉÚÁÃÉÉ É ÉÎÔÅÒÎÁÃÉÏÎÁÌÉÚÁÃÉÉ, ×ÏÐÌÏÝÅÎÉÀ É\rÐÒÉÍÅÎÅÎÉÀ Unicode × ÒÁÚÌÉÞÎÙÈ ÏÐÅÒÁÃÉÏÎÎÙÈ ÓÉÓÔÅÍÁÈ É ÐÒÏÇÒÁÍÍÎÙÈ\rÐÒÉÌÏÖÅÎÉÑÈ, ÛÒÉÆÔÁÈ, ×ÅÒÓÔËÅ É ÍÎÏÇÏÑÚÙÞÎÙÈ ËÏÍÐØÀÔÅÒÎÙÈ ÓÉÓÔÅÍÁÈ.\r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/russian_koi8r_crlf.txt b/deps/flexi-streams/test/russian_koi8r_crlf.txt
new file mode 100644 (file)
index 0000000..1bf8ccc
--- /dev/null
@@ -0,0 +1,6 @@
+úÁÒÅÇÉÓÔÒÉÒÕÊÔÅÓØ ÓÅÊÞÁÓ ÎÁ äÅÓÑÔÕÀ íÅÖÄÕÎÁÒÏÄÎÕÀ ëÏÎÆÅÒÅÎÃÉÀ ÐÏ\r
+Unicode, ËÏÔÏÒÁÑ ÓÏÓÔÏÉÔÓÑ 10-12 ÍÁÒÔÁ 1997 ÇÏÄÁ × íÁÊÎÃÅ × çÅÒÍÁÎÉÉ.\r
+ëÏÎÆÅÒÅÎÃÉÑ ÓÏÂÅÒÅÔ ÛÉÒÏËÉÊ ËÒÕÇ ÜËÓÐÅÒÔÏ× ÐÏ  ×ÏÐÒÏÓÁÍ ÇÌÏÂÁÌØÎÏÇÏ\r
+éÎÔÅÒÎÅÔÁ É Unicode, ÌÏËÁÌÉÚÁÃÉÉ É ÉÎÔÅÒÎÁÃÉÏÎÁÌÉÚÁÃÉÉ, ×ÏÐÌÏÝÅÎÉÀ É\r
+ÐÒÉÍÅÎÅÎÉÀ Unicode × ÒÁÚÌÉÞÎÙÈ ÏÐÅÒÁÃÉÏÎÎÙÈ ÓÉÓÔÅÍÁÈ É ÐÒÏÇÒÁÍÍÎÙÈ\r
+ÐÒÉÌÏÖÅÎÉÑÈ, ÛÒÉÆÔÁÈ, ×ÅÒÓÔËÅ É ÍÎÏÇÏÑÚÙÞÎÙÈ ËÏÍÐØÀÔÅÒÎÙÈ ÓÉÓÔÅÍÁÈ.\r
diff --git a/deps/flexi-streams/test/russian_koi8r_lf.txt b/deps/flexi-streams/test/russian_koi8r_lf.txt
new file mode 100644 (file)
index 0000000..7f27c81
--- /dev/null
@@ -0,0 +1,6 @@
+úÁÒÅÇÉÓÔÒÉÒÕÊÔÅÓØ ÓÅÊÞÁÓ ÎÁ äÅÓÑÔÕÀ íÅÖÄÕÎÁÒÏÄÎÕÀ ëÏÎÆÅÒÅÎÃÉÀ ÐÏ
+Unicode, ËÏÔÏÒÁÑ ÓÏÓÔÏÉÔÓÑ 10-12 ÍÁÒÔÁ 1997 ÇÏÄÁ × íÁÊÎÃÅ × çÅÒÍÁÎÉÉ.
+ëÏÎÆÅÒÅÎÃÉÑ ÓÏÂÅÒÅÔ ÛÉÒÏËÉÊ ËÒÕÇ ÜËÓÐÅÒÔÏ× ÐÏ  ×ÏÐÒÏÓÁÍ ÇÌÏÂÁÌØÎÏÇÏ
+éÎÔÅÒÎÅÔÁ É Unicode, ÌÏËÁÌÉÚÁÃÉÉ É ÉÎÔÅÒÎÁÃÉÏÎÁÌÉÚÁÃÉÉ, ×ÏÐÌÏÝÅÎÉÀ É
+ÐÒÉÍÅÎÅÎÉÀ Unicode × ÒÁÚÌÉÞÎÙÈ ÏÐÅÒÁÃÉÏÎÎÙÈ ÓÉÓÔÅÍÁÈ É ÐÒÏÇÒÁÍÍÎÙÈ
+ÐÒÉÌÏÖÅÎÉÑÈ, ÛÒÉÆÔÁÈ, ×ÅÒÓÔËÅ É ÍÎÏÇÏÑÚÙÞÎÙÈ ËÏÍÐØÀÔÅÒÎÙÈ ÓÉÓÔÅÍÁÈ.
diff --git a/deps/flexi-streams/test/russian_utf8_cr.txt b/deps/flexi-streams/test/russian_utf8_cr.txt
new file mode 100644 (file)
index 0000000..0a02785
--- /dev/null
@@ -0,0 +1 @@
+Зарегистрируйтесь сейчас на Десятую Международную Конференцию по\rUnicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.\rКонференция соберет широкий круг экспертов по  вопросам глобального\rИнтернета и Unicode, локализации и интернационализации, воплощению и\rприменению Unicode в различных операционных системах и программных\rприложениях, шрифтах, верстке и многоязычных компьютерных системах.\r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/russian_utf8_crlf.txt b/deps/flexi-streams/test/russian_utf8_crlf.txt
new file mode 100644 (file)
index 0000000..79fed2f
--- /dev/null
@@ -0,0 +1,6 @@
+Зарегистрируйтесь сейчас на Десятую Международную Конференцию по\r
+Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.\r
+Конференция соберет широкий круг экспертов по  вопросам глобального\r
+Интернета и Unicode, локализации и интернационализации, воплощению и\r
+применению Unicode в различных операционных системах и программных\r
+приложениях, шрифтах, верстке и многоязычных компьютерных системах.\r
diff --git a/deps/flexi-streams/test/russian_utf8_lf.txt b/deps/flexi-streams/test/russian_utf8_lf.txt
new file mode 100644 (file)
index 0000000..75e097d
--- /dev/null
@@ -0,0 +1,6 @@
+Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
+Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
+Конференция соберет широкий круг экспертов по  вопросам глобального
+Интернета и Unicode, локализации и интернационализации, воплощению и
+применению Unicode в различных операционных системах и программных
+приложениях, шрифтах, верстке и многоязычных компьютерных системах.
diff --git a/deps/flexi-streams/test/test.lisp b/deps/flexi-streams/test/test.lisp
new file mode 100644 (file)
index 0000000..0db75ab
--- /dev/null
@@ -0,0 +1,728 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS-TEST; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/test/test.lisp,v 1.39 2008/05/30 09:10:55 edi Exp $
+
+;;; Copyright (c) 2006-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams-test)
+
+(defmacro with-test-suite ((test-description &key show-progress-p) &body body)
+  "Defines a test suite.  Three utilities are available inside of the
+body of the macro: The function FAIL, and the macros CHECK and
+WITH-EXPECTED-ERROR.  FAIL, the lowest level utility, marks the test
+defined by WITH-TEST-SUITE as failed.  CHECK checks whether its argument is
+true, otherwise it calls FAIL. If during evaluation of the specified
+expression any condition is signalled, this is also considered a
+failure.  WITH-EXPECTED-ERROR executes its body and considers the test
+a success if the specified error was signalled, otherwise it calls
+FAIL.
+
+WITH-TEST-SUITE prints a simple progress report if SHOW-PROGRESS-P is true."
+  (with-unique-names (successp testcount)
+    (with-rebinding (show-progress-p)
+      `(let ((,successp t)
+             (,testcount 1))
+         (when (and ,show-progress-p (not (numberp ,show-progress-p)))
+           (setq ,show-progress-p 1))
+         (flet ((fail (format-str &rest format-args)
+                  (apply #'format t format-str format-args)
+                  (setq ,successp nil))
+                (maybe-show-progress ()
+                  (when (and ,show-progress-p (zerop (mod ,testcount ,show-progress-p)))
+                    (format t ".")
+                    (when (zerop (mod ,testcount (* 10 ,show-progress-p)))
+                      (terpri))
+                    (force-output))
+                  (incf ,testcount)))
+           (macrolet ((check (expression)
+                        `(progn
+                           (maybe-show-progress)
+                           (handler-case
+                               (unless ,expression
+                                 (fail "~&Test ~S failed.~%" ',expression))
+                             (error (c)
+                               (fail "~&Test ~S failed signalling error of type ~A: ~A.~%" 
+                                     ',expression (type-of c) c)))))
+                      (with-expected-error ((condition-type) &body body)
+                        `(progn
+                           (maybe-show-progress)
+                           (handler-case (progn ,@body)
+                             (,condition-type () t)
+                             (:no-error (&rest args)
+                               (declare (ignore args))                           
+                               (fail "~&Expected condition ~S not signalled.~%"
+                                     ',condition-type))))))
+             (format t "~&Test suite: ~S~%" ,test-description)
+             ,@body))
+         ,successp))))
+
+;; LW can't indent this correctly because it's in a MACROLET
+#+:lispworks
+(editor:setup-indent "with-expected-error" 1 2 4)
+
+(defconstant +buffer-size+ 8192
+  "Size of buffers for COPY-STREAM* below.")
+
+(defvar *copy-function* nil
+  "Which function to use when copying from one stream to the other -
+see for example COPY-FILE below.")
+
+(defvar *this-file* (load-time-value
+                     (or #.*compile-file-pathname* *load-pathname*))
+  "The pathname of the file \(`test.lisp') where this variable was
+defined.")
+
+#+:lispworks
+(defun get-env-variable-as-directory (name)
+  (lw:when-let (string (lw:environment-variable name))
+    (when (plusp (length string))
+      (cond ((find (char string (1- (length string))) "\\/" :test #'char=) string)
+            (t (lw:string-append string "/"))))))
+
+(defvar *tmp-dir*
+  (load-time-value
+    (merge-pathnames "odd-streams-test/"
+                     #+:allegro (system:temporary-directory)
+                     #+:lispworks (pathname (or (get-env-variable-as-directory "TEMP")
+                                                (get-env-variable-as-directory "TMP")
+                                                #+:win32 "C:/"
+                                                #-:win32 "/tmp/"))
+                     #-(or :allegro :lispworks) #p"/tmp/"))
+  "The pathname of a temporary directory used for testing.")
+
+(defvar *test-files*
+  '(("kafka" (:utf8 :latin1 :cp1252))
+    ("tilton" (:utf8 :ascii))
+    ("hebrew" (:utf8 :latin8))
+    ("russian" (:utf8 :koi8r))
+    ("unicode_demo" (:utf8 :ucs2 :ucs4)))
+  "A list of test files where each entry consists of the name
+prefix and a list of encodings.")
+
+(defun create-file-variants (file-name symbol)
+  "For a name suffix FILE-NAME and a symbol SYMBOL denoting an
+encoding returns a list of pairs where the car is a full file
+name and the cdr is the corresponding external format.  This list
+contains all possible variants w.r.t. to line-end conversion and
+endianness."
+  (let ((args (ecase symbol
+                (:ascii '(:ascii))
+                (:latin1 '(:latin-1))
+                (:latin8 '(:hebrew))
+                (:cp1252 '(:code-page :id 1252))
+                (:koi8r '(:koi8-r))
+                (:utf8 '(:utf-8))
+                (:ucs2 '(:utf-16))
+                (:ucs4 '(:utf-32))))
+        (endianp (member symbol '(:ucs2 :ucs4))))
+    (loop for little-endian in (if endianp '(t nil) '(t))
+          for endian-suffix in (if endianp '("_le" "_be") '(""))
+          nconc (loop for eol-style in '(:lf :cr :crlf)
+                      collect (cons (format nil "~A_~(~A~)_~(~A~)~A.txt"
+                                            file-name symbol eol-style endian-suffix)
+                                    (apply #'make-external-format
+                                           (append args `(:eol-style ,eol-style
+                                                          :little-endian ,little-endian))))))))
+
+(defun create-test-combinations (file-name symbols &optional simplep)
+  "For a name suffix FILE-NAME and a list of symbols SYMBOLS denoting
+different encodings of the corresponding file returns a list of lists
+which can be used as arglists by COMPARE-FILES.  If SIMPLEP is true, a
+list which can be used for the string and sequence tests below is
+returned."
+  (let ((file-variants (loop for symbol in symbols
+                             nconc (create-file-variants file-name symbol))))
+    (loop for (name-in . external-format-in) in file-variants
+          when simplep
+          collect (list name-in external-format-in)
+          else
+          nconc (loop for (name-out . external-format-out) in file-variants
+                      collect (list name-in external-format-in name-out external-format-out)))))
+                      
+(defun file-equal (file1 file2)
+  "Returns a true value iff FILE1 and FILE2 have the same
+contents \(viewed as binary files)."
+  (with-open-file (stream1 file1 :element-type 'octet)
+    (with-open-file (stream2 file2 :element-type 'octet)
+      (and (= (file-length stream1) (file-length stream2))
+           (loop for byte1 = (read-byte stream1 nil nil)
+                 for byte2 = (read-byte stream2 nil nil)
+                 while (and byte1 byte2)
+                 always (= byte1 byte2))))))
+
+(defun copy-stream (stream-in external-format-in stream-out external-format-out)
+  "Copies the contents of the binary stream STREAM-IN to the
+binary stream STREAM-OUT using flexi streams - STREAM-IN is read
+with the external format EXTERNAL-FORMAT-IN and STREAM-OUT is
+written with EXTERNAL-FORMAT-OUT."
+  (let ((in (make-flexi-stream stream-in :external-format external-format-in))
+        (out (make-flexi-stream stream-out :external-format external-format-out)))
+    (loop for line = (read-line in nil nil)
+          while line
+          do (write-line line out))))
+
+(defun copy-stream* (stream-in external-format-in stream-out external-format-out)
+  "Like COPY-STREAM, but uses READ-SEQUENCE and WRITE-SEQUENCE instead
+of READ-LINE and WRITE-LINE."
+  (let ((in (make-flexi-stream stream-in :external-format external-format-in))
+        (out (make-flexi-stream stream-out :external-format external-format-out))
+        (buffer (make-array +buffer-size+ :element-type 'char*)))
+    (loop
+     (let ((position (read-sequence buffer in)))
+       (when (zerop position) (return))
+       (write-sequence buffer out :end position)))))
+
+(defun copy-file (path-in external-format-in path-out external-format-out direction-out direction-in)
+  "Copies the contents of the file denoted by the pathname
+PATH-IN to the file denoted by the pathname PATH-OUT using flexi
+streams - STREAM-IN is read with the external format
+EXTERNAL-FORMAT-IN and STREAM-OUT is written with
+EXTERNAL-FORMAT-OUT.  The input file is opened with
+the :DIRECTION keyword argument DIRECTION-IN, the output file is
+opened with the :DIRECTION keyword argument DIRECTION-OUT."
+  (with-open-file (in path-in
+                      :element-type 'octet
+                      :direction direction-in
+                      :if-does-not-exist :error
+                      :if-exists :overwrite)
+    (with-open-file (out path-out
+                         :element-type 'octet
+                         :direction direction-out
+                         :if-does-not-exist :create
+                         :if-exists :supersede)
+      (funcall *copy-function* in external-format-in out external-format-out))))
+
+#+:lispworks
+(defun copy-file-lw (path-in external-format-in path-out external-format-out direction-out direction-in)
+  "Same as COPY-FILE, but uses character streams instead of
+binary streams.  Only used to test LispWorks-specific behaviour."
+  (with-open-file (in path-in
+                      :external-format '(:latin-1 :eol-style :lf)
+                      :element-type 'base-char
+                      :direction direction-in
+                      :if-does-not-exist :error
+                      :if-exists :overwrite)
+    (with-open-file (out path-out
+                         :external-format '(:latin-1 :eol-style :lf)
+                         :element-type 'base-char
+                         :direction direction-out
+                         :direction :output
+                         :if-does-not-exist :create
+                         :if-exists :supersede)
+      (funcall *copy-function* in external-format-in out external-format-out))))
+
+(defun compare-files (&key verbose)
+  "Each test in this suite copies the contents of one file \(in the
+`test' directory) to another file \(in a temporary directory) using
+flexi streams with different external formats.  The resulting file is
+compared with an existing file in the `test' directory to check if the
+outcome is as expected.  Uses various variants of the :DIRECTION
+keyword when opening the files.
+
+Returns a true value iff all tests succeeded.  Prints information
+about each individual comparison if VERBOSE is true."
+  (with-test-suite ("Reading/writing files" :show-progress-p (not verbose))      
+    (flet ((one-comparison (path-in external-format-in path-out external-format-out verbose) 
+             (when verbose
+               (format t "~&File ~S, using copy function ~S" (file-namestring path-in) *copy-function*)
+               (format t "~&  and external formats ~S --> ~S"
+                       (normalize-external-format external-format-in)
+                       (normalize-external-format external-format-out)))
+             (let ((full-path-in (merge-pathnames path-in *this-file*))
+                   (full-path-out (ensure-directories-exist
+                                   (merge-pathnames path-out *tmp-dir*)))
+                   (full-path-orig (merge-pathnames path-out *this-file*)))
+               (dolist (direction-out '(:output :io))
+                 (dolist (direction-in '(:input :io))
+                   (when verbose
+                     (format t "~&...directions ~S --> ~S" direction-in direction-out))
+                   (copy-file full-path-in external-format-in
+                              full-path-out external-format-out
+                              direction-out direction-in)
+                   (check (file-equal full-path-out full-path-orig))
+                   #+:lispworks
+                   (progn
+                   (when verbose
+                     (format t "~&...directions ~S --> ~S \(LispWorks)" direction-in direction-out))
+                     (copy-file-lw full-path-in external-format-in
+                                   full-path-out external-format-out
+                                   direction-out direction-in)
+                     (check (file-equal full-path-out full-path-orig))))))))
+      (loop with compare-files-args-list = (loop for (file-name symbols) in *test-files*
+                                                 nconc (create-test-combinations file-name symbols))
+            for *copy-function* in '(copy-stream copy-stream*)
+            do (loop for (path-in external-format-in path-out external-format-out) in compare-files-args-list
+                     do (one-comparison path-in external-format-in path-out external-format-out verbose))))))
+
+(defun file-as-octet-vector (pathspec)
+  "Returns the contents of the file denoted by PATHSPEC as a vector of
+octets."
+  (with-open-file (in pathspec :element-type 'octet)
+    (let ((vector (make-array (file-length in) :element-type 'octet)))
+      (read-sequence vector in)
+      vector)))
+
+(defun file-as-string (pathspec external-format)
+  "Reads the contents of the file denoted by PATHSPEC using the
+external format EXTERNAL-FORMAT and returns the result as a string."
+  (with-open-file (in pathspec :element-type 'octet)
+    (let* ((number-of-octets (file-length in))
+           (in (make-flexi-stream in :external-format external-format))
+           (string (make-array number-of-octets
+                               :element-type #+:lispworks 'lw:simple-char
+                                             #-:lispworks 'character
+                               :fill-pointer t)))
+      (setf (fill-pointer string) (read-sequence string in))
+      string)))
+
+(defun old-string-to-octets (string &key
+                                    (external-format (make-external-format :latin1))
+                                    (start 0) end)
+  "The old version of STRING-TO-OCTETS.  We can use it to test
+in-memory streams."
+  (declare (optimize speed))
+  (with-output-to-sequence (out)
+    (let ((flexi (make-flexi-stream out :external-format external-format)))
+      (write-string string flexi :start start :end end))))
+
+(defun old-octets-to-string (vector &key
+                                    (external-format (make-external-format :latin1))
+                                    (start 0) (end (length vector)))
+  "The old version of OCTETS-TO-STRING.  We can use it to test
+in-memory streams."
+  (declare (optimize speed))
+  (with-input-from-sequence (in vector :start start :end end)
+    (let ((flexi (make-flexi-stream in :external-format external-format))
+          (result (make-array (- end start)
+                              :element-type #+:lispworks 'lw:simple-char
+                                            #-:lispworks 'character
+                              :fill-pointer t)))
+      (setf (fill-pointer result)
+            (read-sequence result flexi))
+      result)))
+
+(defun string-tests (&key verbose)
+  "Tests whether conversion from strings to octets and vice versa
+works as expected.  Also tests with the old versions of the conversion
+functions in order to test in-memory streams."
+  (with-test-suite ("String tests" :show-progress-p (and (not verbose) 10))
+    (flet ((one-string-test (pathspec external-format verbose)
+             (when verbose
+               (format t "~&With external format ~S:" (normalize-external-format external-format)))
+             (let* ((full-path (merge-pathnames pathspec *this-file*))
+                    (octets-vector (file-as-octet-vector full-path))
+                    (octets-list (coerce octets-vector 'list))
+                    (string (file-as-string full-path external-format)))
+               (when verbose
+                 (format t "~&...testing OCTETS-TO-STRING"))
+               (check (string= (octets-to-string octets-vector :external-format external-format) string))
+               (check (string= (octets-to-string octets-list :external-format external-format) string))
+               (when verbose
+                 (format t "~&...testing STRING-TO-OCTETS"))
+               (check (equalp (string-to-octets string :external-format external-format) octets-vector))
+               (when verbose
+                 (format t "~&...testing in-memory streams"))
+               (check (string= (old-octets-to-string octets-vector :external-format external-format) string))
+               (check (string= (old-octets-to-string octets-list :external-format external-format) string))
+               (check (equalp (old-string-to-octets string :external-format external-format) octets-vector)))))
+      (loop with simple-test-args-list = (loop for (file-name symbols) in *test-files*
+                                               nconc (create-test-combinations file-name symbols t))
+            for (pathspec external-format) in simple-test-args-list
+            do (one-string-test pathspec external-format verbose)))))
+      
+
+(defun sequence-equal (seq1 seq2)
+  "Whether the two sequences have the same elements."
+  (and (= (length seq1) (length seq2))
+       (loop for i below (length seq1)
+             always (eql (elt seq1 i) (elt seq2 i)))))
+
+(defun sequence-tests (&key verbose)
+  "Several tests to confirm that READ-SEQUENCE and WRITE-SEQUENCE
+behave as expected."
+  (with-test-suite ("Sequence tests" :show-progress-p (and (not verbose) 10))
+    (flet ((one-sequence-test (pathspec external-format verbose)
+             (when verbose
+               (format t "~&With external format ~S:" (normalize-external-format external-format)))
+             (let* ((full-path (merge-pathnames pathspec *this-file*))
+                    (file-string (file-as-string full-path external-format))
+                    (string-length (length file-string))
+                    (octets (file-as-octet-vector full-path))
+                    (octet-length (length octets)))
+               (when (external-format-equal external-format (make-external-format :utf8))
+                 (when verbose
+                   (format t "~&...reading octets"))
+                 #-:openmcl
+                 ;; FLEXI-STREAMS puts integers into the list, but OpenMCL
+                 ;; thinks they are characters...
+                 (with-open-file (in full-path :element-type 'octet)
+                   (let* ((in (make-flexi-stream in :external-format external-format))
+                          (list (make-list octet-length)))
+                     (setf (flexi-stream-element-type in) 'octet)
+                     #-:clisp
+                     (read-sequence list in)
+                     #+:clisp
+                     (ext:read-byte-sequence list in)
+                     (check (sequence-equal list octets))))
+                 (with-open-file (in full-path :element-type 'octet)
+                   (let* ((in (make-flexi-stream in :external-format external-format))
+                          (third (floor octet-length 3))
+                          (half (floor octet-length 2))
+                          (vector (make-array half :element-type 'octet)))
+                     (check (sequence-equal (loop repeat third
+                                                  collect (read-byte in))
+                                            (subseq octets 0 third)))
+                     (read-sequence vector in)
+                     (check (sequence-equal vector (subseq octets third (+ third half)))))))
+               (when verbose
+                 (format t "~&...reading characters"))
+               (with-open-file (in full-path :element-type 'octet)
+                 (let* ((in (make-flexi-stream in :external-format external-format))
+                        (string (make-string (- string-length 10) :element-type 'char*)))
+                   (setf (flexi-stream-element-type in) 'octet)
+                   (check (sequence-equal (loop repeat 10
+                                                collect (read-char in))
+                                          (subseq file-string 0 10)))
+                   (read-sequence string in)
+                   (check (sequence-equal string (subseq file-string 10)))))
+               (with-open-file (in full-path :element-type 'octet)
+                 (let* ((in (make-flexi-stream in :external-format external-format))
+                        (list (make-list (- string-length 100))))
+                   (check (sequence-equal (loop repeat 50
+                                                collect (read-char in))
+                                          (subseq file-string 0 50)))
+                   #-:clisp
+                   (read-sequence list in)
+                   #+:clisp
+                   (ext:read-char-sequence list in)
+                   (check (sequence-equal list (subseq file-string 50 (- string-length 50))))
+                   (check (sequence-equal (loop repeat 50
+                                                collect (read-char in))
+                                          (subseq file-string (- string-length 50))))))
+               (with-open-file (in full-path :element-type 'octet)
+                 (let* ((in (make-flexi-stream in :external-format external-format))
+                        (array (make-array (- string-length 50))))
+                   (check (sequence-equal (loop repeat 25
+                                                collect (read-char in))
+                                          (subseq file-string 0 25)))
+                   #-:clisp
+                   (read-sequence array in)
+                   #+:clisp
+                   (ext:read-char-sequence array in)
+                   (check (sequence-equal array (subseq file-string 25 (- string-length 25))))
+                   (check (sequence-equal (loop repeat 25
+                                                collect (read-char in))
+                                          (subseq file-string (- string-length 25))))))
+               (let ((path-out (ensure-directories-exist (merge-pathnames pathspec *tmp-dir*))))
+                 (when verbose
+                   (format t "~&...writing sequences"))
+                 (with-open-file (out path-out
+                                      :direction :output
+                                      :if-exists :supersede
+                                      :element-type 'octet)
+                   (let ((out (make-flexi-stream out :external-format external-format)))
+                     (write-sequence octets out)))
+                 (check (file-equal full-path path-out))
+                 (with-open-file (out path-out
+                                      :direction :output
+                                      :if-exists :supersede
+                                      :element-type 'octet)
+                   (let ((out (make-flexi-stream out :external-format external-format)))
+                     (write-sequence file-string out)))
+                 (check (file-equal full-path path-out))
+                 (with-open-file (out path-out
+                                      :direction :output
+                                      :if-exists :supersede
+                                      :element-type 'octet)
+                   (let ((out (make-flexi-stream out :external-format external-format)))
+                     (write-sequence file-string out :end 100)
+                     (write-sequence octets out
+                                     :start (length (string-to-octets file-string
+                                                                      :external-format external-format
+                                                                      :end 100)))))
+                 (check (file-equal full-path path-out))))))
+
+      (loop with simple-test-args-list = (loop for (file-name symbols) in *test-files*
+                                               nconc (create-test-combinations file-name symbols t))
+            for (pathspec external-format) in simple-test-args-list
+            do (one-sequence-test pathspec external-format verbose)))))
+
+(defmacro using-values ((&rest values) &body body)
+  "Executes BODY and feeds an element from VALUES to the USE-VALUE
+restart each time a EXTERNAL-FORMAT-ENCODING-ERROR is signalled.
+Signals an error when there are more or less
+EXTERNAL-FORMAT-ENCODING-ERRORs than there are elements in VALUES."
+  (with-unique-names (value-stack condition-counter)
+    `(let ((,value-stack ',values)
+          (,condition-counter 0))
+       (handler-bind ((external-format-encoding-error
+                       #'(lambda (c)
+                           (declare (ignore c)) 
+                           (unless ,value-stack
+                             (error "Too many encoding errors signalled, expected only ~A."
+                                    ,(length values)))
+                           (incf ,condition-counter)
+                           (use-value (pop ,value-stack)))))
+         (prog1 (progn ,@body)
+           (when ,value-stack
+             (error "~A encoding errors signalled, but ~A were expected."
+                    ,condition-counter ,(length values))))))))
+
+(defun accept-overlong (octets code-point)
+  "Converts the `overlong' UTF-8 sequence OCTETS to using
+OCTETS-TO-STRINGS, accepts the expected error with the corresponding
+restart and checks that the result is CODE-POINT."
+  (handler-bind ((external-format-encoding-error
+                  (lambda (c)
+                    (declare (ignore c))
+                    (invoke-restart 'accept-overlong-sequence))))
+    (string= (octets-to-string octets :external-format :utf-8)
+             (string (code-char code-point)))))
+
+(defun read-flexi-line (sequence external-format)
+  "Creates and returns a string from the octet sequence SEQUENCE using
+the external format EXTERNAL-FORMAT."
+  (with-input-from-sequence (in sequence)
+    (setq in (make-flexi-stream in :external-format external-format))
+    (read-line in)))
+
+(defun read-flexi-line* (sequence external-format)
+  "Like READ-FLEXI-LINE but uses OCTETS-TO-STRING internally."
+  (octets-to-string sequence :external-format external-format))
+
+(defun error-handling-tests (&key verbose)
+  "Tests several possible errors and how they are handled."
+  (with-test-suite ("Testing error handling" :show-progress-p (not verbose))
+    (macrolet ((want-encoding-error (input format)
+                 `(with-expected-error (external-format-encoding-error)
+                    (read-flexi-line* ,input ,format))))
+      (when verbose
+        (format t "~&\"Overlong\" UTF-8 sequences"))
+      (want-encoding-error #(#b11000000 #b10000000) :utf-8)
+      (want-encoding-error #(#b11000001 #b10000000) :utf-8)
+      (want-encoding-error #(#b11100000 #b10011111 #b10000000) :utf-8)
+      (want-encoding-error #(#b11110000 #b10001111 #b10000000 #b10000000) :utf-8)
+      (check (accept-overlong #(#b11000000 #b10000000) #b00000000))
+      (check (accept-overlong #(#b11000001 #b10000000) #b01000000))
+      (check (accept-overlong #(#b11100000 #b10011111 #b10000000) #b011111000000))
+      (check (accept-overlong #(#b11110000 #b10001111 #b10000000 #b10000000)
+                              #b1111000000000000))
+      (when verbose
+        (format t "~&Invalid lead octets in UTF-8"))
+      (want-encoding-error #(#b11111000) :utf-8)
+      (want-encoding-error #(#b11111001) :utf-8)
+      (want-encoding-error #(#b11111100) :utf-8)
+      (want-encoding-error #(#b11111101) :utf-8)
+      (want-encoding-error #(#b11111110) :utf-8)
+      (want-encoding-error #(#b11111111) :utf-8)
+      (when verbose
+        (format t "~&Illegal code points"))
+      (want-encoding-error #(#x00 #x00 #x11 #x00) :utf-32le)
+      (want-encoding-error #(#x00 #xd8) :utf-16le)
+      (want-encoding-error #(#xff #xdf) :utf-16le))
+    (macrolet ((want-encoding-error (input format)
+                 `(with-expected-error (external-format-encoding-error)
+                    (read-flexi-line* ,input ,format))))                 
+      (when verbose
+        (format t "~&UTF-8 sequences which are too short"))
+      (want-encoding-error #(#xe4 #xf6 #xfc) :utf8)
+      (want-encoding-error #(#xc0) :utf8)
+      (want-encoding-error #(#xe0 #xff) :utf8)
+      (want-encoding-error #(#xf0 #xff #xff) :utf8)
+      (when verbose
+        (format t "~&UTF-16 sequences with an odd number of octets"))
+      (want-encoding-error #(#x01) :utf-16le)
+      (want-encoding-error #(#x01 #x01 #x01) :utf-16le)
+      (want-encoding-error #(#x01) :utf-16be)
+      (want-encoding-error #(#x01 #x01 #x01) :utf-16be)
+      (when verbose
+        (format t "~&Missing words in UTF-16"))
+      (want-encoding-error #(#x01 #xd8) :utf-16le)
+      (want-encoding-error #(#xd8 #x01) :utf-16be)
+      (when verbose
+        (format t "~&Missing octets in UTF-32"))
+      (want-encoding-error #(#x01) :utf-32le)
+      (want-encoding-error #(#x01 #x01) :utf-32le)
+      (want-encoding-error #(#x01 #x01 #x01) :utf-32le)
+      (want-encoding-error #(#x01 #x01 #x01 #x01 #x01) :utf-32le)
+      (want-encoding-error #(#x01) :utf-32be)
+      (want-encoding-error #(#x01 #x01) :utf-32be)
+      (want-encoding-error #(#x01 #x01 #x01) :utf-32be)
+      (want-encoding-error #(#x01 #x01 #x01 #x01 #x01) :utf-32be))
+    (when verbose
+      (format t "~&Handling of EOF in the middle of CRLF"))
+    (check (string= #.(string #\Return)
+                    (read-flexi-line `(,(char-code #\Return)) '(:ascii :eol-style :crlf))))
+    (let ((*substitution-char* #\?))
+      (when verbose
+        (format t "~&Fixed substitution character #\?")
+        (format t "~&:ASCII doesn't have characters with char codes > 127"))
+      (check (string= "a??" (read-flexi-line `(,(char-code #\a) 128 200) :ascii)))
+      (check (string= "a??" (read-flexi-line* `#(,(char-code #\a) 128 200) :ascii)))
+      (when verbose
+        (format t "~&:WINDOWS-1253 doesn't have a characters with codes 170 and 210"))
+      (check (string= "a??" (read-flexi-line `(,(char-code #\a) 170 210) :windows-1253)))
+      (check (string= "a??" (read-flexi-line* `#(,(char-code #\a) 170 210) :windows-1253)))
+      (when verbose
+        (format t "~&Not a valid UTF-8 sequence"))
+      (check (string= "??" (read-flexi-line '(#xe4 #xf6 #xfc) :utf8))))
+    (let ((*substitution-char* nil))
+      (when verbose
+        (format t "~&Variable substitution using USE-VALUE restart")
+        (format t "~&:ASCII doesn't have characters with char codes > 127"))
+      (check (string= "abc" (using-values (#\b #\c)
+                              (read-flexi-line `(,(char-code #\a) 128 200) :ascii))))
+      (check (string= "abc" (using-values (#\b #\c)
+                              (read-flexi-line* `#(,(char-code #\a) 128 200) :ascii))))
+      (when verbose
+        (format t "~&:WINDOWS-1253 doesn't have a characters with codes 170 and 210"))
+      (check (string= "axy" (using-values (#\x #\y)
+                              (read-flexi-line `(,(char-code #\a) 170 210) :windows-1253))))
+      (check (string= "axy" (using-values (#\x #\y)
+                              (read-flexi-line* `#(,(char-code #\a) 170 210) :windows-1253))))
+      (when verbose
+        (format t "~&Not a valid UTF-8 sequence"))
+      (check (string= "QW" (using-values (#\Q #\W) (read-flexi-line '(#xe4 #xf6 #xfc) :utf8))))
+      (when verbose
+        (format t "~&UTF-8 can't start neither with #b11111110 nor with #b11111111"))
+      (check (string= "QW" (using-values (#\Q #\W) (read-flexi-line '(#b11111110 #b11111111) :utf8))))
+      (when verbose
+        (format t "~&Only one octet in UTF-16 sequence"))
+      (check (string= "E" (using-values (#\E) (read-flexi-line '(#x01) :utf-16le))))
+      (when verbose
+        (format t "~&Two octets in UTF-16, but value of resulting word suggests that another word follows"))
+      (check (string= "R" (using-values (#\R) (read-flexi-line '(#x01 #xd8) :utf-16le))))
+      (when verbose
+        (format t "~&The second word must fit into the [#xdc00; #xdfff] interval, but it is #xdbff"))
+      (check (string= "T" (using-values (#\T) (read-flexi-line '(#x01 #xd8 #xff #xdb) :utf-16le))))
+      (check (string= "T" (using-values (#\T) (read-flexi-line* #(#x01 #xd8 #xff #xdb) :utf-16le))))
+      (when verbose
+        (format t "~&The same as for little endian above, but using inverse order of bytes in words"))
+      (check (string= "E" (using-values (#\E) (read-flexi-line '(#x01) :utf-16be))))
+      (check (string= "R" (using-values (#\R) (read-flexi-line '(#xd8 #x01) :utf-16be))))
+      (check (string= "T" (using-values (#\T) (read-flexi-line '(#xd8 #x01 #xdb #xff) :utf-16be))))
+      (check (string= "T" (using-values (#\T) (read-flexi-line* #(#xd8 #x01 #xdb #xff) :utf-16be))))
+      (when verbose
+        (format t "~&EOF in the middle of a 4-octet sequence in UTF-32"))
+      (check (string= "Y" (using-values (#\Y) (read-flexi-line '(#x01) :utf-32le))))
+      (check (string= "Y" (using-values (#\Y) (read-flexi-line '(#x01 #x01) :utf-32le))))
+      (check (string= "Y" (using-values (#\Y) (read-flexi-line '(#x01 #x01 #x01) :utf-32le))))
+      (check (string= "aY" (using-values (#\Y)
+                             (read-flexi-line `(,(char-code #\a) #x00 #x00 #x00 #x01) :utf-32le))))
+      (check (string= "Y" (using-values (#\Y) (read-flexi-line '(#x01) :utf-32be))))
+      (check (string= "Y" (using-values (#\Y) (read-flexi-line '(#x01 #x01) :utf-32be))))
+      (check (string= "Y" (using-values (#\Y) (read-flexi-line '(#x01 #x01 #x01) :utf-32be))))
+      (check (string= "aY" (using-values (#\Y)
+                             (read-flexi-line `(#x00 #x00 #x00 ,(char-code #\a) #x01) :utf-32be)))))))
+
+(defun unread-char-tests (&key verbose)
+  "Tests whether UNREAD-CHAR behaves as expected."
+  (with-test-suite ("UNREAD-CHAR behaviour." :show-progress-p (and (not verbose) 100))
+    (flet ((test-one-file (file-name external-format)
+             (when verbose
+               (format t "~&  ...and external format ~A" (normalize-external-format external-format)))
+             (with-open-file (in (merge-pathnames file-name *this-file*)
+                                 :element-type 'flex:octet)
+               (let ((in (make-flexi-stream in :external-format external-format)))
+                 (loop repeat 300
+                       for char = (read-char in)
+                       do (unread-char char in)
+                          (check (char= (read-char in) char)))))))
+      (loop for (file-name symbols) in *test-files*
+            when verbose
+            do (format t "~&With file ~S" file-name)
+            do (loop for symbol in symbols
+                     do (loop for (file-name . external-format) in (create-file-variants file-name symbol)
+                              do (test-one-file file-name external-format)))))))
+
+(defun column-tests (&key verbose)
+  (with-test-suite ("STREAM-LINE-COLUMN tests" :show-progress-p (not verbose))
+    (let* ((binary-stream (flexi-streams:make-in-memory-output-stream))
+           (stream (flexi-streams:make-flexi-stream binary-stream :external-format :iso-8859-1)))
+      (write-sequence "hello" stream)
+      (format stream "~12Tworld")
+      (finish-output stream)
+      (check (string= "hello       world"
+                      (flexi-streams:octets-to-string
+                       (flexi-streams::vector-stream-vector binary-stream)
+                       :external-format :iso-8859-1)))
+      (terpri stream)
+      (check (= 0 (flexi-stream-column stream)))
+      (write-sequence "abc" stream)
+      (check (= 3 (flexi-stream-column stream)))
+      (terpri stream)
+      (check (= 0 (flexi-stream-column stream))))))
+
+(defun make-external-format-tests (&key verbose)
+  (with-test-suite ("MAKE-EXTERNAL-FORMAT tests" :show-progress-p (not verbose))
+    (flet ((make-case (real-name &key id name)
+           (list real-name
+                 :id id
+                 :input-names (list name (string-upcase name) (string-downcase name)))))
+      (let ((cases (append '((:utf-8 :id nil
+                                     :input-names (:utf8 :utf-8 "utf8" "utf-8" "UTF8" "UTF-8")))
+                           (loop for (name . real-name) in +name-map+
+                                 unless (member :code-page (list name real-name))
+                                   append (list (make-case real-name :name name)
+                                                (make-case real-name :name real-name)))
+                           (loop for (name . definition) in +shortcut-map+
+                                 for key = (car definition)
+                                 for id = (getf (cdr definition) :id)
+                                 for expected = (or (cdr (assoc key +name-map+)) key)
+                                 collect (make-case expected :id id :name name)))))
+
+        (loop for (expected-name . kwargs) in cases
+              for id = (getf kwargs :id)
+              for input-names = (getf kwargs :input-names)
+              do (loop for name in input-names
+                       for ext-format = (make-external-format name)
+                       do (check (eq (flex:external-format-name ext-format) expected-name))
+                       when id
+                         do (check (= (flex:external-format-id ext-format) id))))))
+
+    (let ((error-cases '("utf-8 " " utf-8" "utf8 " " utf8" "utf89" :utf89 utf89 :code-page nil)))
+      (loop for input-name in error-cases
+            do (with-expected-error (external-format-error)
+                 (make-external-format input-name))))))
+
+(defun run-all-tests (&key verbose)
+  "Runs all tests for FLEXI-STREAMS and returns a true value iff all
+tests succeeded.  VERBOSE is interpreted by the individual test suites
+above."
+  (let ((successp t))
+    (macrolet ((run-test-suite (&body body)
+                 `(unless (progn ,@body)
+                    (setq successp nil))))
+      (run-test-suite (compare-files :verbose verbose))
+      (run-test-suite (string-tests :verbose verbose))
+      (run-test-suite (sequence-tests :verbose verbose))
+      (run-test-suite (error-handling-tests :verbose verbose))
+      (run-test-suite (unread-char-tests :verbose verbose))
+      (run-test-suite (column-tests :verbose verbose))
+      (run-test-suite (make-external-format-tests :verbose verbose))
+      (format t "~2&~:[Some tests failed~;All tests passed~]." successp)
+      successp)))
+            
diff --git a/deps/flexi-streams/test/tilton_ascii_cr.txt b/deps/flexi-streams/test/tilton_ascii_cr.txt
new file mode 100644 (file)
index 0000000..894c20c
--- /dev/null
@@ -0,0 +1 @@
+Programmers who lock onto a design decision and cling to it in the face of\rcontradictory new information -- well, that's almost everyone in my\rexperience, so I better not say what I think of them or people will start\rsaying bad things about me on c.l.l.\r   -- Ken Tilton\r%\rThis reminds me of the NYC cabby who accepted a fare to Chicago. When\rthey got there and could not find the friend who was supposed to pay the\rfare he just laughed and said he should have known.\r   -- Ken Tilton\r%\r>> Actually, I believe that Aikido, Jazz and Lisp are different appearances\r>> of the same thing.\rYes, the Tao. /Everything/ is a different appearance of the tao.\r   -- Ken Tilton\r\r"Ken, I went to the library and read up on Buddhism, and believe me, you\rare no Buddhist."\r   -- Kenny's mom\r%\rThat absolutely terrifies the herd-following, lockstep-marching,\rmainstream-saluting cowards that obediently dash out or online to\rscoop up books on The Latest Thing. They learn and use atrocities like\rJava, C++, XML, and even Python for the security it gives them and\rthen sit there slaving away miserably, tediously, joylously paying off\rmortgages and supporting ungrateful teenagers who despise them, only\rto look out the double-sealed thermo-pane windows of their\rcentral-heated, sound-proofed, dead-bolted, suffocating little nests\rinto the howling gale thinking "what do they know that I do not know?"\rwhen they see us under a lean-to hunched over our laptops to shield\rthem from the rain laughing our asses off as we write great code\rbetween bong hits.... what was the question?\r   -- Ken Tilton\r%\rShut up! (That last phrase has four or more syllables if pronounced as\rintended.)\r   -- Ken Tilton\r%\rNonsense. You'll be using it for the GUI, not protein-folding.\r   -- Ken Tilton\r            (responding to a comment that LTK was slow because it\r             was based on TK)\r%\rContinuations certainly are clever, but if we learned anything from the\rrejection of the cover art for "Smell the Glove", it is that "there is a\rfine line between stupid... and clever".\r   -- Ken Tilton\r%\rAh, there's no place like academia for dispassionate, intellectually\rhonest discussion of new ideas on their merits. Thank god for tenure\rgiving your bold antagonist the protection they needed to shout down\ryour iconoclastic..... hang on...\r   -- Ken Tilton\r%\rWhoever objected must be in my killfile, ...\r   -- Ken Tilton\r%\rFrom memory (but I think I have it right):\r\r"But Jesus said, Suffer captured variables, and forbid them not, to come\runto thine macro bodies: for of such is are DSLs made."\r   -- Ken Tilton\r\rCan I get an Amen?\r%\rAwareness of defect is the first step to recovery.\r   -- Ken Tilton\r%\rYou made a bad analogy (there are no good ones, but you found a new\rlow) ...\r   -- Ken Tilton\r%\rYes, it is true that Kent Pitman was raised by a closet full of Lisp\rMachines, but the exception only proves the rule.\r   -- Ken Tilton\r           (in a postscript after positing that computer\r            languages are not learned in infancy)\r%\rI suggest you try bartender's school to support yourself, start\rprogramming for fun again.\r   -- Ken Tilton\r           (responding to a comment that 98% of anything to do\r            with computers was not interesting code)\r%\rYou could add four lanes to my carpal tunnel and I still could not\rwrite all the code I am dying to write.\r   -- Ken Tilton\r%\rNeutrality? I want to bury other languages, not have a gateway to them.\r   -- Ken Tilton\r%\rKen: "Cute puppy. Did you get it for companionship or to pick up chicks?"\rSimon: "Hunh? My puppy /always/ gives me companionship."\r   -- Ken Tilton\r           (on how he was understood by a native english speaker)\r%\r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/tilton_ascii_crlf.txt b/deps/flexi-streams/test/tilton_ascii_crlf.txt
new file mode 100644 (file)
index 0000000..1905479
--- /dev/null
@@ -0,0 +1,96 @@
+Programmers who lock onto a design decision and cling to it in the face of\r
+contradictory new information -- well, that's almost everyone in my\r
+experience, so I better not say what I think of them or people will start\r
+saying bad things about me on c.l.l.\r
+   -- Ken Tilton\r
+%\r
+This reminds me of the NYC cabby who accepted a fare to Chicago. When\r
+they got there and could not find the friend who was supposed to pay the\r
+fare he just laughed and said he should have known.\r
+   -- Ken Tilton\r
+%\r
+>> Actually, I believe that Aikido, Jazz and Lisp are different appearances\r
+>> of the same thing.\r
+Yes, the Tao. /Everything/ is a different appearance of the tao.\r
+   -- Ken Tilton\r
+\r
+"Ken, I went to the library and read up on Buddhism, and believe me, you\r
+are no Buddhist."\r
+   -- Kenny's mom\r
+%\r
+That absolutely terrifies the herd-following, lockstep-marching,\r
+mainstream-saluting cowards that obediently dash out or online to\r
+scoop up books on The Latest Thing. They learn and use atrocities like\r
+Java, C++, XML, and even Python for the security it gives them and\r
+then sit there slaving away miserably, tediously, joylously paying off\r
+mortgages and supporting ungrateful teenagers who despise them, only\r
+to look out the double-sealed thermo-pane windows of their\r
+central-heated, sound-proofed, dead-bolted, suffocating little nests\r
+into the howling gale thinking "what do they know that I do not know?"\r
+when they see us under a lean-to hunched over our laptops to shield\r
+them from the rain laughing our asses off as we write great code\r
+between bong hits.... what was the question?\r
+   -- Ken Tilton\r
+%\r
+Shut up! (That last phrase has four or more syllables if pronounced as\r
+intended.)\r
+   -- Ken Tilton\r
+%\r
+Nonsense. You'll be using it for the GUI, not protein-folding.\r
+   -- Ken Tilton\r
+            (responding to a comment that LTK was slow because it\r
+             was based on TK)\r
+%\r
+Continuations certainly are clever, but if we learned anything from the\r
+rejection of the cover art for "Smell the Glove", it is that "there is a\r
+fine line between stupid... and clever".\r
+   -- Ken Tilton\r
+%\r
+Ah, there's no place like academia for dispassionate, intellectually\r
+honest discussion of new ideas on their merits. Thank god for tenure\r
+giving your bold antagonist the protection they needed to shout down\r
+your iconoclastic..... hang on...\r
+   -- Ken Tilton\r
+%\r
+Whoever objected must be in my killfile, ...\r
+   -- Ken Tilton\r
+%\r
+From memory (but I think I have it right):\r
+\r
+"But Jesus said, Suffer captured variables, and forbid them not, to come\r
+unto thine macro bodies: for of such is are DSLs made."\r
+   -- Ken Tilton\r
+\r
+Can I get an Amen?\r
+%\r
+Awareness of defect is the first step to recovery.\r
+   -- Ken Tilton\r
+%\r
+You made a bad analogy (there are no good ones, but you found a new\r
+low) ...\r
+   -- Ken Tilton\r
+%\r
+Yes, it is true that Kent Pitman was raised by a closet full of Lisp\r
+Machines, but the exception only proves the rule.\r
+   -- Ken Tilton\r
+           (in a postscript after positing that computer\r
+            languages are not learned in infancy)\r
+%\r
+I suggest you try bartender's school to support yourself, start\r
+programming for fun again.\r
+   -- Ken Tilton\r
+           (responding to a comment that 98% of anything to do\r
+            with computers was not interesting code)\r
+%\r
+You could add four lanes to my carpal tunnel and I still could not\r
+write all the code I am dying to write.\r
+   -- Ken Tilton\r
+%\r
+Neutrality? I want to bury other languages, not have a gateway to them.\r
+   -- Ken Tilton\r
+%\r
+Ken: "Cute puppy. Did you get it for companionship or to pick up chicks?"\r
+Simon: "Hunh? My puppy /always/ gives me companionship."\r
+   -- Ken Tilton\r
+           (on how he was understood by a native english speaker)\r
+%\r
diff --git a/deps/flexi-streams/test/tilton_ascii_lf.txt b/deps/flexi-streams/test/tilton_ascii_lf.txt
new file mode 100644 (file)
index 0000000..386c698
--- /dev/null
@@ -0,0 +1,96 @@
+Programmers who lock onto a design decision and cling to it in the face of
+contradictory new information -- well, that's almost everyone in my
+experience, so I better not say what I think of them or people will start
+saying bad things about me on c.l.l.
+   -- Ken Tilton
+%
+This reminds me of the NYC cabby who accepted a fare to Chicago. When
+they got there and could not find the friend who was supposed to pay the
+fare he just laughed and said he should have known.
+   -- Ken Tilton
+%
+>> Actually, I believe that Aikido, Jazz and Lisp are different appearances
+>> of the same thing.
+Yes, the Tao. /Everything/ is a different appearance of the tao.
+   -- Ken Tilton
+
+"Ken, I went to the library and read up on Buddhism, and believe me, you
+are no Buddhist."
+   -- Kenny's mom
+%
+That absolutely terrifies the herd-following, lockstep-marching,
+mainstream-saluting cowards that obediently dash out or online to
+scoop up books on The Latest Thing. They learn and use atrocities like
+Java, C++, XML, and even Python for the security it gives them and
+then sit there slaving away miserably, tediously, joylously paying off
+mortgages and supporting ungrateful teenagers who despise them, only
+to look out the double-sealed thermo-pane windows of their
+central-heated, sound-proofed, dead-bolted, suffocating little nests
+into the howling gale thinking "what do they know that I do not know?"
+when they see us under a lean-to hunched over our laptops to shield
+them from the rain laughing our asses off as we write great code
+between bong hits.... what was the question?
+   -- Ken Tilton
+%
+Shut up! (That last phrase has four or more syllables if pronounced as
+intended.)
+   -- Ken Tilton
+%
+Nonsense. You'll be using it for the GUI, not protein-folding.
+   -- Ken Tilton
+            (responding to a comment that LTK was slow because it
+             was based on TK)
+%
+Continuations certainly are clever, but if we learned anything from the
+rejection of the cover art for "Smell the Glove", it is that "there is a
+fine line between stupid... and clever".
+   -- Ken Tilton
+%
+Ah, there's no place like academia for dispassionate, intellectually
+honest discussion of new ideas on their merits. Thank god for tenure
+giving your bold antagonist the protection they needed to shout down
+your iconoclastic..... hang on...
+   -- Ken Tilton
+%
+Whoever objected must be in my killfile, ...
+   -- Ken Tilton
+%
+From memory (but I think I have it right):
+
+"But Jesus said, Suffer captured variables, and forbid them not, to come
+unto thine macro bodies: for of such is are DSLs made."
+   -- Ken Tilton
+
+Can I get an Amen?
+%
+Awareness of defect is the first step to recovery.
+   -- Ken Tilton
+%
+You made a bad analogy (there are no good ones, but you found a new
+low) ...
+   -- Ken Tilton
+%
+Yes, it is true that Kent Pitman was raised by a closet full of Lisp
+Machines, but the exception only proves the rule.
+   -- Ken Tilton
+           (in a postscript after positing that computer
+            languages are not learned in infancy)
+%
+I suggest you try bartender's school to support yourself, start
+programming for fun again.
+   -- Ken Tilton
+           (responding to a comment that 98% of anything to do
+            with computers was not interesting code)
+%
+You could add four lanes to my carpal tunnel and I still could not
+write all the code I am dying to write.
+   -- Ken Tilton
+%
+Neutrality? I want to bury other languages, not have a gateway to them.
+   -- Ken Tilton
+%
+Ken: "Cute puppy. Did you get it for companionship or to pick up chicks?"
+Simon: "Hunh? My puppy /always/ gives me companionship."
+   -- Ken Tilton
+           (on how he was understood by a native english speaker)
+%
diff --git a/deps/flexi-streams/test/tilton_utf8_cr.txt b/deps/flexi-streams/test/tilton_utf8_cr.txt
new file mode 100644 (file)
index 0000000..894c20c
--- /dev/null
@@ -0,0 +1 @@
+Programmers who lock onto a design decision and cling to it in the face of\rcontradictory new information -- well, that's almost everyone in my\rexperience, so I better not say what I think of them or people will start\rsaying bad things about me on c.l.l.\r   -- Ken Tilton\r%\rThis reminds me of the NYC cabby who accepted a fare to Chicago. When\rthey got there and could not find the friend who was supposed to pay the\rfare he just laughed and said he should have known.\r   -- Ken Tilton\r%\r>> Actually, I believe that Aikido, Jazz and Lisp are different appearances\r>> of the same thing.\rYes, the Tao. /Everything/ is a different appearance of the tao.\r   -- Ken Tilton\r\r"Ken, I went to the library and read up on Buddhism, and believe me, you\rare no Buddhist."\r   -- Kenny's mom\r%\rThat absolutely terrifies the herd-following, lockstep-marching,\rmainstream-saluting cowards that obediently dash out or online to\rscoop up books on The Latest Thing. They learn and use atrocities like\rJava, C++, XML, and even Python for the security it gives them and\rthen sit there slaving away miserably, tediously, joylously paying off\rmortgages and supporting ungrateful teenagers who despise them, only\rto look out the double-sealed thermo-pane windows of their\rcentral-heated, sound-proofed, dead-bolted, suffocating little nests\rinto the howling gale thinking "what do they know that I do not know?"\rwhen they see us under a lean-to hunched over our laptops to shield\rthem from the rain laughing our asses off as we write great code\rbetween bong hits.... what was the question?\r   -- Ken Tilton\r%\rShut up! (That last phrase has four or more syllables if pronounced as\rintended.)\r   -- Ken Tilton\r%\rNonsense. You'll be using it for the GUI, not protein-folding.\r   -- Ken Tilton\r            (responding to a comment that LTK was slow because it\r             was based on TK)\r%\rContinuations certainly are clever, but if we learned anything from the\rrejection of the cover art for "Smell the Glove", it is that "there is a\rfine line between stupid... and clever".\r   -- Ken Tilton\r%\rAh, there's no place like academia for dispassionate, intellectually\rhonest discussion of new ideas on their merits. Thank god for tenure\rgiving your bold antagonist the protection they needed to shout down\ryour iconoclastic..... hang on...\r   -- Ken Tilton\r%\rWhoever objected must be in my killfile, ...\r   -- Ken Tilton\r%\rFrom memory (but I think I have it right):\r\r"But Jesus said, Suffer captured variables, and forbid them not, to come\runto thine macro bodies: for of such is are DSLs made."\r   -- Ken Tilton\r\rCan I get an Amen?\r%\rAwareness of defect is the first step to recovery.\r   -- Ken Tilton\r%\rYou made a bad analogy (there are no good ones, but you found a new\rlow) ...\r   -- Ken Tilton\r%\rYes, it is true that Kent Pitman was raised by a closet full of Lisp\rMachines, but the exception only proves the rule.\r   -- Ken Tilton\r           (in a postscript after positing that computer\r            languages are not learned in infancy)\r%\rI suggest you try bartender's school to support yourself, start\rprogramming for fun again.\r   -- Ken Tilton\r           (responding to a comment that 98% of anything to do\r            with computers was not interesting code)\r%\rYou could add four lanes to my carpal tunnel and I still could not\rwrite all the code I am dying to write.\r   -- Ken Tilton\r%\rNeutrality? I want to bury other languages, not have a gateway to them.\r   -- Ken Tilton\r%\rKen: "Cute puppy. Did you get it for companionship or to pick up chicks?"\rSimon: "Hunh? My puppy /always/ gives me companionship."\r   -- Ken Tilton\r           (on how he was understood by a native english speaker)\r%\r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/tilton_utf8_crlf.txt b/deps/flexi-streams/test/tilton_utf8_crlf.txt
new file mode 100644 (file)
index 0000000..1905479
--- /dev/null
@@ -0,0 +1,96 @@
+Programmers who lock onto a design decision and cling to it in the face of\r
+contradictory new information -- well, that's almost everyone in my\r
+experience, so I better not say what I think of them or people will start\r
+saying bad things about me on c.l.l.\r
+   -- Ken Tilton\r
+%\r
+This reminds me of the NYC cabby who accepted a fare to Chicago. When\r
+they got there and could not find the friend who was supposed to pay the\r
+fare he just laughed and said he should have known.\r
+   -- Ken Tilton\r
+%\r
+>> Actually, I believe that Aikido, Jazz and Lisp are different appearances\r
+>> of the same thing.\r
+Yes, the Tao. /Everything/ is a different appearance of the tao.\r
+   -- Ken Tilton\r
+\r
+"Ken, I went to the library and read up on Buddhism, and believe me, you\r
+are no Buddhist."\r
+   -- Kenny's mom\r
+%\r
+That absolutely terrifies the herd-following, lockstep-marching,\r
+mainstream-saluting cowards that obediently dash out or online to\r
+scoop up books on The Latest Thing. They learn and use atrocities like\r
+Java, C++, XML, and even Python for the security it gives them and\r
+then sit there slaving away miserably, tediously, joylously paying off\r
+mortgages and supporting ungrateful teenagers who despise them, only\r
+to look out the double-sealed thermo-pane windows of their\r
+central-heated, sound-proofed, dead-bolted, suffocating little nests\r
+into the howling gale thinking "what do they know that I do not know?"\r
+when they see us under a lean-to hunched over our laptops to shield\r
+them from the rain laughing our asses off as we write great code\r
+between bong hits.... what was the question?\r
+   -- Ken Tilton\r
+%\r
+Shut up! (That last phrase has four or more syllables if pronounced as\r
+intended.)\r
+   -- Ken Tilton\r
+%\r
+Nonsense. You'll be using it for the GUI, not protein-folding.\r
+   -- Ken Tilton\r
+            (responding to a comment that LTK was slow because it\r
+             was based on TK)\r
+%\r
+Continuations certainly are clever, but if we learned anything from the\r
+rejection of the cover art for "Smell the Glove", it is that "there is a\r
+fine line between stupid... and clever".\r
+   -- Ken Tilton\r
+%\r
+Ah, there's no place like academia for dispassionate, intellectually\r
+honest discussion of new ideas on their merits. Thank god for tenure\r
+giving your bold antagonist the protection they needed to shout down\r
+your iconoclastic..... hang on...\r
+   -- Ken Tilton\r
+%\r
+Whoever objected must be in my killfile, ...\r
+   -- Ken Tilton\r
+%\r
+From memory (but I think I have it right):\r
+\r
+"But Jesus said, Suffer captured variables, and forbid them not, to come\r
+unto thine macro bodies: for of such is are DSLs made."\r
+   -- Ken Tilton\r
+\r
+Can I get an Amen?\r
+%\r
+Awareness of defect is the first step to recovery.\r
+   -- Ken Tilton\r
+%\r
+You made a bad analogy (there are no good ones, but you found a new\r
+low) ...\r
+   -- Ken Tilton\r
+%\r
+Yes, it is true that Kent Pitman was raised by a closet full of Lisp\r
+Machines, but the exception only proves the rule.\r
+   -- Ken Tilton\r
+           (in a postscript after positing that computer\r
+            languages are not learned in infancy)\r
+%\r
+I suggest you try bartender's school to support yourself, start\r
+programming for fun again.\r
+   -- Ken Tilton\r
+           (responding to a comment that 98% of anything to do\r
+            with computers was not interesting code)\r
+%\r
+You could add four lanes to my carpal tunnel and I still could not\r
+write all the code I am dying to write.\r
+   -- Ken Tilton\r
+%\r
+Neutrality? I want to bury other languages, not have a gateway to them.\r
+   -- Ken Tilton\r
+%\r
+Ken: "Cute puppy. Did you get it for companionship or to pick up chicks?"\r
+Simon: "Hunh? My puppy /always/ gives me companionship."\r
+   -- Ken Tilton\r
+           (on how he was understood by a native english speaker)\r
+%\r
diff --git a/deps/flexi-streams/test/tilton_utf8_lf.txt b/deps/flexi-streams/test/tilton_utf8_lf.txt
new file mode 100644 (file)
index 0000000..386c698
--- /dev/null
@@ -0,0 +1,96 @@
+Programmers who lock onto a design decision and cling to it in the face of
+contradictory new information -- well, that's almost everyone in my
+experience, so I better not say what I think of them or people will start
+saying bad things about me on c.l.l.
+   -- Ken Tilton
+%
+This reminds me of the NYC cabby who accepted a fare to Chicago. When
+they got there and could not find the friend who was supposed to pay the
+fare he just laughed and said he should have known.
+   -- Ken Tilton
+%
+>> Actually, I believe that Aikido, Jazz and Lisp are different appearances
+>> of the same thing.
+Yes, the Tao. /Everything/ is a different appearance of the tao.
+   -- Ken Tilton
+
+"Ken, I went to the library and read up on Buddhism, and believe me, you
+are no Buddhist."
+   -- Kenny's mom
+%
+That absolutely terrifies the herd-following, lockstep-marching,
+mainstream-saluting cowards that obediently dash out or online to
+scoop up books on The Latest Thing. They learn and use atrocities like
+Java, C++, XML, and even Python for the security it gives them and
+then sit there slaving away miserably, tediously, joylously paying off
+mortgages and supporting ungrateful teenagers who despise them, only
+to look out the double-sealed thermo-pane windows of their
+central-heated, sound-proofed, dead-bolted, suffocating little nests
+into the howling gale thinking "what do they know that I do not know?"
+when they see us under a lean-to hunched over our laptops to shield
+them from the rain laughing our asses off as we write great code
+between bong hits.... what was the question?
+   -- Ken Tilton
+%
+Shut up! (That last phrase has four or more syllables if pronounced as
+intended.)
+   -- Ken Tilton
+%
+Nonsense. You'll be using it for the GUI, not protein-folding.
+   -- Ken Tilton
+            (responding to a comment that LTK was slow because it
+             was based on TK)
+%
+Continuations certainly are clever, but if we learned anything from the
+rejection of the cover art for "Smell the Glove", it is that "there is a
+fine line between stupid... and clever".
+   -- Ken Tilton
+%
+Ah, there's no place like academia for dispassionate, intellectually
+honest discussion of new ideas on their merits. Thank god for tenure
+giving your bold antagonist the protection they needed to shout down
+your iconoclastic..... hang on...
+   -- Ken Tilton
+%
+Whoever objected must be in my killfile, ...
+   -- Ken Tilton
+%
+From memory (but I think I have it right):
+
+"But Jesus said, Suffer captured variables, and forbid them not, to come
+unto thine macro bodies: for of such is are DSLs made."
+   -- Ken Tilton
+
+Can I get an Amen?
+%
+Awareness of defect is the first step to recovery.
+   -- Ken Tilton
+%
+You made a bad analogy (there are no good ones, but you found a new
+low) ...
+   -- Ken Tilton
+%
+Yes, it is true that Kent Pitman was raised by a closet full of Lisp
+Machines, but the exception only proves the rule.
+   -- Ken Tilton
+           (in a postscript after positing that computer
+            languages are not learned in infancy)
+%
+I suggest you try bartender's school to support yourself, start
+programming for fun again.
+   -- Ken Tilton
+           (responding to a comment that 98% of anything to do
+            with computers was not interesting code)
+%
+You could add four lanes to my carpal tunnel and I still could not
+write all the code I am dying to write.
+   -- Ken Tilton
+%
+Neutrality? I want to bury other languages, not have a gateway to them.
+   -- Ken Tilton
+%
+Ken: "Cute puppy. Did you get it for companionship or to pick up chicks?"
+Simon: "Hunh? My puppy /always/ gives me companionship."
+   -- Ken Tilton
+           (on how he was understood by a native english speaker)
+%
diff --git a/deps/flexi-streams/test/unicode_demo_ucs2_cr_be.txt b/deps/flexi-streams/test/unicode_demo_ucs2_cr_be.txt
new file mode 100644 (file)
index 0000000..81d95bd
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs2_cr_be.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs2_cr_le.txt b/deps/flexi-streams/test/unicode_demo_ucs2_cr_le.txt
new file mode 100644 (file)
index 0000000..005dc2f
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs2_cr_le.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs2_crlf_be.txt b/deps/flexi-streams/test/unicode_demo_ucs2_crlf_be.txt
new file mode 100644 (file)
index 0000000..96dac59
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs2_crlf_be.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs2_crlf_le.txt b/deps/flexi-streams/test/unicode_demo_ucs2_crlf_le.txt
new file mode 100644 (file)
index 0000000..849caea
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs2_crlf_le.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs2_lf_be.txt b/deps/flexi-streams/test/unicode_demo_ucs2_lf_be.txt
new file mode 100644 (file)
index 0000000..b7e494c
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs2_lf_be.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs2_lf_le.txt b/deps/flexi-streams/test/unicode_demo_ucs2_lf_le.txt
new file mode 100644 (file)
index 0000000..30fe7b0
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs2_lf_le.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs4_cr_be.txt b/deps/flexi-streams/test/unicode_demo_ucs4_cr_be.txt
new file mode 100644 (file)
index 0000000..f13d401
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs4_cr_be.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs4_cr_le.txt b/deps/flexi-streams/test/unicode_demo_ucs4_cr_le.txt
new file mode 100644 (file)
index 0000000..da86f6b
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs4_cr_le.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs4_crlf_be.txt b/deps/flexi-streams/test/unicode_demo_ucs4_crlf_be.txt
new file mode 100644 (file)
index 0000000..f645b12
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs4_crlf_be.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs4_crlf_le.txt b/deps/flexi-streams/test/unicode_demo_ucs4_crlf_le.txt
new file mode 100644 (file)
index 0000000..966a4fe
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs4_crlf_le.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs4_lf_be.txt b/deps/flexi-streams/test/unicode_demo_ucs4_lf_be.txt
new file mode 100644 (file)
index 0000000..a1fd543
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs4_lf_be.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_ucs4_lf_le.txt b/deps/flexi-streams/test/unicode_demo_ucs4_lf_le.txt
new file mode 100644 (file)
index 0000000..043afc9
Binary files /dev/null and b/deps/flexi-streams/test/unicode_demo_ucs4_lf_le.txt differ
diff --git a/deps/flexi-streams/test/unicode_demo_utf8_cr.txt b/deps/flexi-streams/test/unicode_demo_utf8_cr.txt
new file mode 100644 (file)
index 0000000..5c3390c
--- /dev/null
@@ -0,0 +1 @@
+\rUTF-8 encoded sample plain-text file\r‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\r\rMarkus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25\r\r\rThe ASCII compatible UTF-8 encoding used in this plain-text file\ris defined in Unicode, ISO 10646-1, and RFC 2279.\r\r\rUsing Unicode/UTF-8, you can write in emails and source code things such as\r\rMathematics and sciences:\r\r  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i),      ⎧⎡⎛┌─────┐⎞⎤⎫\r                                            ⎪⎢⎜│a²+b³ ⎟⎥⎪\r  ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),    ⎪⎢⎜│───── ⎟⎥⎪\r                                            ⎪⎢⎜⎷ c₈   ⎟⎥⎪\r  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ,                   ⎨⎢⎜       ⎟⎥⎬\r                                            ⎪⎢⎜ ∞     ⎟⎥⎪\r  ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫),      ⎪⎢⎜ ⎲     ⎟⎥⎪\r                                            ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪\r  2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm     ⎩⎣⎝i=1    ⎠⎦⎭\r\rLinguistics and dictionaries:\r\r  ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn\r  Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]\r\rAPL:\r\r  ((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈\r\rNicer typography in plain text files:\r\r  ╔══════════════════════════════════════════╗\r  ║                                          ║\r  ║   • ‘single’ and “double” quotes         ║\r  ║                                          ║\r  ║   • Curly apostrophes: “We’ve been here” ║\r  ║                                          ║\r  ║   • Latin-1 apostrophe and accents: '´`  ║\r  ║                                          ║\r  ║   • ‚deutsche‘ „Anführungszeichen“       ║\r  ║                                          ║\r  ║   • †, ‡, ‰, •, 3–4, —, −5/+5, ™, …      ║\r  ║                                          ║\r  ║   • ASCII safety test: 1lI|, 0OD, 8B     ║\r  ║                      ╭─────────╮         ║\r  ║   • the euro symbol: │ 14.95 € │         ║\r  ║                      ╰─────────╯         ║\r  ╚══════════════════════════════════════════╝\r\rCombining characters:\r\r  STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑\r\rGreek (in Polytonic):\r\r  The Greek anthem:\r\r  Σὲ γνωρίζω ἀπὸ τὴν κόψη\r  τοῦ σπαθιοῦ τὴν τρομερή,\r  σὲ γνωρίζω ἀπὸ τὴν ὄψη\r  ποὺ μὲ βία μετράει τὴ γῆ.\r\r  ᾿Απ᾿ τὰ κόκκαλα βγαλμένη\r  τῶν ῾Ελλήνων τὰ ἱερά\r  καὶ σὰν πρῶτα ἀνδρειωμένη\r  χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!\r\r  From a speech of Demosthenes in the 4th century BC:\r\r  Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,\r  ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς\r  λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ\r  τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿\r  εἰς τοῦτο προήκοντα,  ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ\r  πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν\r  οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,\r  οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν\r  ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον\r  τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι\r  γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν\r  προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους\r  σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ\r  τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ\r  τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς\r  τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.\r\r  Δημοσθένους, Γ´ ᾿Ολυνθιακὸς\r\rGeorgian:\r\r  From a Unicode conference invitation:\r\r  გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო\r  კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,\r  ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს\r  ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,\r  ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება\r  ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,\r  ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.\r\rRussian:\r\r  From a Unicode conference invitation:\r\r  Зарегистрируйтесь сейчас на Десятую Международную Конференцию по\r  Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.\r  Конференция соберет широкий круг экспертов по  вопросам глобального\r  Интернета и Unicode, локализации и интернационализации, воплощению и\r  применению Unicode в различных операционных системах и программных\r  приложениях, шрифтах, верстке и многоязычных компьютерных системах.\r\rThai (UCS Level 2):\r\r  Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese\r  classic 'San Gua'):\r\r  [----------------------------|------------------------]\r    ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่\r  สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา\r    ทรงนับถือขันทีเป็นที่พึ่ง           บ้านเมืองจึงวิปริตเป็นนักหนา\r  โฮจิ๋นเรียกทัพทั่วหัวเมืองมา         หมายจะฆ่ามดชั่วตัวสำคัญ\r    เหมือนขับไสไล่เสือจากเคหา      รับหมาป่าเข้ามาเลยอาสัญ\r  ฝ่ายอ้องอุ้นยุแยกให้แตกกัน          ใช้สาวนั้นเป็นชนวนชื่นชวนใจ\r    พลันลิฉุยกุยกีกลับก่อเหตุ          ช่างอาเพศจริงหนาฟ้าร้องไห้\r  ต้องรบราฆ่าฟันจนบรรลัย           ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ\r\r  (The above is a two-column text. If combining characters are handled\r  correctly, the lines of the second column should be aligned with the\r  | character above.)\r\rEthiopian:\r\r  Proverbs in the Amharic language:\r\r  ሰማይ አይታረስ ንጉሥ አይከሰስ።\r  ብላ ካለኝ እንደአባቴ በቆመጠኝ።\r  ጌጥ ያለቤቱ ቁምጥና ነው።\r  ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።\r  የአፍ ወለምታ በቅቤ አይታሽም።\r  አይጥ በበላ ዳዋ ተመታ።\r  ሲተረጉሙ ይደረግሙ።\r  ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።\r  ድር ቢያብር አንበሳ ያስር።\r  ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።\r  እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።\r  የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።\r  ሥራ ከመፍታት ልጄን ላፋታት።\r  ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።\r  የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።\r  ተንጋሎ ቢተፉ ተመልሶ ባፉ።\r  ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።\r  እግርህን በፍራሽህ ልክ ዘርጋ።\r\rRunes:\r\r  ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ\r\r  (Old English, which transcribed into Latin reads 'He cwaeth that he\r  bude thaem lande northweardum with tha Westsae.' and means 'He said\r  that he lived in the northern land near the Western Sea.')\r\rBraille:\r\r  ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌\r\r  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞\r  ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎\r  ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂\r  ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙\r  ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑\r  ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲\r\r  ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲\r\r  ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹\r  ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞\r  ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕\r  ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹\r  ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎\r  ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎\r  ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳\r  ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞\r  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲\r\r  (The first couple of paragraphs of "A Christmas Carol" by Dickens)\r\rCompact font selection example text:\r\r  ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789\r  abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ\r  –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд\r  ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა\r\rGreetings in various languages:\r\r  Hello world, Καλημέρα κόσμε, コンニチハ\r\rBox drawing alignment tests:                                          █\r                                                                      ▉\r  ╔══╦══╗  ┌──┬──┐  ╭──┬──╮  ╭──┬──╮  ┏━━┳━━┓  ┎┒┏┑   ╷  ╻ ┏┯┓ ┌┰┐    ▊ ╱╲╱╲╳╳╳\r  ║┌─╨─┐║  │╔═╧═╗│  │╒═╪═╕│  │╓─╁─╖│  ┃┌─╂─┐┃  ┗╃╄┙  ╶┼╴╺╋╸┠┼┨ ┝╋┥    ▋ ╲╱╲╱╳╳╳\r  ║│╲ ╱│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╿ │┃  ┍╅╆┓   ╵  ╹ ┗┷┛ └┸┘    ▌ ╱╲╱╲╳╳╳\r  ╠╡ ╳ ╞╣  ├╢   ╟┤  ├┼─┼─┼┤  ├╫─╂─╫┤  ┣┿╾┼╼┿┫  ┕┛┖┚     ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳\r  ║│╱ ╲│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╽ │┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▎\r  ║└─╥─┘║  │╚═╤═╝│  │╘═╪═╛│  │╙─╀─╜│  ┃└─╂─┘┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▏\r  ╚══╩══╝  └──┴──┘  ╰──┴──╯  ╰──┴──╯  ┗━━┻━━┛  ▗▄▖▛▀▜   └╌╌┘ ╎ ┗╍╍┛ ┋  ▁▂▃▄▅▆▇█\r                                               ▝▀▘▙▄▟\r
\ No newline at end of file
diff --git a/deps/flexi-streams/test/unicode_demo_utf8_crlf.txt b/deps/flexi-streams/test/unicode_demo_utf8_crlf.txt
new file mode 100644 (file)
index 0000000..d475c08
--- /dev/null
@@ -0,0 +1,212 @@
+\r
+UTF-8 encoded sample plain-text file\r
+‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\r
+\r
+Markus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25\r
+\r
+\r
+The ASCII compatible UTF-8 encoding used in this plain-text file\r
+is defined in Unicode, ISO 10646-1, and RFC 2279.\r
+\r
+\r
+Using Unicode/UTF-8, you can write in emails and source code things such as\r
+\r
+Mathematics and sciences:\r
+\r
+  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i),      ⎧⎡⎛┌─────┐⎞⎤⎫\r
+                                            ⎪⎢⎜│a²+b³ ⎟⎥⎪\r
+  ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),    ⎪⎢⎜│───── ⎟⎥⎪\r
+                                            ⎪⎢⎜⎷ c₈   ⎟⎥⎪\r
+  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ,                   ⎨⎢⎜       ⎟⎥⎬\r
+                                            ⎪⎢⎜ ∞     ⎟⎥⎪\r
+  ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫),      ⎪⎢⎜ ⎲     ⎟⎥⎪\r
+                                            ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪\r
+  2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm     ⎩⎣⎝i=1    ⎠⎦⎭\r
+\r
+Linguistics and dictionaries:\r
+\r
+  ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn\r
+  Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]\r
+\r
+APL:\r
+\r
+  ((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈\r
+\r
+Nicer typography in plain text files:\r
+\r
+  ╔══════════════════════════════════════════╗\r
+  ║                                          ║\r
+  ║   • ‘single’ and “double” quotes         ║\r
+  ║                                          ║\r
+  ║   • Curly apostrophes: “We’ve been here” ║\r
+  ║                                          ║\r
+  ║   • Latin-1 apostrophe and accents: '´`  ║\r
+  ║                                          ║\r
+  ║   • ‚deutsche‘ „Anführungszeichen“       ║\r
+  ║                                          ║\r
+  ║   • †, ‡, ‰, •, 3–4, —, −5/+5, ™, …      ║\r
+  ║                                          ║\r
+  ║   • ASCII safety test: 1lI|, 0OD, 8B     ║\r
+  ║                      ╭─────────╮         ║\r
+  ║   • the euro symbol: │ 14.95 € │         ║\r
+  ║                      ╰─────────╯         ║\r
+  ╚══════════════════════════════════════════╝\r
+\r
+Combining characters:\r
+\r
+  STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑\r
+\r
+Greek (in Polytonic):\r
+\r
+  The Greek anthem:\r
+\r
+  Σὲ γνωρίζω ἀπὸ τὴν κόψη\r
+  τοῦ σπαθιοῦ τὴν τρομερή,\r
+  σὲ γνωρίζω ἀπὸ τὴν ὄψη\r
+  ποὺ μὲ βία μετράει τὴ γῆ.\r
+\r
+  ᾿Απ᾿ τὰ κόκκαλα βγαλμένη\r
+  τῶν ῾Ελλήνων τὰ ἱερά\r
+  καὶ σὰν πρῶτα ἀνδρειωμένη\r
+  χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!\r
+\r
+  From a speech of Demosthenes in the 4th century BC:\r
+\r
+  Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,\r
+  ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς\r
+  λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ\r
+  τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿\r
+  εἰς τοῦτο προήκοντα,  ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ\r
+  πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν\r
+  οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,\r
+  οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν\r
+  ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον\r
+  τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι\r
+  γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν\r
+  προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους\r
+  σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ\r
+  τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ\r
+  τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς\r
+  τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.\r
+\r
+  Δημοσθένους, Γ´ ᾿Ολυνθιακὸς\r
+\r
+Georgian:\r
+\r
+  From a Unicode conference invitation:\r
+\r
+  გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო\r
+  კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,\r
+  ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს\r
+  ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,\r
+  ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება\r
+  ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,\r
+  ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.\r
+\r
+Russian:\r
+\r
+  From a Unicode conference invitation:\r
+\r
+  Зарегистрируйтесь сейчас на Десятую Международную Конференцию по\r
+  Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.\r
+  Конференция соберет широкий круг экспертов по  вопросам глобального\r
+  Интернета и Unicode, локализации и интернационализации, воплощению и\r
+  применению Unicode в различных операционных системах и программных\r
+  приложениях, шрифтах, верстке и многоязычных компьютерных системах.\r
+\r
+Thai (UCS Level 2):\r
+\r
+  Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese\r
+  classic 'San Gua'):\r
+\r
+  [----------------------------|------------------------]\r
+    ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่\r
+  สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา\r
+    ทรงนับถือขันทีเป็นที่พึ่ง           บ้านเมืองจึงวิปริตเป็นนักหนา\r
+  โฮจิ๋นเรียกทัพทั่วหัวเมืองมา         หมายจะฆ่ามดชั่วตัวสำคัญ\r
+    เหมือนขับไสไล่เสือจากเคหา      รับหมาป่าเข้ามาเลยอาสัญ\r
+  ฝ่ายอ้องอุ้นยุแยกให้แตกกัน          ใช้สาวนั้นเป็นชนวนชื่นชวนใจ\r
+    พลันลิฉุยกุยกีกลับก่อเหตุ          ช่างอาเพศจริงหนาฟ้าร้องไห้\r
+  ต้องรบราฆ่าฟันจนบรรลัย           ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ\r
+\r
+  (The above is a two-column text. If combining characters are handled\r
+  correctly, the lines of the second column should be aligned with the\r
+  | character above.)\r
+\r
+Ethiopian:\r
+\r
+  Proverbs in the Amharic language:\r
+\r
+  ሰማይ አይታረስ ንጉሥ አይከሰስ።\r
+  ብላ ካለኝ እንደአባቴ በቆመጠኝ።\r
+  ጌጥ ያለቤቱ ቁምጥና ነው።\r
+  ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።\r
+  የአፍ ወለምታ በቅቤ አይታሽም።\r
+  አይጥ በበላ ዳዋ ተመታ።\r
+  ሲተረጉሙ ይደረግሙ።\r
+  ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።\r
+  ድር ቢያብር አንበሳ ያስር።\r
+  ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።\r
+  እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።\r
+  የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።\r
+  ሥራ ከመፍታት ልጄን ላፋታት።\r
+  ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።\r
+  የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።\r
+  ተንጋሎ ቢተፉ ተመልሶ ባፉ።\r
+  ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።\r
+  እግርህን በፍራሽህ ልክ ዘርጋ።\r
+\r
+Runes:\r
+\r
+  ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ\r
+\r
+  (Old English, which transcribed into Latin reads 'He cwaeth that he\r
+  bude thaem lande northweardum with tha Westsae.' and means 'He said\r
+  that he lived in the northern land near the Western Sea.')\r
+\r
+Braille:\r
+\r
+  ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌\r
+\r
+  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞\r
+  ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎\r
+  ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂\r
+  ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙\r
+  ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑\r
+  ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲\r
+\r
+  ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲\r
+\r
+  ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹\r
+  ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞\r
+  ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕\r
+  ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹\r
+  ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎\r
+  ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎\r
+  ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳\r
+  ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞\r
+  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲\r
+\r
+  (The first couple of paragraphs of "A Christmas Carol" by Dickens)\r
+\r
+Compact font selection example text:\r
+\r
+  ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789\r
+  abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ\r
+  –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд\r
+  ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა\r
+\r
+Greetings in various languages:\r
+\r
+  Hello world, Καλημέρα κόσμε, コンニチハ\r
+\r
+Box drawing alignment tests:                                          █\r
+                                                                      ▉\r
+  ╔══╦══╗  ┌──┬──┐  ╭──┬──╮  ╭──┬──╮  ┏━━┳━━┓  ┎┒┏┑   ╷  ╻ ┏┯┓ ┌┰┐    ▊ ╱╲╱╲╳╳╳\r
+  ║┌─╨─┐║  │╔═╧═╗│  │╒═╪═╕│  │╓─╁─╖│  ┃┌─╂─┐┃  ┗╃╄┙  ╶┼╴╺╋╸┠┼┨ ┝╋┥    ▋ ╲╱╲╱╳╳╳\r
+  ║│╲ ╱│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╿ │┃  ┍╅╆┓   ╵  ╹ ┗┷┛ └┸┘    ▌ ╱╲╱╲╳╳╳\r
+  ╠╡ ╳ ╞╣  ├╢   ╟┤  ├┼─┼─┼┤  ├╫─╂─╫┤  ┣┿╾┼╼┿┫  ┕┛┖┚     ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳\r
+  ║│╱ ╲│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╽ │┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▎\r
+  ║└─╥─┘║  │╚═╤═╝│  │╘═╪═╛│  │╙─╀─╜│  ┃└─╂─┘┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▏\r
+  ╚══╩══╝  └──┴──┘  ╰──┴──╯  ╰──┴──╯  ┗━━┻━━┛  ▗▄▖▛▀▜   └╌╌┘ ╎ ┗╍╍┛ ┋  ▁▂▃▄▅▆▇█\r
+                                               ▝▀▘▙▄▟\r
diff --git a/deps/flexi-streams/test/unicode_demo_utf8_lf.txt b/deps/flexi-streams/test/unicode_demo_utf8_lf.txt
new file mode 100644 (file)
index 0000000..4363f27
--- /dev/null
@@ -0,0 +1,212 @@
+
+UTF-8 encoded sample plain-text file
+‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+Markus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25
+
+
+The ASCII compatible UTF-8 encoding used in this plain-text file
+is defined in Unicode, ISO 10646-1, and RFC 2279.
+
+
+Using Unicode/UTF-8, you can write in emails and source code things such as
+
+Mathematics and sciences:
+
+  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i),      ⎧⎡⎛┌─────┐⎞⎤⎫
+                                            ⎪⎢⎜│a²+b³ ⎟⎥⎪
+  ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),    ⎪⎢⎜│───── ⎟⎥⎪
+                                            ⎪⎢⎜⎷ c₈   ⎟⎥⎪
+  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ,                   ⎨⎢⎜       ⎟⎥⎬
+                                            ⎪⎢⎜ ∞     ⎟⎥⎪
+  ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫),      ⎪⎢⎜ ⎲     ⎟⎥⎪
+                                            ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪
+  2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm     ⎩⎣⎝i=1    ⎠⎦⎭
+
+Linguistics and dictionaries:
+
+  ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
+  Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]
+
+APL:
+
+  ((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
+
+Nicer typography in plain text files:
+
+  ╔══════════════════════════════════════════╗
+  ║                                          ║
+  ║   • ‘single’ and “double” quotes         ║
+  ║                                          ║
+  ║   • Curly apostrophes: “We’ve been here” ║
+  ║                                          ║
+  ║   • Latin-1 apostrophe and accents: '´`  ║
+  ║                                          ║
+  ║   • ‚deutsche‘ „Anführungszeichen“       ║
+  ║                                          ║
+  ║   • †, ‡, ‰, •, 3–4, —, −5/+5, ™, …      ║
+  ║                                          ║
+  ║   • ASCII safety test: 1lI|, 0OD, 8B     ║
+  ║                      ╭─────────╮         ║
+  ║   • the euro symbol: │ 14.95 € │         ║
+  ║                      ╰─────────╯         ║
+  ╚══════════════════════════════════════════╝
+
+Combining characters:
+
+  STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑
+
+Greek (in Polytonic):
+
+  The Greek anthem:
+
+  Σὲ γνωρίζω ἀπὸ τὴν κόψη
+  τοῦ σπαθιοῦ τὴν τρομερή,
+  σὲ γνωρίζω ἀπὸ τὴν ὄψη
+  ποὺ μὲ βία μετράει τὴ γῆ.
+
+  ᾿Απ᾿ τὰ κόκκαλα βγαλμένη
+  τῶν ῾Ελλήνων τὰ ἱερά
+  καὶ σὰν πρῶτα ἀνδρειωμένη
+  χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!
+
+  From a speech of Demosthenes in the 4th century BC:
+
+  Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
+  ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
+  λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
+  τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿
+  εἰς τοῦτο προήκοντα,  ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
+  πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
+  οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
+  οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
+  ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
+  τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
+  γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
+  προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
+  σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
+  τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
+  τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
+  τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.
+
+  Δημοσθένους, Γ´ ᾿Ολυνθιακὸς
+
+Georgian:
+
+  From a Unicode conference invitation:
+
+  გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
+  კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
+  ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
+  ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
+  ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
+  ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
+  ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
+
+Russian:
+
+  From a Unicode conference invitation:
+
+  Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
+  Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
+  Конференция соберет широкий круг экспертов по  вопросам глобального
+  Интернета и Unicode, локализации и интернационализации, воплощению и
+  применению Unicode в различных операционных системах и программных
+  приложениях, шрифтах, верстке и многоязычных компьютерных системах.
+
+Thai (UCS Level 2):
+
+  Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
+  classic 'San Gua'):
+
+  [----------------------------|------------------------]
+    ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่
+  สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา
+    ทรงนับถือขันทีเป็นที่พึ่ง           บ้านเมืองจึงวิปริตเป็นนักหนา
+  โฮจิ๋นเรียกทัพทั่วหัวเมืองมา         หมายจะฆ่ามดชั่วตัวสำคัญ
+    เหมือนขับไสไล่เสือจากเคหา      รับหมาป่าเข้ามาเลยอาสัญ
+  ฝ่ายอ้องอุ้นยุแยกให้แตกกัน          ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
+    พลันลิฉุยกุยกีกลับก่อเหตุ          ช่างอาเพศจริงหนาฟ้าร้องไห้
+  ต้องรบราฆ่าฟันจนบรรลัย           ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ
+
+  (The above is a two-column text. If combining characters are handled
+  correctly, the lines of the second column should be aligned with the
+  | character above.)
+
+Ethiopian:
+
+  Proverbs in the Amharic language:
+
+  ሰማይ አይታረስ ንጉሥ አይከሰስ።
+  ብላ ካለኝ እንደአባቴ በቆመጠኝ።
+  ጌጥ ያለቤቱ ቁምጥና ነው።
+  ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
+  የአፍ ወለምታ በቅቤ አይታሽም።
+  አይጥ በበላ ዳዋ ተመታ።
+  ሲተረጉሙ ይደረግሙ።
+  ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
+  ድር ቢያብር አንበሳ ያስር።
+  ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
+  እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
+  የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
+  ሥራ ከመፍታት ልጄን ላፋታት።
+  ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
+  የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
+  ተንጋሎ ቢተፉ ተመልሶ ባፉ።
+  ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
+  እግርህን በፍራሽህ ልክ ዘርጋ።
+
+Runes:
+
+  ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ
+
+  (Old English, which transcribed into Latin reads 'He cwaeth that he
+  bude thaem lande northweardum with tha Westsae.' and means 'He said
+  that he lived in the northern land near the Western Sea.')
+
+Braille:
+
+  ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌
+
+  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
+  ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
+  ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
+  ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
+  ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑
+  ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲
+
+  ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
+
+  ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
+  ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
+  ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
+  ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹
+  ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎
+  ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
+  ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
+  ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
+  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
+
+  (The first couple of paragraphs of "A Christmas Carol" by Dickens)
+
+Compact font selection example text:
+
+  ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
+  abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
+  –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
+  ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა
+
+Greetings in various languages:
+
+  Hello world, Καλημέρα κόσμε, コンニチハ
+
+Box drawing alignment tests:                                          █
+                                                                      ▉
+  ╔══╦══╗  ┌──┬──┐  ╭──┬──╮  ╭──┬──╮  ┏━━┳━━┓  ┎┒┏┑   ╷  ╻ ┏┯┓ ┌┰┐    ▊ ╱╲╱╲╳╳╳
+  ║┌─╨─┐║  │╔═╧═╗│  │╒═╪═╕│  │╓─╁─╖│  ┃┌─╂─┐┃  ┗╃╄┙  ╶┼╴╺╋╸┠┼┨ ┝╋┥    ▋ ╲╱╲╱╳╳╳
+  ║│╲ ╱│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╿ │┃  ┍╅╆┓   ╵  ╹ ┗┷┛ └┸┘    ▌ ╱╲╱╲╳╳╳
+  ╠╡ ╳ ╞╣  ├╢   ╟┤  ├┼─┼─┼┤  ├╫─╂─╫┤  ┣┿╾┼╼┿┫  ┕┛┖┚     ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
+  ║│╱ ╲│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╽ │┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▎
+  ║└─╥─┘║  │╚═╤═╝│  │╘═╪═╛│  │╙─╀─╜│  ┃└─╂─┘┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▏
+  ╚══╩══╝  └──┴──┘  ╰──┴──╯  ╰──┴──╯  ┗━━┻━━┛  ▗▄▖▛▀▜   └╌╌┘ ╎ ┗╍╍┛ ┋  ▁▂▃▄▅▆▇█
+                                               ▝▀▘▙▄▟
diff --git a/deps/flexi-streams/util.lisp b/deps/flexi-streams/util.lisp
new file mode 100644 (file)
index 0000000..85dbde3
--- /dev/null
@@ -0,0 +1,206 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: FLEXI-STREAMS; Base: 10 -*-
+;;; $Header: /usr/local/cvsrep/flexi-streams/util.lisp,v 1.24 2008/05/25 21:26:12 edi Exp $
+
+;;; Copyright (c) 2005-2008, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :flexi-streams)
+
+#+:lispworks
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (import '(lw:with-unique-names lw:when-let)))
+
+#-:lispworks
+(defmacro when-let ((var form) &body body)
+  "Evaluates FORM and binds VAR to the result, then executes BODY
+if VAR has a true value."
+  `(let ((,var ,form))
+     (when ,var ,@body)))
+
+#-:lispworks
+(defmacro with-unique-names ((&rest bindings) &body body)
+  "Syntax: WITH-UNIQUE-NAMES ( { var | (var x) }* ) declaration* form*
+
+Executes a series of forms with each VAR bound to a fresh,
+uninterned symbol. The uninterned symbol is as if returned by a call
+to GENSYM with the string denoted by X - or, if X is not supplied, the
+string denoted by VAR - as argument.
+
+The variable bindings created are lexical unless special declarations
+are specified. The scopes of the name bindings and declarations do not
+include the Xs.
+
+The forms are evaluated in order, and the values of all but the last
+are discarded \(that is, the body is an implicit PROGN)."
+  ;; reference implementation posted to comp.lang.lisp as
+  ;; <cy3bshuf30f.fsf@ljosa.com> by Vebjorn Ljosa - see also
+  ;; <http://www.cliki.net/Common%20Lisp%20Utilities>
+  `(let ,(mapcar #'(lambda (binding)
+                     (check-type binding (or cons symbol))
+                     (if (consp binding)
+                       (destructuring-bind (var x) binding
+                         (check-type var symbol)
+                         `(,var (gensym ,(etypecase x
+                                          (symbol (symbol-name x))
+                                          (character (string x))
+                                          (string x)))))
+                       `(,binding (gensym ,(symbol-name binding)))))
+                 bindings)
+         ,@body))
+
+#+:lispworks
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (setf (macro-function 'with-rebinding)
+          (macro-function 'lw:rebinding)))
+
+#-:lispworks
+(defmacro with-rebinding (bindings &body body)
+  "WITH-REBINDING ( { var | (var prefix) }* ) form*
+
+Evaluates a series of forms in the lexical environment that is
+formed by adding the binding of each VAR to a fresh, uninterned
+symbol, and the binding of that fresh, uninterned symbol to VAR's
+original value, i.e., its value in the current lexical environment.
+
+The uninterned symbol is created as if by a call to GENSYM with the
+string denoted by PREFIX - or, if PREFIX is not supplied, the string
+denoted by VAR - as argument.
+
+The forms are evaluated in order, and the values of all but the last
+are discarded \(that is, the body is an implicit PROGN)."
+  ;; reference implementation posted to comp.lang.lisp as
+  ;; <cy3wv0fya0p.fsf@ljosa.com> by Vebjorn Ljosa - see also
+  ;; <http://www.cliki.net/Common%20Lisp%20Utilities>
+  (loop for binding in bindings
+        for var = (if (consp binding) (car binding) binding)
+        for name = (gensym)
+        collect `(,name ,var) into renames
+        collect ``(,,var ,,name) into temps
+        finally (return `(let ,renames
+                          (with-unique-names ,bindings
+                            `(let (,,@temps)
+                              ,,@body))))))
+
+(defun normalize-external-format-name (name)
+  "Converts NAME \(a symbol) to a `canonical' name for an
+external format, e.g. :LATIN1 will be converted to :ISO-8859-1.
+Also checks if there is an external format with that name and
+signals an error otherwise."
+  (let ((real-name (cdr (find name flex::+name-map+
+                              :test (lambda (item pair)
+                                      (or (string-equal item (cdr pair))
+                                          (string-equal item (car pair))))))))
+    (unless real-name
+      (error 'external-format-error
+             :format-control "~S is not known to be a name for an external format."
+             :format-arguments (list name)))
+    real-name))
+
+(defun ascii-name-p (name)
+  "Checks whether NAME is the keyword :ASCII."
+  (eq name :us-ascii))
+
+(defun koi8-r-name-p (name)
+  "Checks whether NAME is the keyword :KOI8-R."
+  (eq name :koi8-r))
+
+(defun code-page-name-p (name)
+  "Checks whether NAME is the keyword :CODE-PAGE."
+  (eq name :code-page))
+
+(defun iso-8859-name-p (name)
+  "Checks whether NAME \(a keyword) names one of the known
+ISO-8859 encodings."
+  (find name +iso-8859-tables+ :key #'car))
+
+(defun known-code-page-id-p (id)
+  "Checks whether ID \(a number) denotes one of the known Windows
+code pages."
+  (and (find id +code-page-tables+ :key #'car)
+       id))
+
+#+:lispworks
+(defun sans (plist &rest keys)
+  "Returns PLIST with keyword arguments from KEYS removed."
+  (sys::remove-properties plist keys))
+
+#-:lispworks
+(defun sans (plist &rest keys)
+  "Returns PLIST with keyword arguments from KEYS removed."
+  ;; stolen from Usenet posting <3247672165664225@naggum.no> by Erik
+  ;; Naggum
+  (let ((sans ()))
+    (loop
+      (let ((tail (nth-value 2 (get-properties plist keys))))
+        ;; this is how it ends
+        (unless tail
+          (return (nreconc sans plist)))
+        ;; copy all the unmatched keys
+        (loop until (eq plist tail) do
+              (push (pop plist) sans)
+              (push (pop plist) sans))
+        ;; skip the matched key
+        (setq plist (cddr plist))))))
+
+#+:lispworks
+(defmacro with-accessors (slot-entries instance &body body)
+  "For LispWorks, we prefer SLOT-VALUE over accessors for better
+performance."
+  ;; note that we assume that the variables have the same names as the
+  ;; slots
+  `(with-slots ,(mapcar #'car slot-entries)
+       ,instance
+     ,@body))
+
+(defun make-octet-buffer (&optional (size +buffer-size+))
+  "Creates and returns a fresh buffer \(a specialized array) of size
++BUFFER-SIZE+ to hold octets."
+  (declare #.*standard-optimize-settings*)
+  (make-array size :element-type 'octet))
+
+(defun type-equal (type1 type2)
+  "Whether TYPE1 and TYPE2 denote the same type."
+  (declare #.*standard-optimize-settings*)
+  (and (subtypep type1 type2)
+       (subtypep type2 type1)))
+
+(defun maybe-rewind (stream octets)
+  "Tries to `rewind' the \(binary) stream STREAM by OCTETS octets.
+Returns T if it succeeds, otherwise NIL."
+  (when-let (position (file-position stream))
+    (if (file-position stream (- position octets)) t nil)))
+
+(defmacro logand* (x y)
+  "Solely for optimization purposes.  Some Lisps need it, some don't."
+  `(the fixnum (logand ,x ,y)))
+
+(defmacro logior* (x y)
+  "Solely for optimization purposes.  Some Lisps need it, some don't."
+  `(the fixnum (logior ,x ,y)))
+
+(defmacro ash* (integer count)
+  "Solely for optimization purposes.  Some Lisps need it, some don't."
+  `(the fixnum (ash ,integer ,count)))
diff --git a/deps/hunchentoot/.gitignore b/deps/hunchentoot/.gitignore
new file mode 100644 (file)
index 0000000..6896773
--- /dev/null
@@ -0,0 +1,2 @@
+*.*f*sl
+*~
diff --git a/deps/hunchentoot/.pre-release.sh b/deps/hunchentoot/.pre-release.sh
new file mode 100755 (executable)
index 0000000..a4f1c8f
--- /dev/null
@@ -0,0 +1 @@
+cd doc; make
diff --git a/deps/hunchentoot/CHANGELOG b/deps/hunchentoot/CHANGELOG
new file mode 100644 (file)
index 0000000..d8ce7bb
--- /dev/null
@@ -0,0 +1,567 @@
+Version 1.2.38
+2017-12-03
+Better pathname validation.
+A couple of small fixes.
+
+Version 1.2.37
+2016-12-11
+Support listening on random port number (Lucien Pullen)
+
+Version 1.2.36
+2016-12-08
+Compare on the path-string. (matthieupeeters)
+speed up shutdown and avoid timeout on the listening socket (Felix Lange)
+clarify definition of handler function (Robert Smith)
+[doc] fix ID clash in index.xml (Alex Dunn)
+
+Version 1.2.35
+2016-02-10
+Avoid double URL decoding, reported by muyinliu (Hans Huebner)
+Remove a duplicate word in the documentation (William Halliburton)
+Call convert-hack in name also (José Ronquillo Rivera)
+
+Version 1.2.34
+2015-07-06
+decode URL considering content-type's charset (Tim Richardt)
+
+Version 1.2.33
+2015-07-05
+Ignore malformed session IDs (Hans Huebner)
+prevent failures when session cookie is malformed (Andrey Kutejko)
+correct started-p for lispworks (LinkFly)
+
+Version 1.2.32
+2015-05-03
+Adds the session-regenerate-cookie-value function (Florian Margaine)
+Bugfix: using variables within with-accessors (Dmitry Igrishin)
+Added Gitter link (The Gitter Badger)
+Add "charset" property to Content-Type HTTP header (Dmitry Igrishin)
+"Connection: close" on unthreaded builds (Philipp Marek)
+Make the RFC 6585 status constants external (Grim Schjetne)
+Add HTTP status codes described in RFC 6585 (Grim Schjetne)
+Add charset to Content-Type when serving static (Dmitry Igrishin)
+Fix to PROCESS-CONNECTION leaking socket fds (Jussi Lahdenniemi)
+
+Version 1.2.31
+2015-03-06
+Adds HttpOnly and remove cookie on remove-session (Florian MARGAINE)
+
+Version 1.2.30
+2015-02-25
+Clear content-length before emitting 304 (Jason Miller)
+Treat errors during url decoding as bad requests. (Stas Boukarev)
+
+Version 1.2.29
+2014-11-30
+temporarily revert ipv6 changes (Hans Huebner)
+
+Version 1.2.28
+2014-11-28
+Remove dead links and update support information (Hans Huebner)
+restore listening to usocket:*wildcard-host* (Hans Huebner)
+deal with IPv6 addresses from usocket (Hans Huebner)
+eliminate duplicate logging of warnings (reported by loke) (Hans Huebner)
+add DETACH-SOCKET function (Hans Huebner)
+Add the ability to prevent the sockets from being closed after a request has been processed. (Elias Mårtenson)
+document *FINISH-PROCESSING-SOCKET* (Hans Huebner)
+Make check for stream timeouts more robust (Raymond Wiker)
+
+Version 1.2.27
+2014-05-18
+fix warning about missing NAME keyword arg at start-thread (Mark H. David)
+remove tbnl-announce list (Hans Huebner)
+correct speling eror (Hans Huebner)
+Update request.lisp (muyinliu)
+file upload file name encoding error fixed (muyinliu)
+support use of logical file names for document-root and error-template-directory (Hans Huebner)
+Generate www/hunchentoot-doc.html. (Stas Boukarev)
+Remove mentions of asdf-install and the darcs mirror from docs. (Stas Boukarev)
+
+Version 1.2.26
+2014-01-18
+Optimize get-peer/local-address-and-port. (Stas Boukarev)
+Close SSL streams after processing connection. (Stas Boukarev)
+
+Version 1.2.25
+2014-01-17
+allow for handler setting of the "connection" header (William Halliburton)
+
+Version 1.2.24
+2014-01-07
+Use version number from ASDF system definition, not special var (Hans Huebner)
+
+Version 1.2.23
+2014-01-05
+Don't set the Connection header to Close if it's already set (AndrejSuc/Stas Boukarev)
+
+Version 1.2.22
+2013-12-09
+Print header value (if number) in base 10 (Chaitanya Gupta)
+Fix for CLISP compilation (Anton Vodonosov)
+
+Version 1.2.21
+2013-10-04
+Fix capitalization inconsistencies in docs (reported by Stas Boukarev)
+
+Version 1.2.20
+2013-10-04
+Don't rely on asdf to find default document directory
+Add m4v mime type (Wes Henderson)
+
+Version 1.2.19
+2013-07-28
+Fix ACCEPTOR-REMOVE-SESSION default implementation (Stas Boukarev, Mathieu Lemoine)
+
+Version 1.2.18
+2013-05-03
+Prevent errors when basic auth user or password contains colon
+Add missing implementation for client-as-string for Lispworks, contributed by Raymond Wiker
+
+Version 1.2.17
+2013-04-01
+New START-THREAD API function (Faré Rideau)
+
+Version 1.2.16
+2013-03-31
+Fix bug that caused error when requesting nonexistent page.
+
+Version 1.2.15
+2013-03-17
+Fix race condition in acceptor shutdown (Faré Rideau)
+
+Version 1.2.14
+2013-03-08
+Call ACCEPTOR-STATUS-MESSAGE in a saner and more useful fashion (sponsored by Ron Garret)
+
+Version 1.2.13
+2013-03-03
+fix wrong documented signature in acceptor-status-message (reported by Zach Beane)
+
+Version 1.2.12
+2013-03-03
+Various documentation updates
+Fix bug in static file handling that caused Safari to hang on 304 responses by Hunchentoot (reported by Wim Oudshoorn)
+
+Version 1.2.11
+2013-01-27
+Fix bug in MD5-HEX that could cause session keys to be reused
+
+Version 1.2.10
+2013-01-19
+Add local-addr* and local-port* functions
+
+Version 1.2.9
+2012-12-28
+Fix test script to accomodate for Drakma fix regarding redirect from POST to GET
+Fix range handling once again
+
+Version 1.2.8
+2012-12-19
+ECL fixes (Juan Jose Giarcia-Ripoll)
+
+Version 1.2.7
+2012-10-19
+Fix documentation for COOKIE-IN, which returns a string, not a cookie.
+Hunchentoot could not deal with / pointing to a static file directory (Stas Boukarev).
+Fixes to pathname sanitizing when handling static files.
+Further Range: header handling fixes
+Fix some export names relating to taskmaster thread count (Faré Rideau)
+
+Version 1.2.6
+2012-09-02
+Doc fixes, add .pre-release.sh script
+
+Version 1.2.5
+2012-09-02
+High-load multithread stability fixes (Mathieu Lemoine)
+
+Version 1.2.4
+2012-08-16
+Remove dead code & style fixes (Ala'a Mohammad)
+Bug fix: setting *print-base* / *print-radix* caused invalid cookie values (Scott L. Burson)
+Various documentation and style updates
+Fix documentation bug found (Mathieu Lemoine)
+Fix bug that could hang Hunchentoot under load (Mathieu Lemoine)
+
+Version 1.2.3
+2012-03-03
+Fix crash when error occurs while logging error (reported by Xu Jingtao)
+Fix compilation with :hunchentoot-no-ssl feature (Mark Evenson)
+Fix Range header handling (Simon Sandlund)
+Export acceptor-remove-session
+ECL timeout support (Juan Jose Giarcia-Ripoll)
+Changed cookie handling - Hunchentoot no longer encodes cookies
+automatically.  Applications must make sure that they only set cookies
+to permitted values (refer to RFC6265 for the details, thanks to Ralf
+Stoye for debugging help)
+
+Version 1.2.2
+2011-11-30
+Fix warning on LispWorks (Nico de Jager)
+Documentation updates
+Remove obsolete symbols from export list
+Add easy-ssl-acceptor
+Document acceptor-remove-session, remove obsolete *session-removal-hook* export (Issue #15)
+Added :description to asdf system definition
+Add documentation section describing how to bind to privileged ports
+
+Version 1.2.1
+2011-11-04
+Use FINISH-OUTPUT instead of FORCE-OUTPUT at connection end (I. Perminov)
+Documentation updates
+External format EOL style fixes for Windows (Anton Kovalenko)
+
+Version 1.2.0
+2011-10-30
+Fix to allow send-service-unavailable-reply to work (Faré Rideau)
+Make sure we always have a cooked message to send in case of error (Faré Rideau)
+Add www/ directory with default file tree that is being served
+Add error template mechanism and improve error reporting in general.
+Improve automatic testing, SBCL kludge to support asdf:test-op
+Allegro CL modern mode fixes
+Fix bugs in serving partial responses
+Limit maximum number of threads that Hunchentoot creates (Dan Weinreb, Scott McKay)
+Export fixes (Gordon Sims, Andrey Moskvitin, Faré Rideau)
+Factor out easy-handler logic into separate acceptor subclass
+Export two session functions (Nico de Jager)
+Allow no Content-Type header (Chaitanya Gupta)
+Patch for compilation with ECL (Sohail Somani)
+Fix DEFINE-EASY-HANDLER for multiple acceptors (Nicolas Neuss)
+Revived *SHOW-LISP-BACKTRACES-P*
+Made sure "100 Continue" is returned even if the client sends "Expect: 100-continue" twice (reported by Gordon Sims)
+Fixed typo in code which interprets transfer encodings
+
+Version 1.1.1
+2010-08-24
+Exported WITHIN-REQUEST-P (Faré Rideau)
+Safeguard measures against XSS attacks (J.P. Larocque)
+Prevent potential leak when closing stream (Matt Lamari, Martin Simmons)
+Change some occurrences of HANDLER-CASE* to HANDLER-CASE (Hans Hübner, Allan Dee)
+
+Version 1.1.0
+2010-01-08
+Architectural changes - see HANDLE-REQUEST (thanks to Andreas Fuchs and Frode Fjeld)
+Re-introduced *CATCH-ERRORS-P* and MAYBE-INVOKE-DEBUGGER
+Integration with trivial-backtrace (see *LOG-LISP-BACKTRACES-P*)
+Treat :UNSPECIFIC like NIL in pathname components (reported by Frode Fjeld)
+Fixed RESET-SESSIONS
+Prepared for LispWorks 6 (Nico de Jager)
+Fixed reading of post parameters (Peter Seibel and Stephen P. Compall)
+Fixed STOP by supplying the :READY-ONLY keyword to USOCKET:WAIT-FOR-INPUT
+Enabled SSL key passwords for Lisps other than LW (Vsevolod)
+
+Version 1.0.0
+2009-02-19
+Complete architectural redesign (together with Hans Hübner)
+Lots of small fixes and improvements, too many to enumerate here
+
+Version 0.15.6
+2008-04-09
+Fixed embarrassingly mis-placed parentheses (thanks to Hans Hübner)
+
+Version 0.15.5
+2008-04-08
+Removed FORCE-OUTPUT* and thus the ACL-COMPAT dependency (thanks to Hans Hübner)
+Support for MP-less CMUCL (thanks to Hans Hübner)
+
+Version 0.15.4
+2008-03-27
+Fixed unportable LOOP usage (caught by "C S S")
+
+Version 0.15.3
+2008-03-17
+Added CODE parameter to REDIRECT (thanks to Michael Weber)
+
+Version 0.15.2
+2008-03-06
+Fixed typo in test.lisp (thanks to Ben Hyde)
+Changed wrong usage of EQ to EQL (thanks to Ariel Badichi)
+Fixed typo in default handler (thanks to Eugene Ossintsev)
+
+Version 0.15.1
+2008-02-13
+Uses CL-FAD for HANDLE-STATIC-FILE now
+Better error reporting for CREATE-FOLDER-DISPATCHER-AND-HANDLER (suggested by Cyrus Harmon)
+Faster version of WRITE-HEADER-LINE (thanks to V. Segu�)
+
+Version 0.15.0
+2007-12-29
+Added support for CLISP (thanks to Anton Vodonosov)
+
+Version 0.14.7
+2007-11-15
+Replace ENOUGH-NAMESTRING with ENOUGH-URL (patch by Kilian Sprotte and Hans Hübner)
+
+Version 0.14.6
+2007-11-08
+Fix compilation order (thanks to Tiarnan O'Corrain and Chris Dean)
+
+Version 0.14.5
+2007-10-21
+Robustified MAKE-SOCKET-STREAM against potential leak (thanks to Alain Picard)
+Replaced #-FOO #-FOO constructs for OpenMCL (patch by Michael Weber)
+Updated tutorial links
+
+Version 0.14.4
+2007-10-20
+Made log stream shared on OpenMCL (thanks to Gary Byers)
+
+Version 0.14.3
+2007-10-07
+Enabled GET-GID-FROM-NAME for newer versions of SBCL (patch by Cyrus Harmon)
+
+Version 0.14.2
+2007-09-26
+Better handling of PORT parameter in REDIRECT (thanks to Vladimir Sedach)
+
+Version 0.14.1
+2007-09-24
+Fixed bug where you couldn't set "Server" header (caught by Ralf Mattes)
+Documentation clarification for HEADER-OUT function
+
+Version 0.14.0
+2007-09-18
+Added support for "HttpOnly" cookie attribute
+
+Version 0.13.0
+2007-09-14
+Added *METHODS-FOR-POST-PARAMETERS* (suggested by Jonathon McKitrick)
+
+Version 0.12.1
+2007-09-13
+Better support for WITH-TIMEOUT on SBCL/Win32 (thanks to Anton Vodonosov)
+
+Version 0.12.0
+2007-09-07
+Now uses bound for flexi stream returned by RAW-POST-DATA
+Needs FLEXI-STREAMS 0.12.0 or higher
+
+Version 0.11.2
+2007-09-05
+Fixed typo in docs
+Added declaration in server.lisp to appease SBCL
+
+Version 0.11.1
+2007-05-25
+Fixes for OpenMCL (thanks to Lennart Staflin and Tiarnan O'Corrain)
+
+Version 0.11.0
+2007-05-25
+Added server names and coupled them with easy handlers (suggested by Mac Chan)
+Exported SESSION-COOKIE-VALUE instead of SESSION-STRING (suggested by Slava Akhmechet)
+Documentation fixes (thanks to Victor Kryukov and Igor Plekhov)
+
+Version 0.10.0
+2007-05-12
+Made MAYBE-INVOKE-DEBUGGER a generic function and exported it (suggested by Vladimir Sedach)
+
+Version 0.9.3
+2007-05-08
+Fixed CREATE-FOLDER-DISPATCHER-AND-HANDLER in the presence of URL-encoded URLs (bug caught by Nicolas Lamirault)
+
+Version 0.9.2
+2007-05-01
+Made DEF-HTTP-RETURN-CODE more flexible (suggested by Jong-won Choi)
+
+Version 0.9.1
+2007-04-29
+Added PORT parameter to REDIRECT (suggested by Cyrus Harmon)
+Exported REMOVE-SESSION (suggested by Vamsee Kanakala)
+
+Version 0.9.0
+2007-04-19
+Added socket timeouts for AllegroCL
+Catch IO timeout conditions for AllegroCL, SBCL and CMUCL (suggested by Red Daly and others)
+Added per-server dispatch tables (suggested by Robert Synnott and Andrei Stebakov)
+
+Version 0.8.6
+2007-04-18
+USE the CL package explicitly when defining HUNCHENTOOT-MP (bug report by Joel Boehland)
+
+Version 0.8.5
+2007-04-10
+Correct behaviour for "100 Continue" responses
+
+Version 0.8.4
+2007-04-09
+Cleanup
+
+Version 0.8.3
+2007-04-07
+Don't use chunked encoding for empty (NIL) bodies
+
+Version 0.8.2
+2007-04-05
+Really exported REASON-PHRASE this time (and also *CURRENT-PROCESS*)
+
+Version 0.8.1
+2007-04-04
+Added HUNCHENTOOT-MP package (suggested by Cyrus Harmon)
+Only invoke MARK-AND-SWEEP for 32-bit versions of LW (thanks to Chris Dean)
+Exported REASON-PHRASE
+
+Version 0.8.0
+2007-03-31
+Added *APPROVED-RETURN-CODES*, *HEADER-STREAM*, and +HTTP-FAILED-DEPENDENCY+
+Exported MIME-TYPE and SSL-P
+Some minor changes
+
+Version 0.7.3
+2007-03-28
+Added +HTTP-MULTI-STATUS+
+
+Version 0.7.2
+2007-03-09
+Fix test suite to properly handle non-base characters in LW (bug caught by Jong-won Choi)
+
+Version 0.7.1
+2007-03-09
+Fixed last change (thanks to Marko Kocic)
+
+Version 0.7.0
+2007-03-09
+Development port (no threads) to SBCL/Win32 (patch by Marko Kocic)
+Support for compilation without SSL
+
+Version 0.6.2
+2007-02-22
+Don't use NSTRING-UPCASE for outgoing headers (bug caught by Saurabh Nanda)
+Changed ProxyPass example in docs from /lisp to /hunchentoot
+
+Version 0.6.1
+2007-01-24
+Reset to "faithful" external format on each iteration (bug caught by Viljo Marrandi and Ury Marshak)
+
+Version 0.6.0
+2007-01-23
+Accept chunked transfer encoding for mod_lisp request bodies (thanks to Hugh Winkler's mod_lisp additions)
+Robustify against erroneous form-data submissions (caught by Ury Marshak)
+
+Version 0.5.1
+2007-01-18
+Even more flexible behaviour of RAW-POST-DATA
+
+Version 0.5.0
+2007-01-17
+More flexible behaviour of RAW-POST-DATA
+Robustified PARSE-CONTENT-TYPE
+
+Version 0.4.14
+2007-01-17
+More meaningful results for RAW-POST-DATA
+
+Version 0.4.13
+2007-01-14
+Added favicon.ico to example website (thanks to Yoni Rabkin Katzenell, Toby, and Uwe von Loh)
+
+Version 0.4.12
+2006-12-27
+Added Hunchentoot logo by Uwe von Loh
+
+Version 0.4.11
+2006-12-01
+Exported symbols related to session GC (suggested by Nico de Jager)
+
+Version 0.4.10
+2006-11-19
+Added *HANDLE-HTTP-ERRORS-P* (thanks to Marijn Haverbeke)
+Remove duplicate headers when reading from mod_lisp
+
+Version 0.4.9
+2006-11-12
+Fixed HEADER-OUT (thanks to Robert J. Macomber)
+
+Version 0.4.8
+2006-11-06
+Fixed bug in START-OUTPUT which confused mod_lisp
+
+Version 0.4.7
+2006-11-06
+Changed behaviour of REAL-REMOTE-ADDR (as suggested by Robert J. Macomber)
+Fixed COOKIE-OUT (thanks to Robert J. Macomber)
+
+Version 0.4.6
+2006-11-05
+Don't bind *DISPATCH-TABLE* too early (thanks to Marijn Haverbeke)
+
+Version 0.4.5
+2006-10-25
+Fixed bug in AUTHORIZATION function (reported by Michael J. Forster)
+
+Version 0.4.4
+2006-10-12
+Correct SSL check in REDIRECT function
+LOG-MESSAGE now checks for (BOUNDP '*SERVER*)
+
+Version 0.4.3
+2006-10-11
+OpenMCL fixes (by Ralf Stoye)
+
+Version 0.4.2
+2006-10-10
+No timeouts for mod_lisp servers (as in Hunchentoot 0.3.x)
+
+Version 0.4.1
+2006-10-10
+Fixed a typo in easy-handlers.lisp (caught by Travis Cross)
+
+Version 0.4.0
+2006-10-10
+Ported to CMUCL, SBCL, OpenMCL, and AllegroCL
+Merged with TBNL
+Tons of small changes, too many to list them individually
+
+Version 0.3.2
+2006-09-14
+Uses TBNL's WITH-DEBUGGER now
+
+Version 0.3.1
+2006-09-14
+Added *CATCH-ERRORS-P* (from TBNL)
+
+Version 0.3.0
+2006-09-05
+Accept HTTP requests with chunked transfer encoding
+Use Chunga for chunking
+
+Version 0.2.2
+2006-08-31
+Skip START-OUTPUT advice completely if working for TBNL
+
+Version 0.2.1
+2006-08-28
+Added write timeouts for LW 5.0
+Updated LW links in documentation
+
+Version 0.2.0
+2006-08-28
+Serves as infrastructure for TBNL now (to replace KMRCL)
+For HTTP/1.1 only send 'Keep-Alive' headers if explicitly requested
+
+Version 0.1.5
+2006-08-23
+Connection headers are separated by commas, not semicolons
+
+Version 0.1.4
+2006-08-22
+Refactored streams.lisp to appease LW compiler (thanks to Martin Simmons)
+Changed handling of version string
+Changed package handling in system definition (thanks to Christophe Rhodes)
+
+Version 0.1.3
+2006-02-08
+Removed KMRCL workaround
+
+Version 0.1.2
+2006-01-03
+Mention TBNL version number in server name header
+
+Version 0.1.1
+2005-12-31
+Fixed package stuff and HYPERDOC support
+
+Version 0.1.0
+2005-12-31
+Initial public release
+
+[For earlier changes see the file "CHANGELOG_TBNL" that is included with the release.]
diff --git a/deps/hunchentoot/CHANGELOG_TBNL b/deps/hunchentoot/CHANGELOG_TBNL
new file mode 100644 (file)
index 0000000..8933384
--- /dev/null
@@ -0,0 +1,340 @@
+Version 0.11.3
+2006-09-30
+Added *FILE-UPLOAD-HOOK* (suggested by Erik Enge)
+Fixed DEFINE-EASY-HANDLER for cases where URI is NIL   
+       
+Version 0.11.2
+2006-09-20
+DEFINE-EASY-HANDLER: fixed and clarified redefinition
+DEFINE-EASY-HANDLER: allow for functions designators as "URIs"
+DEFINE-EASY-HANDLER: take file uploads into account
+Made logging a little bit more robust
+Added mime type for XSL-FO (.fo)
+
+Version 0.11.1
+2006-09-14
+Cleaner implementation of *CATCH-ERRORS-P*
+
+Version 0.11.0
+2006-09-14
+Added *CATCH-ERRORS-P*
+
+Version 0.10.3
+2006-09-05
+Appease SBCL (thanks to Juho Snellman) 
+
+Version 0.10.2
+2006-09-05
+Better reporting of IP addresses and ports if not behind mod_lisp
+Improved logging
+Fixed REAL-REMOTE-ADDR
+Cookies always use UTF-8 encoding (which is opaque to the client anyway)       
+Read request bodies without 'Content-Length' header (for Hunchentoot)
+Removed accented character from test.lisp to appease SBCL (reported by Xristos Kalkanis)
+
+Version 0.10.1
+2006-08-31
+Only LispWorks: Set read timeout to NIL if connected to mod_lisp       
+
+Version 0.10.0
+2006-08-28
+Based LispWorks version of TBNL on Hunchentoot infrastructure
+Added "easy" handlers
+Exported GET-BACKTRACE (suggested by Erik Enge)
+       
+Version 0.9.11
+2006-08-16
+Added note about SBCL problems
+
+Version 0.9.10
+2006-05-24
+Prepare for LW 5.0 release
+
+Version 0.9.9
+2006-05-12
+Workaround for something like "application/x-www-form-urlencoded;charset=UTF-8" (caught by John Bates)
+
+Version 0.9.8
+2006-04-25
+For mod_lisp, Lisp-Content-Length header must be sent after Content-Length header
+
+Version 0.9.7
+2006-02-06
+More robust computation of content length
+
+Version 0.9.6
+2006-01-22
+Added the missing piece (argh!)
+
+Version 0.9.5
+2006-01-22
+Made creation of REQUEST object safer (thanks to Robert J. Macomber)
+Replaced some erroneous DECLAIMs with DECLAREs (thanks to SBCL's style warnings)
+Slight documentation enhancements
+
+Version 0.9.4
+2006-01-03
+Handle "Expect: 100-continue" for non-Apache front-ends
+Re-introduced IGNORE-ERRORS in GET-REQUEST-DATA
+
+Version 0.9.3
+2006-01-01
+Fixed bug in READ-HTTP-REQUEST
+
+Version 0.9.2
+2005-12-31
+Protocol of reply is HTTP/1.1 now
+Made HTTP/0.9 default protocol of request if none was provided
+Some preparations for Hunchentoot
+Various minor changes
+Small fixes in docs
+
+Version 0.9.1
+2005-12-25
+Added missing file mime-types.lisp (thanks to Hilverd Reker)
+
+Version 0.9.0
+2005-12-24
+Experimental support for writing directly to the front-end (see SEND-HEADERS)
+Added HANDLE-STATIC-FILE
+Changed CREATE-STATIC-FILE-DISPATCHER-AND-HANDLER to use new facilities
+Added CREATE-FOLDER-DISPATCHER-AND-HANDLER
+Added link to Travis Cross' message w.r.t. SBCL
+
+Version 0.8.9
+2005-12-16
+Also use :TBNL-BIVALENT-STREAMS if :SB-UNICODE is present
+
+Version 0.8.8
+2005-12-08
+Made RAW-POST-DATA more useful
+Updated docs w.r.t. Araneida (thanks to Alan Shields)
+
+Version 0.8.7
+2005-11-29
+Made "Content-Length" header SETFable
+
+Version 0.8.6
+2005-11-18
+Restored original stream-based code for multipart/form-data parsing (got lost somehow)
+Wrapped REMOTE-ADDR with IGNORE-ERRORS (just in case)
+
+Version 0.8.5
+2005-11-14
+Added generic function DISPATCH-REQUEST (thanks to Jeff Caldwell)
+
+Version 0.8.4
+2005-10-21
+Provide REMOTE-ADDR if connected directly (for LispWorks and AllegroCL)
+Show remote user and address (if available) in non-Apache logs
+Mention Debian package in docs
+
+Version 0.8.3
+2005-10-10
+Alert LW users that a patch for OCTETS-TO-STRINGS is available (thanks to LispWorks support)
+
+Version 0.8.2
+2005-10-06
+Make STRING-TO-OCTETS and OCTETS-TO-STRING safer for LW
+
+Version 0.8.1
+2005-09-29
+Bugfix in CMUCL version of STRING-TO-OCTETS
+
+Version 0.8.0
+2005-09-24
+Added the ability to cope with different external formats (incorporating suggestions from Will Glozer and Ivan Shvedunov)
+Raw post data is now always saved (so *SAVE-RAW-POST-DATA-P* is gone)
+
+Version 0.7.0
+2005-09-17
+Added the ability to store arbitrary data within REQUEST objects (suggested by Zach Beane)
+Fixed handling of *HTTP-ERROR-HANDLER*
+Note: *TBNL-VERSION* was wrong in 0.6.0 and 0.6.1
+
+Version 0.6.1
+2005-09-10
+Robustified socket handling code
+
+Version 0.6.0
+2005-09-08
+Added TBNL-CONTRIB package
+Added contrib directory with first entry (from Alceste Scalas)
+Updated link to Bill Clementson's blog
+Don't redefine what's already there (for LispWorks)
+
+Version 0.5.5
+2005-04-18
+Make RFC 2388 code an external dependency (thanks to Janis Dzerins)
+
+Version 0.5.4
+2005-04-03
+Fixed dumb typo (caught by Bob Hutchison)
+
+Version 0.5.3
+2005-04-03
+Re-introduced automatic front-end selection (originally by Bob Hutchison)
+
+Version 0.5.2
+2005-03-26
+Fixed bug in modlisp.html where *CLOSE-TBNL-STREAM* could be NIL although it should be T
+Set correct content type for 304 replies
+
+Version 0.5.1
+2005-03-17
+Changed default cookie path in START-SESSION (suggested by Stefan Scholl)
+Small bugfixes
+More headers from the Araneida front-end
+Added *SHOW-ACCESS-LOG-MESSAGES*
+Changed "back-end" to "front-end" :)
+
+Version 0.5.0
+2005-03-17
+Initial support for "stand-alone" version (no front-end) (supplied by Bob Hutchison)
+New logging API
+Fixes in START-TBNL/STOP-TBNL
+Documentation enhancements
+
+Version 0.4.1
+2005-03-15
+Fixed some typos, removed unused code
+
+Version 0.4.0
+2005-03-14
+Initial Araneida support (supplied by Bob Hutchison)
+
+Version 0.3.13
+2005-03-12
+Small bugfix in RFC-1123-DATE (thanks to Bob Hutchison and Stefan Scholl)
+
+Version 0.3.12
+2005-03-01
+Added *HTTP-ERROR-HANDLER* (suggested and coded by Stefan Scholl)
+Exported and documented *SESSION-MAX-TIME*
+
+Version 0.3.11
+2005-02-21
+Added ability to access raw post data (suggested and coded by Zach Beane)
+
+Version 0.3.10
+2005-01-24
+Make bivalent streams work with LispWorks 4.4
+UTF-8 demo for LispWorks (thanks to Bob Hutchison)
+
+Version 0.3.9
+2004-12-31
+Re-compute content length after applying MAYBE-REWRITE-URLS-FOR-SESSION (caught by Stefan Scholl)
+
+Version 0.3.8
+2004-12-27
+Don't send body for HEAD requests (needs current mod_lisp version) 
+
+Version 0.3.7
+2004-12-22
+Change #\Del to #\Rubout in QUOTE-STRING (AllegroCL complains, #\Del isn't even semi-standard)
+
+Version 0.3.6
+2004-12-02
+Make REQUIRE-AUTHORIZATION compliant to RFC 2616 (thanks to Stefan Scholl)
+
+Version 0.3.5
+2004-12-01
+Several small doc fixes (thanks to Stefan Scholl)
+Catch requests like "GET http://server/foo.html HTTP/1.0" (suggested by Stefan Scholl)
+
+Version 0.3.4
+2004-11-29
+Added backtrace code for OpenMCL (provided by Tiarnán Ó Corráin)
+
+Version 0.3.3
+2004-11-22
+Cleaner handling of macro variables
+
+Version 0.3.2
+2004-11-11
+Updated docs for mod_lisp2
+
+Version 0.3.1
+2004-11-09
+Slight changes to support Chris Hanson's mod_lisp2
+Changed GET-BACKTRACE for newer SBCL versions (thanks to Nikodemus Siivola)
+
+Version 0.3.0
+2004-11-09
+Initial support for multipart/form-data (thanks to Michael Weber and Janis Dzerins)
+Fixed bug in CREATE-STATIC-FILE-DISPATCHER-AND-HANDLER (caught by Bill Clementson)
+
+Version 0.2.12
+2004-10-15
+Exported and documented DO-SESSIONS
+
+Version 0.2.11
+2004-09-02
+FORM-URL-ENCODED-LIST-TO-ALIST now decodes names and values
+
+Version 0.2.10
+2004-08-28
+Allow non-strings to be cookie values (bug caught by Zach Beane)
+
+Version 0.2.9
+2004-08-11
+Consistent usage of RFC-1123-DATE (provided by Stefan Scholl)
+Added all missing http headers from RFC 2616 (provided by Stefan Scholl)
+Added support for mod_lisp version strings (see <http://common-lisp.net/pipermail/mod-lisp-devel/2004-August/000019.html>)
+Don't always add session IDs when redirecting
+
+Version 0.2.8
+2004-07-24
+Fixed typo in html.lisp and improved docs (both caught by Stefan Scholl)
+
+Version 0.2.7
+2004-07-24
+Add missing exports and docs
+
+Version 0.2.6
+2004-07-24
+Make CREATE-STATIC-FILE-DISPATCHER-AND-HANDLER thread-safe (caught by Jeff Caldwell)
+Added support for 'If-Modified-Since' request headers (provided by Stefan Scholl)
+
+Version 0.2.5
+2004-07-21
+Added CREATE-STATIC-FILE-DISPATCHER-AND-HANDLER (provided by Stefan Scholl)
+Improved test suite
+
+Version 0.2.4
+2004-07-19
+New variable *CONTENT-TYPES-FOR-URL-REWRITE* (suggested by Stefan Scholl)
+Updated index.html regarding new version of mod_lisp
+
+Version 0.2.3
+2004-06-12
+Bugfix for FORM-URL-ENCODED-LIST-TO-ALIST (bug caught by Jong-won Choi)
+
+Version 0.2.2
+2004-06-10
+Bugfix for SESSION-GC and RESET-SESSIONS (bug introduced in 0.2.0)
+
+Version 0.2.1
+2004-06-10
+Only create backtrace if needed (speeds up AllegroCL considerably)
+
+Version 0.2.0
+2004-06-07
+Added SESSION-STRING and *SESSION-REMOVAL-HOOK*
+Added GET-BACKTRACE for AllegroCL
+
+Version 0.1.2
+2004-05-12
+Removed some more typos in docs (thanks to Karl A. Krueger)
+Changed BASE64 to CL-BASE64 in .asd file (thanks to Frank Sonnemans and Nicolas Lamirault)
+
+Version 0.1.1
+2004-05-08
+Removed some old files from Jeff's port
+Fixed a couple of typos in docs
+
+Version 0.1.0
+2004-05-07
+First public release
+Original code by Edi Weitz
+Initial doc strings, port to KMRCL, logging code and various other improvements by Jeff Caldwell
diff --git a/deps/hunchentoot/README b/deps/hunchentoot/README
new file mode 100644 (file)
index 0000000..5d03367
--- /dev/null
@@ -0,0 +1,4 @@
+Complete documentation for Hunchentoot including details about how to
+install it can be found in the 'doc' directory.
+
+Join the chat at https://gitter.im/edicl/hunchentoot
diff --git a/deps/hunchentoot/acceptor.lisp b/deps/hunchentoot/acceptor.lisp
new file mode 100644 (file)
index 0000000..5d90b8a
--- /dev/null
@@ -0,0 +1,789 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(eval-when (:load-toplevel :compile-toplevel :execute)
+  (defun default-document-directory (&optional sub-directory)
+    (let ((source-directory #.(or *compile-file-truename* *load-truename*)))
+      (merge-pathnames (make-pathname :directory (append (pathname-directory source-directory)
+                                                         (list "www")
+                                                         (when sub-directory
+                                                           (list sub-directory)))
+                                      :name nil
+                                      :type nil
+                                      :defaults source-directory)))))
+
+(defclass acceptor ()
+  ((port :initarg :port
+         :reader acceptor-port
+         :documentation "The port the acceptor is listening on.  The
+default is 80.  Note that depending on your operating system you might
+need special privileges to listen on port 80.  When 0, the port will be
+chosen by the system the first time the acceptor is started.")
+   (address :initarg :address
+            :reader acceptor-address
+            :documentation "The address the acceptor is listening on.
+If address is a string denoting an IP address, then the server only
+receives connections for that address.  This must be one of the
+addresses associated with the machine and allowed values are host
+names such as \"www.zappa.com\" and address strings such as
+\"72.3.247.29\".  If address is NIL, then the server will receive
+connections to all IP addresses on the machine. This is the default.")
+   (name :initarg :name
+         :accessor acceptor-name
+         :documentation "The optional name of the acceptor, a symbol.
+This name can be utilized when defining \"easy handlers\" - see
+DEFINE-EASY-HANDLER.  The default name is an uninterned symbol as
+returned by GENSYM.")
+   (request-class :initarg :request-class
+                  :accessor acceptor-request-class
+                  :documentation "Determines which class of request
+objects is created when a request comes in and should be \(a symbol
+naming) a class which inherits from REQUEST.  The default is the
+symbol REQUEST.")
+   (reply-class :initarg :reply-class
+                :accessor acceptor-reply-class
+                  :documentation "Determines which class of reply
+objects is created when a request is served in and should be \(a
+symbol naming) a class which inherits from REPLY.  The default is the
+symbol REPLY.")
+   (taskmaster :initarg :taskmaster
+               :reader acceptor-taskmaster
+               :documentation "The taskmaster \(i.e. an instance of a
+subclass of TASKMASTER) that is responsible for scheduling the work
+for this acceptor.  The default depends on the MP capabilities of the
+underlying Lisp.")
+   (output-chunking-p :initarg :output-chunking-p
+                      :accessor acceptor-output-chunking-p
+                      :documentation "A generalized boolean denoting
+whether the acceptor may use chunked encoding for output, i.e. when
+sending data to the client.  The default is T and there's usually no
+reason to change this to NIL.")
+   (input-chunking-p :initarg :input-chunking-p
+                     :accessor acceptor-input-chunking-p
+                      :documentation "A generalized boolean denoting
+whether the acceptor may use chunked encoding for input, i.e. when
+accepting request bodies from the client.  The default is T and
+there's usually no reason to change this to NIL.")
+   (persistent-connections-p :initarg :persistent-connections-p
+                             :accessor acceptor-persistent-connections-p
+                             :documentation "A generalized boolean
+denoting whether the acceptor supports persistent connections, which
+is the default for threaded acceptors.  If this property is NIL,
+Hunchentoot closes each incoming connection after having processed one
+request.  This is the default for non-threaded acceptors.")
+   (read-timeout :initarg :read-timeout
+                 :reader acceptor-read-timeout
+                 :documentation "The read timeout of the acceptor,
+specified in \(fractional) seconds.  The precise semantics of this
+parameter is determined by the underlying Lisp's implementation of
+socket timeouts.  NIL means no timeout.")
+   (write-timeout :initarg :write-timeout
+                  :reader acceptor-write-timeout
+                  :documentation "The write timeout of the acceptor,
+specified in \(fractional) seconds.  The precise semantics of this
+parameter is determined by the underlying Lisp's implementation of
+socket timeouts.  NIL means no timeout.")
+   #+:lispworks
+   (process :accessor acceptor-process
+            :documentation "The Lisp process which accepts incoming
+requests.  This is the process started by COMM:START-UP-SERVER and no
+matter what kind of taskmaster you are using this will always be a new
+process different from the one where START was called.")
+   #-:lispworks
+   (listen-socket :initform nil
+                  :accessor acceptor-listen-socket
+                  :documentation "The socket listening for incoming
+connections.")
+   #-:lispworks
+   (listen-backlog :initarg :listen-backlog
+                   :reader acceptor-listen-backlog
+                   :documentation "Number of pending connections
+          allowed in the listen socket before the kernel rejects
+          further incoming connections.")
+   (acceptor-shutdown-p :initform t
+                        :accessor acceptor-shutdown-p
+                        :documentation "A flag that makes the acceptor
+shutdown itself when set to something other than NIL.")
+   (requests-in-progress :initform 0
+                         :accessor accessor-requests-in-progress
+                         :documentation "The number of
+requests currently in progress.")
+   (shutdown-queue :initform (make-condition-variable)
+                   :accessor acceptor-shutdown-queue
+                   :documentation "A condition variable
+used with soft shutdown, signaled when all requests
+have been processed.")
+   (shutdown-lock :initform (make-lock "hunchentoot-acceptor-shutdown")
+                  :accessor acceptor-shutdown-lock
+                  :documentation "The lock protecting the shutdown-queue
+condition variable and the requests-in-progress counter.")
+   (access-log-destination :initarg :access-log-destination
+                        :accessor acceptor-access-log-destination
+                        :documentation "Destination of the access log
+which contains one log entry per request handled in a format similar
+to Apache's access.log.  Can be set to a pathname or string
+designating the log file, to a open output stream or to NIL to
+suppress logging.")
+   (message-log-destination :initarg :message-log-destination
+                         :accessor acceptor-message-log-destination
+                         :documentation "Destination of the server
+error log which is used to log informational, warning and error
+messages in a free-text format intended for human inspection. Can be
+set to a pathname or string designating the log file, to a open output
+stream or to NIL to suppress logging.")
+   (error-template-directory :initarg :error-template-directory
+                             :accessor acceptor-error-template-directory
+                             :documentation "Directory pathname that
+ contains error message template files for server-generated error
+ messages.  Files must be named <return-code>.html with <return-code>
+ representing the HTTP return code that the file applies to,
+ i.e. 404.html would be used as the content for a HTTP 404 Not found
+ response.")
+   (document-root :initarg :document-root
+                  :accessor acceptor-document-root
+                  :documentation "Directory pathname that points to
+files that are served by the acceptor if no more specific
+acceptor-dispatch-request method handles the request."))
+  (:default-initargs
+   :address nil
+   :port 80
+   :name (gensym)
+   :request-class 'request
+   :reply-class 'reply
+   #-lispworks :listen-backlog #-lispworks 50
+   :taskmaster (make-instance (cond (*supports-threads-p* 'one-thread-per-connection-taskmaster)
+                                    (t 'single-threaded-taskmaster)))
+   :output-chunking-p t
+   :input-chunking-p t
+   :persistent-connections-p t
+   :read-timeout *default-connection-timeout*
+   :write-timeout *default-connection-timeout*
+   :access-log-destination *error-output*
+   :message-log-destination *error-output*
+   :document-root (load-time-value (default-document-directory))
+   :error-template-directory (load-time-value (default-document-directory "errors")))
+  (:documentation "To create a Hunchentoot webserver, you make an
+instance of this class and use the generic function START to start it
+\(and STOP to stop it).  Use the :PORT initarg if you don't want to
+listen on the default http port 80.  There are other initargs most of
+which you probably won't need very often.  They are explained in
+detail in the docstrings of the slot definitions for this class.
+
+Unless you are in a Lisp without MP capabilities, you can have several
+active instances of ACCEPTOR \(listening on different ports) at the
+same time."))
+
+(defmethod print-object ((acceptor acceptor) stream)
+  (print-unreadable-object (acceptor stream :type t)
+    (format stream "\(host ~A, port ~A)"
+            (or (acceptor-address acceptor) "*") (acceptor-port acceptor))))
+
+(defmethod initialize-instance :after ((acceptor acceptor) &key)
+  (with-accessors ((document-root acceptor-document-root)
+                   (persistent-connections-p acceptor-persistent-connections-p)
+                   (taskmaster acceptor-taskmaster)
+                   (error-template-directory acceptor-error-template-directory)) acceptor
+    (when (typep taskmaster
+                 'single-threaded-taskmaster)
+      (setf persistent-connections-p nil))
+    (when document-root
+      (setf document-root (translate-logical-pathname document-root)))
+    (when error-template-directory
+      (setf error-template-directory (translate-logical-pathname error-template-directory)))))
+
+(defgeneric start (acceptor)
+  (:documentation "Starts the ACCEPTOR so that it begins accepting
+connections.  Returns the acceptor."))
+
+(defgeneric stop (acceptor &key soft)
+  (:documentation "Stops the ACCEPTOR so that it no longer accepts
+requests.  If SOFT is true, and there are any requests in progress,
+wait until all requests are fully processed, but meanwhile do not
+accept new requests.  Note that SOFT must not be set when calling
+STOP from within a request handler, as that will deadlock."))
+
+(defgeneric started-p (acceptor)
+  (:documentation "Tells if ACCEPTOR has been started.
+The default implementation simply queries ACCEPTOR for its listening
+status, so if T is returned to the calling thread, then some thread
+has called START or some thread's call to STOP hasn't finished. If NIL
+is returned either some thread has called STOP, or some thread's call
+to START hasn't finished or START was never called at all for
+ACCEPTOR.")
+  (:method (acceptor)
+    #-lispworks (and (acceptor-listen-socket acceptor) t)
+    #+lispworks (not (acceptor-shutdown-p acceptor))))
+
+(defgeneric start-listening (acceptor)
+  (:documentation "Sets up a listen socket for the given ACCEPTOR and
+enables it to listen to incoming connections.  This function is called
+from the thread that starts the acceptor initially and may return
+errors resulting from the listening operation \(like 'address in use'
+or similar)."))
+
+(defgeneric accept-connections (acceptor)
+  (:documentation "In a loop, accepts a connection and hands it over
+to the acceptor's taskmaster for processing using
+HANDLE-INCOMING-CONNECTION.  On LispWorks, this function returns
+immediately, on other Lisps it retusn only once the acceptor has been
+stopped."))
+
+(defgeneric initialize-connection-stream (acceptor stream)
+ (:documentation "Can be used to modify the stream which is used to
+communicate between client and server before the request is read.  The
+default method of ACCEPTOR does nothing, but see for example the
+method defined for SSL-ACCEPTOR.  All methods of this generic function
+must return the stream to use."))
+
+(defgeneric reset-connection-stream (acceptor stream)
+  (:documentation "Resets the stream which is used to communicate
+between client and server after one request has been served so that it
+can be used to process the next request.  This generic function is
+called after a request has been processed and must return the
+stream."))
+
+(defgeneric process-connection (acceptor socket)
+  (:documentation "This function is called by the taskmaster when a
+new client connection has been established.  Its arguments are the
+ACCEPTOR object and a LispWorks socket handle or a usocket socket
+stream object in SOCKET.  It reads the request headers, sets up the
+request and reply objects, and hands over to PROCESS-REQUEST.  This is
+done in a loop until the stream has to be closed or until a connection
+timeout occurs.
+
+It is probably not a good idea to re-implement this method until you
+really, really know what you're doing."))
+
+(defgeneric handle-request (acceptor request)
+  (:documentation "This function is called once the request has been
+read and a REQUEST object has been created.  Its job is to set up
+standard error handling and request logging.
+
+Might be a good place for around methods specialized for your subclass
+of ACCEPTOR which bind or rebind special variables which can then be
+accessed by your handlers."))
+
+(defgeneric acceptor-dispatch-request (acceptor request)
+  (:documentation "This function is called to actually dispatch the
+request once the standard logging and error handling has been set up.
+ACCEPTOR subclasses implement methods for this function in order to
+perform their own request routing.  If a method does not want to
+handle the request, it is supposed to invoke CALL-NEXT-METHOD so that
+the next ACCEPTOR in the inheritance chain gets a chance to handle the
+request."))
+
+(defgeneric acceptor-ssl-p (acceptor)
+  (:documentation "Returns a true value if ACCEPTOR uses SSL
+connections.  The default is to unconditionally return NIL and
+subclasses of ACCEPTOR must specialize this method to signal that
+they're using secure connections - see the SSL-ACCEPTOR class."))
+
+;; general implementation
+
+(defmethod start ((acceptor acceptor))
+  (setf (acceptor-shutdown-p acceptor) nil)
+  (let ((taskmaster (acceptor-taskmaster acceptor)))
+    (setf (taskmaster-acceptor taskmaster) acceptor)
+    (start-listening acceptor)
+    (execute-acceptor taskmaster))
+  acceptor)
+
+(defmethod stop ((acceptor acceptor) &key soft)
+  (with-lock-held ((acceptor-shutdown-lock acceptor))
+    (setf (acceptor-shutdown-p acceptor) t))
+  #-lispworks
+  (wake-acceptor-for-shutdown acceptor)
+  (when soft
+    (with-lock-held ((acceptor-shutdown-lock acceptor))
+      (when (plusp (accessor-requests-in-progress acceptor))
+        (condition-variable-wait (acceptor-shutdown-queue acceptor)
+                                 (acceptor-shutdown-lock acceptor)))))
+  (shutdown (acceptor-taskmaster acceptor))
+  #-lispworks
+  (usocket:socket-close (acceptor-listen-socket acceptor))
+  #-lispworks
+  (setf (acceptor-listen-socket acceptor) nil)
+  #+lispworks
+  (mp:process-kill (acceptor-process acceptor))
+  acceptor)
+
+#-lispworks
+(defun wake-acceptor-for-shutdown (acceptor)
+  "Creates a dummy connection to the acceptor, waking ACCEPT-CONNECTIONS while it is waiting.
+This is supposed to force a check of ACCEPTOR-SHUTDOWN-P."
+  (handler-case
+      (multiple-value-bind (address port) (usocket:get-local-name (acceptor-listen-socket acceptor))
+        (let ((conn (usocket:socket-connect address port)))
+          (usocket:socket-close conn)))
+    (error (e)
+      (acceptor-log-message acceptor :error "Wake-for-shutdown connect failed: ~A" e))))
+
+(defmethod initialize-connection-stream ((acceptor acceptor) stream)
+ ;; default method does nothing
+ stream)
+
+(defmethod reset-connection-stream ((acceptor acceptor) stream)
+  ;; turn chunking off at this point
+  (cond ((typep stream 'chunked-stream)
+         ;; flush the stream first and check if there's unread input
+         ;; which would be an error
+         (setf (chunked-stream-output-chunking-p stream) nil
+               (chunked-stream-input-chunking-p stream) nil)
+         ;; switch back to bare socket stream
+         (chunked-stream-stream stream))
+        (t stream)))
+
+(defmethod process-connection :around ((*acceptor* acceptor) (socket t))
+  ;; this around method is used for error handling
+  ;; note that this method also binds *ACCEPTOR*
+  (with-conditions-caught-and-logged ()
+    (with-mapped-conditions ()
+      (call-next-method))))
+
+(defun do-with-acceptor-request-count-incremented (*acceptor* function)
+  (with-lock-held ((acceptor-shutdown-lock *acceptor*))
+    (incf (accessor-requests-in-progress *acceptor*)))
+  (unwind-protect
+       (funcall function)
+    (with-lock-held ((acceptor-shutdown-lock *acceptor*))
+      (decf (accessor-requests-in-progress *acceptor*))
+      (when (acceptor-shutdown-p *acceptor*)
+        (condition-variable-signal (acceptor-shutdown-queue *acceptor*))))))
+
+(defmacro with-acceptor-request-count-incremented ((acceptor) &body body)
+  "Execute BODY with ACCEPTOR-REQUESTS-IN-PROGRESS of ACCEPTOR
+  incremented by one.  If the ACCEPTOR-SHUTDOWN-P returns true after
+  the BODY has been executed, the ACCEPTOR-SHUTDOWN-QUEUE condition
+  variable of the ACCEPTOR is signalled in order to finish shutdown
+  processing."
+  `(do-with-acceptor-request-count-incremented ,acceptor (lambda () ,@body)))
+
+(defun acceptor-make-request (acceptor socket
+                              &key
+                                  headers-in
+                                  content-stream
+                                  method
+                                  uri
+                                  server-protocol)
+  "Make a REQUEST instance for the ACCEPTOR, setting up those slots
+  that are determined from the SOCKET by calling the appropriate
+  socket query functions."
+  (multiple-value-bind (remote-addr remote-port)
+      (get-peer-address-and-port socket)
+    (multiple-value-bind (local-addr local-port)
+        (get-local-address-and-port socket)
+      (make-instance (acceptor-request-class acceptor)
+                     :acceptor acceptor
+                     :local-addr local-addr
+                     :local-port local-port
+                     :remote-addr remote-addr
+                     :remote-port remote-port
+                     :headers-in headers-in
+                     :content-stream content-stream
+                     :method method
+                     :uri uri
+                     :server-protocol server-protocol))))
+
+(defgeneric detach-socket (acceptor)
+  (:documentation "Indicate to Hunchentoot that it should stop serving
+                   requests on the current request's socket.
+                   Hunchentoot will finish processing the current
+                   request and then return from PROCESS-CONNECTION
+                   without closing the connection to the client.
+                   DETACH-SOCKET can only be called from within a
+                   request handler function."))
+
+(defmethod detach-socket ((acceptor acceptor))
+  (setf *finish-processing-socket* t
+        *close-hunchentoot-stream* nil))
+
+(defmethod process-connection ((*acceptor* acceptor) (socket t))
+  (let* ((socket-stream (make-socket-stream socket *acceptor*))
+         (*hunchentoot-stream*)
+         (*close-hunchentoot-stream* t))
+    (unwind-protect
+         ;; process requests until either the acceptor is shut down,
+         ;; *CLOSE-HUNCHENTOOT-STREAM* has been set to T by the
+         ;; handler, or the peer fails to send a request
+         (progn
+           (setq *hunchentoot-stream* (initialize-connection-stream *acceptor* socket-stream))
+           (loop
+              (let ((*finish-processing-socket* t))
+                (when (acceptor-shutdown-p *acceptor*)
+                  (return))
+                (multiple-value-bind (headers-in method url-string protocol)
+                    (get-request-data *hunchentoot-stream*)
+                  ;; check if there was a request at all
+                  (unless method
+                    (return))
+                  ;; bind per-request special variables, then process the
+                  ;; request - note that *ACCEPTOR* was bound above already
+                  (let ((*reply* (make-instance (acceptor-reply-class *acceptor*)))
+                        (*session* nil)
+                        (transfer-encodings (cdr (assoc* :transfer-encoding headers-in))))
+                    (when transfer-encodings
+                      (setq transfer-encodings
+                            (split "\\s*,\\s*" transfer-encodings))
+                      (when (member "chunked" transfer-encodings :test #'equalp)
+                        (cond ((acceptor-input-chunking-p *acceptor*)
+                               ;; turn chunking on before we read the request body
+                               (setf *hunchentoot-stream* (make-chunked-stream *hunchentoot-stream*)
+                                     (chunked-stream-input-chunking-p *hunchentoot-stream*) t))
+                              (t (hunchentoot-error "Client tried to use ~
+chunked encoding, but acceptor is configured to not use it.")))))
+                    (with-acceptor-request-count-incremented (*acceptor*)
+                      (process-request (acceptor-make-request *acceptor* socket
+                                                              :headers-in headers-in
+                                                              :content-stream *hunchentoot-stream*
+                                                              :method method
+                                                              :uri url-string
+                                                              :server-protocol protocol))))
+                  (finish-output *hunchentoot-stream*)
+                  (setq *hunchentoot-stream* (reset-connection-stream *acceptor* *hunchentoot-stream*))
+                  (when *finish-processing-socket*
+                    (return))))))
+      (when *close-hunchentoot-stream*
+        (flet ((close-stream (stream)
+                 ;; as we are at the end of the request here, we ignore all
+                 ;; errors that may occur while flushing and/or closing the
+                 ;; stream.
+                 (ignore-errors*
+                  (finish-output stream))
+                 (ignore-errors*
+                  (close stream :abort t))))
+          (unless (or (not *hunchentoot-stream*)
+                      (eql socket-stream *hunchentoot-stream*))
+            (close-stream *hunchentoot-stream*))
+          (close-stream socket-stream))))))
+
+(defmethod acceptor-ssl-p ((acceptor t))
+  ;; the default is to always answer "no"
+  nil)
+
+(defgeneric acceptor-log-access (acceptor &key return-code)
+  (:documentation
+   "Function to call to log access to the acceptor.  The RETURN-CODE,
+CONTENT and CONTENT-LENGTH keyword arguments contain additional
+information about the request to log.  In addition, it can use the
+standard request accessor functions that are available to handler
+functions to find out more information about the request."))
+
+(defmethod acceptor-log-access ((acceptor acceptor) &key return-code)
+  "Default method for access logging.  It logs the information to the
+destination determined by (ACCEPTOR-ACCESS-LOG-DESTINATION ACCEPTOR)
+\(unless that value is NIL) in a format that can be parsed by most
+Apache log analysis tools.)"
+
+  (with-log-stream (stream (acceptor-access-log-destination acceptor) *access-log-lock*)
+    (format stream "~:[-~@[ (~A)~]~;~:*~A~@[ (~A)~]~] ~:[-~;~:*~A~] [~A] \"~A ~A~@[?~A~] ~
+                    ~A\" ~D ~:[-~;~:*~D~] \"~:[-~;~:*~A~]\" \"~:[-~;~:*~A~]\"~%"
+            (remote-addr*)
+            (header-in* :x-forwarded-for)
+            (authorization)
+            (iso-time)
+            (request-method*)
+            (script-name*)
+            (query-string*)
+            (server-protocol*)
+            return-code
+            (content-length*)
+            (referer)
+            (user-agent))))
+
+(defgeneric acceptor-log-message (acceptor log-level format-string &rest format-arguments)
+  (:documentation
+   "Function to call to log messages by the ACCEPTOR.  It must accept
+a severity level for the message, which will be one of :ERROR, :INFO,
+or :WARNING, a format string and an arbitary number of formatting
+arguments."))
+
+(defmethod acceptor-log-message ((acceptor acceptor) log-level format-string &rest format-arguments)
+  "Default function to log server messages.  Sends a formatted message
+  to the destination denoted by (ACCEPTOR-MESSAGE-LOG-DESTINATION
+  ACCEPTOR).  FORMAT and ARGS are as in FORMAT.  LOG-LEVEL is a
+  keyword denoting the log level or NIL in which case it is ignored."
+  (with-log-stream (stream (acceptor-message-log-destination acceptor) *message-log-lock*)
+    (handler-case
+        (format stream "[~A~@[ [~A]~]] ~?~%"
+                (iso-time) log-level
+                format-string format-arguments)
+      (error (e)
+        (ignore-errors
+         (format *trace-output* "error ~A while writing to error log, error not logged~%" e))))))
+
+(defun log-message* (log-level format-string &rest format-arguments)
+  "Convenience function which calls the message logger of the current
+acceptor \(if there is one) with the same arguments it accepts.
+
+This is the function which Hunchentoot itself uses to log errors it
+catches during request processing."
+  (apply 'acceptor-log-message *acceptor* log-level format-string format-arguments))
+
+;; usocket implementation
+
+#-:lispworks
+(defmethod start-listening ((acceptor acceptor))
+  (when (acceptor-listen-socket acceptor)
+    (hunchentoot-error "acceptor ~A is already listening" acceptor))
+  (setf (acceptor-listen-socket acceptor)
+        (usocket:socket-listen (or (acceptor-address acceptor)
+                                   usocket:*wildcard-host*)
+                               (acceptor-port acceptor)
+                               :reuseaddress t
+                               :backlog (acceptor-listen-backlog acceptor)
+                               :element-type '(unsigned-byte 8)))
+  (values))
+
+#-:lispworks
+(defmethod start-listening :after ((acceptor acceptor))
+  (when (zerop (acceptor-port acceptor))
+    (setf (slot-value acceptor 'port) (usocket:get-local-port (acceptor-listen-socket acceptor)))))
+
+#-:lispworks
+(defmethod accept-connections ((acceptor acceptor))
+  (usocket:with-server-socket (listener (acceptor-listen-socket acceptor))
+    (loop
+      (with-lock-held ((acceptor-shutdown-lock acceptor))
+        (when (acceptor-shutdown-p acceptor)
+          (return)))
+      (when (usocket:wait-for-input listener :ready-only t)
+       (when-let (client-connection
+                  (handler-case (usocket:socket-accept listener)
+                    ;; ignore condition
+                    (usocket:connection-aborted-error ())))
+         (set-timeouts client-connection
+                       (acceptor-read-timeout acceptor)
+                       (acceptor-write-timeout acceptor))
+         (handle-incoming-connection (acceptor-taskmaster acceptor)
+                                     client-connection))))))
+
+;; LispWorks implementation
+
+#+:lispworks
+(defmethod start-listening ((acceptor acceptor))
+  (multiple-value-bind (listener-process startup-condition)
+      (comm:start-up-server :service (acceptor-port acceptor)
+                            :address (acceptor-address acceptor)
+                            :process-name (format nil "Hunchentoot listener \(~A:~A)"
+                                                  (or (acceptor-address acceptor) "*")
+                                                  (acceptor-port acceptor))
+                            ;; this function is called once on startup - we
+                            ;; use it to check for errors and random port
+                            :announce (lambda (socket &optional condition)
+                                        (when condition
+                                          (error condition))
+                                        (when (or (null (acceptor-port acceptor))
+                                                  (zerop (acceptor-port acceptor)))
+                                          (multiple-value-bind (address port)
+                                              (comm:get-socket-address socket)
+                                            (declare (ignore address))
+                                            (setf (slot-value acceptor 'port) port))))
+                            ;; this function is called whenever a connection
+                            ;; is made
+                            :function (lambda (handle)
+                                        (unless (acceptor-shutdown-p acceptor)
+                                          (handle-incoming-connection
+                                           (acceptor-taskmaster acceptor) handle)))
+                            ;; wait until the acceptor was successfully started
+                            ;; or an error condition is returned
+                            :wait t)
+    (when startup-condition
+      (error startup-condition))
+    (mp:process-stop listener-process)
+    (setf (acceptor-process acceptor) listener-process)
+    (values)))
+
+#+:lispworks
+(defmethod accept-connections ((acceptor acceptor))
+  (mp:process-unstop (acceptor-process acceptor))
+  nil)
+
+(defmethod acceptor-dispatch-request ((acceptor acceptor) request)
+  "Detault implementation of the request dispatch method, generates an
++http-not-found+ error."
+  (let ((path (and (acceptor-document-root acceptor)
+                   (request-pathname request))))
+    (cond
+      (path
+       (handle-static-file
+        (merge-pathnames (if (equal "/" (script-name request)) #p"index.html" path)
+                         (acceptor-document-root acceptor))))
+      (t
+       (setf (return-code *reply*) +http-not-found+)
+       (abort-request-handler)))))
+
+(defmethod handle-request ((*acceptor* acceptor) (*request* request))
+  "Standard method for request handling.  Calls the request dispatcher
+of *ACCEPTOR* to determine how the request should be handled.  Also
+sets up standard error handling which catches any errors within the
+handler."
+  (handler-bind ((error
+                  (lambda (cond)
+                    ;; if the headers were already sent, the error
+                    ;; happened within the body and we have to close
+                    ;; the stream
+                    (when *headers-sent*
+                      (setq *finish-processing-socket* t))
+                    (throw 'handler-done
+                      (values nil cond (get-backtrace))))))
+    (with-debugger
+      (acceptor-dispatch-request *acceptor* *request*))))
+
+(defgeneric acceptor-status-message (acceptor http-status-code &key &allow-other-keys)
+  (:documentation
+   "This function is called after the request's handler has been
+   invoked to convert the HTTP-STATUS-CODE to a HTML message to be
+   displayed to the user.  If this function returns a string, that
+   string is sent to the client instead of the content produced by the
+   handler, if any.
+
+   If an ERROR-TEMPLATE-DIRECTORY is set in the current acceptor and
+   the directory contains a file corresponding to HTTP-STATUS-CODE
+   named <code>.html, that file is sent to the client after variable
+   substitution.  Variables are referenced by ${<variable-name>}.
+
+   Additional keyword arguments may be provided which are made
+   available to the templating logic as substitution variables.  These
+   variables can be interpolated into error message templates in,
+   which contains the current URL relative to the server and without
+   GET parameters.
+
+   In addition to the variables corresponding to keyword arguments,
+   the script-name, lisp-implementation-type,
+   lisp-implementation-version and hunchentoot-version variables are
+   available."))
+
+(defun make-cooked-message (http-status-code &key error backtrace)
+  (labels ((cooked-message (format &rest arguments)
+             (setf (content-type*) "text/html; charset=iso-8859-1")
+             (format nil "<html><head><title>~D ~A</title></head><body><h1>~:*~A</h1>~?<p><hr>~A</p></body></html>"
+                     http-status-code (reason-phrase http-status-code)
+                     format (mapcar (lambda (arg)
+                                      (if (stringp arg)
+                                          (escape-for-html arg)
+                                          arg))
+                                    arguments)
+                     (address-string))))
+    (case http-status-code
+      ((#.+http-moved-temporarily+
+        #.+http-moved-permanently+)
+       (cooked-message "The document has moved <a href='~A'>here</a>" (header-out :location)))
+      ((#.+http-authorization-required+)
+       (cooked-message "The server could not verify that you are authorized to access the document requested.  ~
+                        Either you supplied the wrong credentials \(e.g., bad password), or your browser doesn't ~
+                        understand how to supply the credentials required."))
+      ((#.+http-forbidden+)
+       (cooked-message "You don't have permission to access ~A on this server."
+                       (script-name *request*)))
+      ((#.+http-not-found+)
+       (cooked-message "The requested URL ~A was not found on this server."
+                       (script-name *request*)))
+      ((#.+http-bad-request+)
+       (cooked-message "Your browser sent a request that this server could not understand."))
+      ((#.+http-internal-server-error+)
+       (if *show-lisp-errors-p*
+           (cooked-message "<pre>~A~@[~%~%Backtrace:~%~%~A~]</pre>"
+                           (escape-for-html (princ-to-string error))
+                           (when *show-lisp-backtraces-p*
+                             (escape-for-html (princ-to-string backtrace))))
+           (cooked-message "An error has occurred")))
+      (t
+         (when (<= 400 http-status-code)
+           (cooked-message "An error has occurred"))))))
+
+(defmethod acceptor-status-message ((acceptor t) http-status-code &rest args &key &allow-other-keys)
+  (apply 'make-cooked-message http-status-code args))
+
+(defmethod acceptor-status-message :around ((acceptor acceptor) http-status-code &rest args &key &allow-other-keys)
+  (handler-case
+      (call-next-method)
+    (error (e)
+      (log-message* :error "error ~A during error processing, sending cooked message to client" e)
+      (apply 'make-cooked-message http-status-code args))))
+
+(defun string-as-keyword (string)
+  "Intern STRING as keyword using the reader so that case conversion is done with the reader defaults."
+  (let ((*package* (find-package :keyword)))
+    (read-from-string string)))
+
+(defmethod acceptor-status-message ((acceptor acceptor) http-status-code &rest properties &key &allow-other-keys)
+  "Default function to generate error message sent to the client."
+  (labels
+      ((substitute-request-context-variables (string)
+         (let ((properties (append `(:script-name ,(script-name*)
+                                     :lisp-implementation-type ,(lisp-implementation-type)
+                                     :lisp-implementation-version ,(lisp-implementation-version)
+                                     :hunchentoot-version ,*hunchentoot-version*)
+                                   properties)))
+           (unless *show-lisp-backtraces-p*
+             (setf (getf properties :backtrace) nil))
+           (cl-ppcre:regex-replace-all "(?i)\\$\\{([a-z0-9-_]+)\\}"
+                                       string
+                                       (lambda (target-string start end match-start match-end reg-starts reg-ends)
+                                         (declare (ignore start end match-start match-end))
+                                         (let ((variable-name (string-as-keyword (subseq target-string
+                                                                                         (aref reg-starts 0)
+                                                                                         (aref reg-ends 0)))))
+                                           (escape-for-html (princ-to-string (getf properties variable-name variable-name))))))))
+       (file-contents (file)
+         (let ((buf (make-string (file-length file))))
+           (read-sequence buf file)
+           buf))
+       (error-contents-from-template ()
+         (let ((error-file-template-pathname (and (acceptor-error-template-directory acceptor)
+                                                  (probe-file (make-pathname :name (princ-to-string http-status-code)
+                                                                             :type "html"
+                                                                             :defaults (acceptor-error-template-directory acceptor))))))
+           (when error-file-template-pathname
+             (with-open-file (file error-file-template-pathname :if-does-not-exist nil :element-type 'character)
+               (when file
+                 (setf (content-type*) "text/html")
+                 (substitute-request-context-variables (file-contents file))))))))
+    (or (unless (< 300 http-status-code)
+          (call-next-method))              ; don't ever try template for positive return codes
+        (when *show-lisp-errors-p*
+          (error-contents-from-template))  ; try template
+        (call-next-method))))              ; fall back to cooked message
+
+(defgeneric acceptor-remove-session (acceptor session)
+  (:documentation
+   "This function is called whenever a session in ACCEPTOR is being
+   destroyed because of a session timout or an explicit REMOVE-SESSION
+   call."))
+
+(defmethod acceptor-remove-session ((acceptor acceptor) (session t))
+  "Default implementation for the session removal hook function.  This
+function is called whenever a session is destroyed."
+  nil)
+
+(defgeneric acceptor-server-name (acceptor)
+  (:documentation "Returns a string which can be used for 'Server' headers.")
+  (:method ((acceptor acceptor))
+    (format nil "Hunchentoot ~A" *hunchentoot-version*)))
diff --git a/deps/hunchentoot/compat.lisp b/deps/hunchentoot/compat.lisp
new file mode 100644 (file)
index 0000000..79376fa
--- /dev/null
@@ -0,0 +1,136 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defmacro when-let ((var form) &body body)
+  "Evaluates FORM and binds VAR to the result, then executes BODY
+if VAR has a true value."
+  `(let ((,var ,form))
+     (when ,var ,@body)))
+
+(defmacro with-unique-names ((&rest bindings) &body body)
+  "Syntax: WITH-UNIQUE-NAMES ( { var | (var x) }* ) declaration* form*
+
+Executes a series of forms with each VAR bound to a fresh,
+uninterned symbol. The uninterned symbol is as if returned by a call
+to GENSYM with the string denoted by X - or, if X is not supplied, the
+string denoted by VAR - as argument.
+
+The variable bindings created are lexical unless special declarations
+are specified. The scopes of the name bindings and declarations do not
+include the Xs.
+
+The forms are evaluated in order, and the values of all but the last
+are discarded \(that is, the body is an implicit PROGN)."
+  ;; reference implementation posted to comp.lang.lisp as
+  ;; <cy3bshuf30f.fsf@ljosa.com> by Vebjorn Ljosa - see also
+  ;; <http://www.cliki.net/Common%20Lisp%20Utilities>
+  `(let ,(mapcar #'(lambda (binding)
+                     (check-type binding (or cons symbol))
+                     (if (consp binding)
+                       (destructuring-bind (var x) binding
+                         (check-type var symbol)
+                         `(,var (gensym ,(etypecase x
+                                          (symbol (symbol-name x))
+                                          (character (string x))
+                                          (string x)))))
+                       `(,binding (gensym ,(symbol-name binding)))))
+                 bindings)
+         ,@body))
+
+(defmacro with-rebinding (bindings &body body)
+  "Syntax: WITH-REBINDING ( { var | (var prefix) }* ) form*
+
+Evaluates a series of forms in the lexical environment that is
+formed by adding the binding of each VAR to a fresh, uninterned
+symbol, and the binding of that fresh, uninterned symbol to VAR's
+original value, i.e., its value in the current lexical environment.
+
+The uninterned symbol is created as if by a call to GENSYM with the
+string denoted by PREFIX - or, if PREFIX is not supplied, the string
+denoted by VAR - as argument.
+
+The forms are evaluated in order, and the values of all but the last
+are discarded \(that is, the body is an implicit PROGN)."
+  ;; reference implementation posted to comp.lang.lisp as
+  ;; <cy3wv0fya0p.fsf@ljosa.com> by Vebjorn Ljosa - see also
+  ;; <http://www.cliki.net/Common%20Lisp%20Utilities>
+  (loop for binding in bindings
+        for var = (if (consp binding) (car binding) binding)
+        for name = (gensym)
+        collect `(,name ,var) into renames
+        collect ``(,,var ,,name) into temps
+        finally (return `(let ,renames
+                          (with-unique-names ,bindings
+                            `(let (,,@temps)
+                              ,,@body))))))
+
+(defun get-peer-address-and-port (socket)
+  "Returns the peer address and port of the socket SOCKET as two
+values.  The address is returned as a string in dotted IP address
+notation."
+  (multiple-value-bind (address port) (usocket:get-peer-name socket)
+    (values (ecase (length address)
+              (4 (usocket:vector-quad-to-dotted-quad address))
+              #+(or) (16 (usocket:vector-to-ipv6-host address)))
+            port)))
+
+(defun get-local-address-and-port (socket)
+  "Returns the local address and port of the socket SOCKET as two
+values.  The address is returned as a string in dotted IP address
+notation."
+  (multiple-value-bind (address port) (usocket:get-local-name socket)
+    (values (ecase (length address)
+              (4 (usocket:vector-quad-to-dotted-quad address))
+              #+(or) (16 (usocket:vector-to-ipv6-host address)))
+            port)))
+
+(defun make-socket-stream (socket acceptor)
+  "Returns a stream for the socket SOCKET.  The ACCEPTOR argument is
+ignored."
+  (declare (ignore acceptor))
+  (usocket:socket-stream socket))
+
+(defun make-lock (name)
+  "Simple wrapper to allow LispWorks and Bordeaux Threads to coexist."
+  (bt:make-lock name))
+
+(defmacro with-lock-held ((lock) &body body)
+  "Simple wrapper to allow LispWorks and Bordeaux Threads to coexist."
+  `(bt:with-lock-held (,lock) ,@body))
+
+(defun make-condition-variable (&key name)
+  (declare (ignore name))
+  (bt:make-condition-variable))
+
+(defun condition-variable-signal (condition-variable)
+  (bt:condition-notify condition-variable))
+
+(defun condition-variable-wait (condition-variable lock)
+  (bt:condition-wait condition-variable lock))
diff --git a/deps/hunchentoot/conditions.lisp b/deps/hunchentoot/conditions.lisp
new file mode 100644 (file)
index 0000000..1147e24
--- /dev/null
@@ -0,0 +1,133 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2008-2009, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(define-condition hunchentoot-condition (condition)
+  ()
+  (:documentation "Superclass for all conditions related to Hunchentoot."))
+
+(define-condition hunchentoot-error (hunchentoot-condition error)
+  ()
+  (:documentation "Superclass for all errors related to Hunchentoot."))
+
+(define-condition hunchentoot-simple-error (hunchentoot-error simple-condition)
+  ()
+  (:documentation "Like HUNCHENTOOT-ERROR but with formatting capabilities."))
+
+(defun hunchentoot-error (format-control &rest format-arguments)
+  "Signals an error of type HUNCHENTOOT-SIMPLE-ERROR with the provided
+format control and arguments."
+  (error 'hunchentoot-simple-error
+         :format-control format-control
+         :format-arguments format-arguments))
+
+(define-condition hunchentoot-warning (hunchentoot-condition warning)
+  ()
+  (:documentation "Superclass for all warnings related to Hunchentoot."))
+
+(define-condition hunchentoot-simple-warning (hunchentoot-warning simple-condition)
+  ()
+  (:documentation "Like HUNCHENTOOT-WARNING but with formatting capabilities."))
+
+(defun hunchentoot-warn (format-control &rest format-arguments)
+  "Signals a warning of type HUNCHENTOOT-SIMPLE-WARNING with the
+provided format control and arguments."
+  (warn 'hunchentoot-simple-warning
+        :format-control format-control
+        :format-arguments format-arguments))
+
+(define-condition parameter-error (hunchentoot-simple-error)
+  ()
+  (:documentation "Signalled if a function was called with incosistent or illegal parameters."))
+
+(defun parameter-error (format-control &rest format-arguments)
+  "Signals an error of type PARAMETER-ERROR with the provided
+format control and arguments."
+  (error 'parameter-error
+         :format-control format-control
+         :format-arguments format-arguments))
+
+(define-condition operation-not-implemented (hunchentoot-error)
+  ((operation :initarg :operation
+              :reader hunchentoot-operation-not-implemented-operation
+              :documentation "The name of the unimplemented operation."))
+  (:report (lambda (condition stream)
+             (format stream "The operation ~A is not yet implemented for the implementation ~A.
+Consider sending a patch..."
+                     (hunchentoot-operation-not-implemented-operation condition)
+                     (lisp-implementation-type))))
+  (:documentation "This warning is signalled when an operation \(like
+SETUID for example) is not implemented for a specific Lisp."))
+
+(defun not-implemented (name)
+  "Used to signal an error if an operation named NAME is not implemented."
+  (error 'operation-not-implemented :operation name))
+
+(define-condition bad-request (hunchentoot-error)
+  ())
+
+;;;
+
+(defgeneric maybe-invoke-debugger (condition)
+  (:documentation "This generic function is called whenever a
+condition CONDITION is signaled in Hunchentoot.  You might want to
+specialize it on specific condition classes for debugging purposes.")
+  (:method (condition)
+   "The default method invokes the debugger with CONDITION if
+*CATCH-ERRORS-P* is NIL."
+   (unless *catch-errors-p*
+     (invoke-debugger condition))))
+
+(defmacro with-debugger (&body body)
+  "Executes BODY and invokes the debugger if an error is signaled and
+*CATCH-ERRORS-P* is NIL."
+  `(handler-bind ((bad-request (lambda (c)
+                                 (declare (ignore c))
+                                 (setf (return-code *reply*) +http-bad-request+)
+                                 (abort-request-handler)))
+                  (error #'maybe-invoke-debugger))
+     ,@body))
+
+(defmacro ignore-errors* (&body body)
+  "Like IGNORE-ERRORS, but observes *CATCH-ERRORS-P*."
+  `(ignore-errors (with-debugger ,@body)))
+       
+(defmacro handler-case* (expression &rest clauses)
+  "Like HANDLER-CASE, but observes *CATCH-ERRORS-P*."
+  `(handler-case (with-debugger ,expression)
+     ,@clauses))
+
+(defun get-backtrace ()
+  "Returns a string with a backtrace of what the Lisp system thinks is
+the \"current\" error."
+  (handler-case
+      (with-output-to-string (s)
+        (trivial-backtrace:print-backtrace-to-stream s))
+    (error (condition)
+      (format nil "Could not generate backtrace: ~A." condition))))
diff --git a/deps/hunchentoot/cookie.lisp b/deps/hunchentoot/cookie.lisp
new file mode 100644 (file)
index 0000000..9315316
--- /dev/null
@@ -0,0 +1,127 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defclass cookie ()
+  ((name :initarg :name
+         :reader cookie-name
+         :type string
+         :documentation "The name of the cookie - a string.")
+   (value :initarg :value
+          :accessor cookie-value
+          :initform ""
+          :documentation "The value of the cookie. Will be URL-encoded
+when sent to the browser.")
+   (expires :initarg :expires
+            :initform nil
+            :accessor cookie-expires
+            :documentation "The time \(a universal time) when the
+cookie expires \(or NIL).")
+   (max-age :initarg :max-age
+            :initform nil
+            :accessor cookie-max-age
+            :documentation "The time delta \(in seconds) after which the
+cookie expires \(or NIL).")
+   (path :initarg :path
+         :initform nil
+         :accessor cookie-path
+         :documentation "The path this cookie is valid for \(or NIL).")
+   (domain :initarg :domain
+           :initform nil
+           :accessor cookie-domain
+           :documentation "The domain this cookie is valid for \(or NIL).")
+   (secure :initarg :secure
+           :initform nil
+           :accessor cookie-secure
+           :documentation "A generalized boolean denoting whether this
+cookie is a secure cookie.")
+   (http-only :initarg :http-only
+              :initform nil
+              :accessor cookie-http-only
+              :documentation "A generalized boolean denoting whether
+this cookie is a `HttpOnly' cookie.
+
+This is a Microsoft extension that has been implemented in Firefox as
+well. See <http://msdn2.microsoft.com/en-us/library/ms533046.aspx>."))
+  (:documentation "Each COOKIE objects describes one outgoing cookie."))
+
+(defmethod initialize-instance :around ((cookie cookie) &rest init-args)
+  "Ensure COOKIE has a correct slot-value for NAME."
+  (let ((name (getf init-args :name)))
+    (unless (http-token-p name)
+      (parameter-error "~S is not a legal name for a cookie." name)))
+  (call-next-method))
+
+(defun set-cookie* (cookie &optional (reply *reply*))
+  "Adds the COOKIE object COOKIE to the outgoing cookies of the
+REPLY object REPLY. If a cookie with the same name
+\(case-sensitive) already exists, it is replaced."
+  (let* ((name (cookie-name cookie))
+         (place (assoc name (cookies-out reply) :test #'string=)))
+    (cond
+      (place
+        (setf (cdr place) cookie))
+      (t
+        (push (cons name cookie) (cookies-out reply))
+        cookie))))
+
+(defun set-cookie (name &key (value "") expires max-age path domain secure http-only (reply *reply*))
+  "Creates a cookie object from the parameters provided and adds
+it to the outgoing cookies of the REPLY object REPLY. If a cookie
+with the name NAME \(case-sensitive) already exists, it is
+replaced."
+  (set-cookie* (make-instance 'cookie
+                              :name name
+                              :value value
+                              :expires expires
+                              :max-age max-age
+                              :path path
+                              :domain domain
+                              :secure secure
+                              :http-only http-only)
+               reply))
+
+(defun cookie-date (universal-time)
+  "Converts UNIVERSAL-TIME to cookie date format."
+  (and universal-time
+       (rfc-1123-date universal-time)))
+
+(defmethod stringify-cookie ((cookie cookie))
+  "Converts the COOKIE object COOKIE to a string suitable for a
+'Set-Cookie' header to be sent to the client."
+  (format nil
+          "~A=~A~@[; Expires=~A~]~@[; Max-Age=~A~]~@[; Domain=~A~]~@[; Path=~A~]~:[~;; Secure~]~:[~;; HttpOnly~]"
+          (cookie-name cookie)
+          (cookie-value cookie)
+          (cookie-date (cookie-expires cookie))
+          (cookie-max-age cookie)
+          (cookie-domain cookie)
+          (cookie-path cookie)
+          (cookie-secure cookie)
+          (cookie-http-only cookie)))
diff --git a/deps/hunchentoot/doc/LICENSE.txt b/deps/hunchentoot/doc/LICENSE.txt
new file mode 100644 (file)
index 0000000..37c6051
--- /dev/null
@@ -0,0 +1,9 @@
+The Hunchentoot logo (the file `hunchentoot.gif' in this directory)
+was created by Uwe von Loh and is available from his website at
+
+  http://www.htg1.de/hunchentoot/hunchentoot.html
+
+It is licensed under a `Creative Commons Attribution-Share Alike 2.0
+Germany License', see
+
+  http://creativecommons.org/licenses/by-sa/2.0/de/
diff --git a/deps/hunchentoot/doc/Makefile b/deps/hunchentoot/doc/Makefile
new file mode 100644 (file)
index 0000000..336129b
--- /dev/null
@@ -0,0 +1,3 @@
+
+all:
+       xsltproc --stringparam library-version `perl -ne 'print "$$1\n" if (/:version "(.*)"/)' ../hunchentoot.asd` clixdoc.xsl index.xml > ../www/hunchentoot-doc.html
diff --git a/deps/hunchentoot/doc/clixdoc.xsl b/deps/hunchentoot/doc/clixdoc.xsl
new file mode 100644 (file)
index 0000000..3718550
--- /dev/null
@@ -0,0 +1,466 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+
+<!--
+;;; Copyright (c) 2008, Hans Hübner.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<xsl:stylesheet
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+   version="1.0"
+   xmlns:clix="http://bknr.net/clixdoc"
+   exclude-result-prefixes="clix">
+
+  <xsl:output method="html"
+              indent="yes"
+              omit-xml-declaration="yes"
+              doctype-public="-//W3C//DTD HTML 4.0 Strict//EN" />
+
+  <xsl:key name="index-entries" match="clix:*[@name and (name() != 'clix:chapter') and (name() != 'clix:subchapter')]" use="@name" />
+
+  <xsl:template match="/clix:documentation">
+    <html xmlns="http://www.w3.org/1999/xhtml">
+      <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+        <title><xsl:value-of select="clix:title"/></title>
+        <meta name="description"><xsl:attribute name="content"><xsl:value-of select="clix:short-description"/></xsl:attribute></meta>
+        <style type="text/css">
+  body { background-color: #ffffff }
+  pre { padding:5px; background-color:#e0e0e0 }
+  pre.none { padding:5px; background-color:#ffffff }
+  h3, h4, h5 { text-decoration: underline; }
+  .entry-type { padding-left: 1em; font-size: 60%; font-style: italic }
+  a { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:visited { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; } 
+  a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; }
+  a.none { text-decoration: none; padding: 0; }
+  a.none:visited { text-decoration: none; padding: 0; } 
+  a.none:hover { text-decoration: none; border: none; padding: 0; } 
+  a.none:focus { text-decoration: none; border: none; padding: 0; } 
+  a.noborder { text-decoration: none; padding: 0; } 
+  a.noborder:visited { text-decoration: none; padding: 0; } 
+  a.noborder:hover { text-decoration: none; border: none; padding: 0; } 
+  a.noborder:focus { text-decoration: none; border: none; padding: 0; }  
+        </style>
+      </head>
+      <body>
+        <xsl:apply-templates/>
+      </body>
+    </html>
+  </xsl:template>
+
+  <xsl:template match="clix:library-version">
+    <xsl:value-of select="$library-version"/>
+  </xsl:template>
+
+  <xsl:template match="clix:title"/>
+  <xsl:template match="clix:short-description"/>
+
+  <xsl:template match="clix:function">
+    <p>
+      <xsl:call-template name="make-anchor"/>
+      <xsl:choose>
+        <xsl:when test="clix:special-definition">
+          <xsl:apply-templates select="clix:special-definition"/>
+        </xsl:when>
+        <xsl:otherwise>
+          [<xsl:call-template name="nice-entry-type-name"/>]
+          <br/>
+          <xsl:call-template name="render-title"/>
+          <xsl:value-of select="' '"/>
+          <i><xsl:apply-templates select="clix:lambda-list"/></i>
+          <xsl:if test="clix:returns">
+            =&gt;
+            <i><xsl:apply-templates select="clix:returns"/></i>
+          </xsl:if>
+        </xsl:otherwise>
+      </xsl:choose>
+      <blockquote>
+        <xsl:apply-templates select="clix:description"/>
+      </blockquote>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="clix:reader">
+    <p>
+      <xsl:call-template name="make-anchor"/>
+      [<xsl:call-template name="nice-entry-type-name"/>]
+      <br/>
+      <xsl:call-template name="render-title"/>
+      <xsl:value-of select="' '"/>
+      <i><xsl:apply-templates select="clix:lambda-list"/></i>
+      <xsl:if test="clix:returns">
+        =&gt;
+        <i><xsl:apply-templates select="clix:returns"/></i>
+      </xsl:if>
+      <blockquote>
+        <xsl:apply-templates select="clix:description"/>
+      </blockquote>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="clix:listed-reader">
+      <xsl:call-template name="make-anchor"/>
+      <xsl:call-template name="render-title"/>
+      <xsl:value-of select="' '"/>
+      <i><xsl:apply-templates select="clix:lambda-list"/></i>
+      <xsl:if test="clix:returns">
+        =&gt;
+        <i><xsl:apply-templates select="clix:returns"/></i>
+      </xsl:if>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="clix:writer">
+    <p>
+      <xsl:call-template name="make-anchor"/>
+      [<xsl:call-template name="nice-entry-type-name"/>]
+      <br/>
+      <tt>(setf (</tt><b><xsl:value-of select="@name"/></b>
+      <xsl:value-of select="' '"/>
+      <i><xsl:apply-templates select="clix:lambda-list"/></i><tt>) <i>new-value</i>)</tt>
+      <xsl:if test="clix:returns">
+        =&gt;
+        <i><xsl:apply-templates select="clix:returns"/></i>
+      </xsl:if>
+      <blockquote>
+        <xsl:apply-templates select="clix:description"/>
+      </blockquote>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="clix:accessor">
+    <p>
+      <xsl:call-template name="make-anchor"/>
+      [<xsl:call-template name="nice-entry-type-name"/>]
+      <br/>
+      <xsl:call-template name="render-title"/>
+      <xsl:value-of select="' '"/>
+      <i><xsl:apply-templates select="clix:lambda-list"/></i>
+      =&gt;
+      <i><xsl:apply-templates select="clix:returns"/></i>
+      <br/>
+      <tt>(setf (</tt><b><xsl:value-of select="@name"/></b>
+      <xsl:value-of select="' '"/>
+      <i><xsl:apply-templates select="clix:lambda-list"/></i><tt>) <i>new-value</i>)</tt>
+      <blockquote>
+        <xsl:apply-templates select="clix:description"/>
+      </blockquote>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="clix:listed-accessor">
+      <xsl:call-template name="make-anchor"/>
+      <xsl:call-template name="render-title"/>
+      <xsl:value-of select="' '"/>
+      <i><xsl:apply-templates select="clix:lambda-list"/></i>
+      =&gt;
+      <i><xsl:apply-templates select="clix:returns"/></i>
+      <br/>
+      <tt>(setf (</tt><b><xsl:value-of select="@name"/></b>
+      <xsl:value-of select="' '"/>
+      <i><xsl:apply-templates select="clix:lambda-list"/></i><tt>) <i>new-value</i>)</tt>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="clix:special-variable | clix:class | clix:condition | clix:symbol | clix:constant">
+    <p>
+      <xsl:call-template name="make-anchor"/>
+      [<xsl:call-template name="nice-entry-type-name"/>]
+      <br/>
+      <xsl:call-template name="render-title"/>
+      <blockquote>
+        <xsl:apply-templates select="clix:description"/>
+      </blockquote>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="clix:listed-constant">
+    <xsl:call-template name="render-title"/>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="clix:constants">
+    <!-- Display a list of constants with a common description -->
+    <p>
+      [Constants]<br/>
+      <xsl:apply-templates select="clix:listed-constant"/>
+      <blockquote>
+        <xsl:apply-templates select="clix:description"/>
+      </blockquote>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="clix:readers">
+    <!-- Display a list of readers with a common description -->
+    <p>
+      [<xsl:call-template name="nice-entry-type-name"/>]<br/>
+      <xsl:apply-templates select="clix:listed-reader"/>
+      <blockquote>
+        <xsl:apply-templates select="clix:description"/>
+      </blockquote>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="clix:accessors">
+    <!-- Display a list of accessors with a common description -->
+    <p>
+      [<xsl:call-template name="nice-entry-type-name"/>]<br/>
+      <xsl:apply-templates select="clix:listed-accessor"/>
+      <blockquote>
+        <xsl:apply-templates select="clix:description"/>
+      </blockquote>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="clix:qualifier">
+    <!-- method qualifier -->
+    <tt><xsl:value-of select="text()"/></tt>
+  </xsl:template>
+
+  <xsl:template match="clix:lkw">
+    <!-- lambda list keyword -->
+    <tt>&#38;<xsl:value-of select="text()"/></tt>
+  </xsl:template>
+
+  <xsl:template match="clix:arg">
+    <!-- argument reference -->
+    <code><i><xsl:value-of select="text()"/></i></code>
+  </xsl:template>
+
+  <xsl:template match="clix:ref">
+    <xsl:call-template name="internal-reference">
+      <xsl:with-param name="name"><xsl:value-of select="."/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="clix:chapter">
+    <h3>
+      <a class="none">
+        <xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
+        <xsl:value-of select="@title"/>
+      </a>
+    </h3>
+    <xsl:apply-templates/>
+  </xsl:template>
+
+  <xsl:template match="clix:subchapter">
+    <h4>
+      <a>
+        <xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
+        <xsl:value-of select="@title"/>
+      </a>
+    </h4>
+    <xsl:apply-templates/>
+  </xsl:template>
+
+  <xsl:template match="clix:contents">
+    <ol>
+      <xsl:for-each select="//clix:chapter">
+        <li>
+          <a>
+            <xsl:attribute name="href">#<xsl:value-of select="@name"/></xsl:attribute>
+            <xsl:value-of select="@title"/>
+          </a>
+          <xsl:if test="clix:subchapter">
+            <ol>
+              <xsl:for-each select="clix:subchapter">
+                <li>
+                  <a>
+                    <xsl:attribute name="href">#<xsl:value-of select="@name"/></xsl:attribute>
+                    <xsl:value-of select="@title"/>
+                  </a>
+                </li>
+              </xsl:for-each>
+            </ol>
+          </xsl:if>
+        </li>
+      </xsl:for-each>
+    </ol>
+  </xsl:template>
+
+  <xsl:template match="clix:index">
+    <ul>
+      <xsl:for-each select="//clix:*[generate-id(.) = generate-id(key('index-entries', @name)[1])]">
+        <xsl:sort select="@name"/>
+        <li>
+          <xsl:choose>
+            <xsl:when test="count(key('index-entries', @name)) = 1">
+              <xsl:call-template name="internal-reference">
+                <xsl:with-param name="name"><xsl:value-of select="@name"/></xsl:with-param>
+              </xsl:call-template>
+              <span class="entry-type"><xsl:call-template name="nice-entry-type-name"/></span>
+            </xsl:when>
+            <xsl:otherwise>
+              <a>
+                <xsl:attribute name="name">
+                  <xsl:value-of select="@name"/>
+                </xsl:attribute>
+              </a>
+              <xsl:value-of select="@name"/>
+              <ul>
+                <xsl:for-each select="key('index-entries', @name)">
+                  <xsl:sort select="name()"/>
+                  <li>
+                    <xsl:call-template name="internal-reference">
+                      <xsl:with-param name="name"><xsl:call-template name="make-anchor-name"/></xsl:with-param>
+                      <xsl:with-param name="title"><xsl:value-of select="@name"/></xsl:with-param>
+                    </xsl:call-template>
+                    <span class="entry-type"><xsl:call-template name="nice-entry-type-name"/></span>
+                  </li>
+                </xsl:for-each>
+              </ul>
+            </xsl:otherwise>
+          </xsl:choose>
+        </li>
+      </xsl:for-each>
+    </ul>
+  </xsl:template>
+
+  <xsl:template match="*">
+    <xsl:copy>
+      <xsl:copy-of select="@*[not(namespace-uri())]"/>
+      <xsl:apply-templates/>
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template name="internal-reference">
+    <!-- internal reference -->
+    <xsl:param name="name"/>
+    <xsl:param name="title"/>
+    <code>
+      <a>
+        <xsl:attribute name="href">
+          #<xsl:value-of select="translate($name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
+        </xsl:attribute>
+        <xsl:choose>
+          <xsl:when test="$title != ''">
+            <xsl:value-of select="$title"/>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="$name"/>
+          </xsl:otherwise>
+        </xsl:choose>
+      </a>
+    </code>
+  </xsl:template>
+
+  <xsl:template name="make-anchor-name">
+    <xsl:choose>
+      <xsl:when test="count(key('index-entries', @name)) = 1">
+        <xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>-<xsl:value-of select="substring(name(), 6)"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template name="make-anchor">
+    <a class="none">
+      <xsl:attribute name="name">
+        <xsl:call-template name="make-anchor-name"/>
+      </xsl:attribute>
+    </a>
+  </xsl:template>
+
+  <xsl:template name="render-title">
+    <b><xsl:value-of select="@name"/></b>
+  </xsl:template>
+
+  <xsl:template name="nice-entry-type-name">
+    <xsl:choose>
+      <xsl:when test="name() = 'clix:function'">
+        <xsl:choose>
+          <xsl:when test="@generic = 'true'">Generic function</xsl:when>
+          <xsl:when test="@specialized = 'true'">Method</xsl:when>
+          <xsl:when test="@macro = 'true'">Macro</xsl:when>
+          <xsl:otherwise>Function</xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="name() = 'clix:reader'">
+        <xsl:choose>
+          <xsl:when test="@generic = 'true'">Generic reader</xsl:when>
+          <xsl:when test="@specialized = 'true'">Specialized reader</xsl:when>
+          <xsl:otherwise>Reader</xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="name() = 'clix:listed-reader'">
+        <xsl:choose>
+          <xsl:when test="@generic = 'true'">Generic reader</xsl:when>
+          <xsl:when test="@specialized = 'true'">Specialized reader</xsl:when>
+          <xsl:otherwise>Reader</xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="name() = 'clix:readers'">
+        <xsl:choose>
+          <xsl:when test="@generic = 'true'">Generic readers</xsl:when>
+          <xsl:when test="@specialized = 'true'">Specialized readers</xsl:when>
+          <xsl:otherwise>Readers</xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="name() = 'clix:accessors'">
+        <xsl:choose>
+          <xsl:when test="@generic = 'true'">Generic accessors</xsl:when>
+          <xsl:when test="@specialized = 'true'">Specialized accessors</xsl:when>
+          <xsl:otherwise>Accessors</xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="name() = 'clix:writer'">
+        <xsl:choose>
+          <xsl:when test="@generic = 'true'">Generic writer</xsl:when>
+          <xsl:when test="@specialized = 'true'">Specialized writer</xsl:when>
+          <xsl:otherwise>Writer</xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="name() = 'clix:listed-accessor'">
+        <xsl:choose>
+          <xsl:when test="@generic = 'true'">Generic accessor</xsl:when>
+          <xsl:when test="@specialized = 'true'">Specialized accessor</xsl:when>
+          <xsl:otherwise>Accessor</xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="name() = 'clix:accessor'">
+        <xsl:choose>
+          <xsl:when test="@generic = 'true'">Generic accessor</xsl:when>
+          <xsl:when test="@specialized = 'true'">Specialized accessor</xsl:when>
+          <xsl:otherwise>Accessor</xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:when test="name() = 'clix:special-variable'">Special variable</xsl:when>
+      <xsl:when test="name() = 'clix:class'">Standard class</xsl:when>
+      <xsl:when test="name() = 'clix:condition'">Condition type</xsl:when>
+      <xsl:when test="name() = 'clix:symbol'">Symbol</xsl:when>
+      <xsl:when test="name() = 'clix:constant'">Constant</xsl:when>
+      <xsl:when test="name() = 'clix:listed-constant'">Constant</xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="name()" />
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/deps/hunchentoot/doc/hunchentoot.gif b/deps/hunchentoot/doc/hunchentoot.gif
new file mode 100644 (file)
index 0000000..6957435
Binary files /dev/null and b/deps/hunchentoot/doc/hunchentoot.gif differ
diff --git a/deps/hunchentoot/doc/index.xml b/deps/hunchentoot/doc/index.xml
new file mode 100644 (file)
index 0000000..5803a27
--- /dev/null
@@ -0,0 +1,3654 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml-stylesheet type="text/xsl" href="clixdoc.xsl" ?>
+
+<clix:documentation xmlns='http://www.w3.org/1999/xhtml' xmlns:clix='http://bknr.net/clixdoc'>
+  <clix:title>Hunchentoot - The Common Lisp web server formerly known as TBNL</clix:title>
+  <clix:short-description>
+    A full-featured web server written in Common Lisp offering things
+    like HTTP/1.1 chunking, persistent connections, and SSL.  Includes
+    a framework for building dynamic websites interactively.
+  </clix:short-description>
+
+  <h2>
+    <a href="http://www.htg1.de/hunchentoot/hunchentoot.html"
+       title="Click here for the Hunchentoot logo"
+       class="noborder">
+      <img align="top" width="93" height="45" border="0" src="hunchentoot.gif" />
+    </a>
+    Hunchentoot - The Common Lisp web server formerly known as TBNL
+  </h2>
+
+  <blockquote>
+    <clix:chapter name='abstract' title='Abstract'>
+      <p>
+        Hunchentoot is a web server written in Common Lisp and at the
+        same time a toolkit for building dynamic websites.  As a
+        stand-alone web server, Hunchentoot is capable of HTTP/1.1
+        chunking (both directions), persistent connections
+        (keep-alive), and SSL.
+      </p>
+      <p>
+        Hunchentoot provides facilities like automatic session
+        handling (with and without cookies), logging, customizable
+        error handling, and easy access to GET and POST parameters
+        sent by the client. It does <em>not</em> include functionality
+        to programmatically generate HTML output. For this task you
+        can use any library you like, e.g. (shameless self-plug)
+        <a href="http://weitz.de/cl-who/">CL-WHO</a> or
+        <a href="http://weitz.de/html-template/">HTML-TEMPLATE</a>.
+      </p>
+      <p>
+        Hunchentoot talks with its front-end or with the client over
+        TCP/IP sockets and optionally uses multiprocessing to handle
+        several requests at the same time.  Therefore, it cannot be
+        implemented completely in <a
+        href="http://www.lispworks.com/documentation/HyperSpec/Front/index.htm">portable
+        Common Lisp</a>.  It currently works with <a
+        href="http://www.lispworks.com/">LispWorks</a> and all Lisps
+        which are supported by the compatibility layers <a
+        href="http://common-lisp.net/project/usocket/">usocket</a> and
+        <a
+        href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux
+        Threads</a>.
+      </p>
+      <p>
+        Hunchentoot comes with a
+        <a href="http://www.opensource.org/licenses/bsd-license.php">BSD-style
+        license</a> so you can basically do with it whatever you want.
+      </p>
+      <p>
+        Hunchentoot is (or was) for example used by
+        <a href="http://quickhoney.com/">QuickHoney</a>,
+        <a href="http://www.city-farming.de/">City Farming</a>,
+        <a href="http://heikestephan.de/">Heike Stephan</a>.
+      </p>
+      <p>
+        <font color="red">Download shortcut:</font>
+        <a href="http://weitz.de/files/hunchentoot.tar.gz">http://weitz.de/files/hunchentoot.tar.gz</a>.
+      </p>
+    </clix:chapter>
+  </blockquote>
+
+  <clix:chapter name='contents' title='Contents'></clix:chapter>
+  <clix:contents></clix:contents>
+
+  <clix:chapter name="install" title="Download and installation">
+    Hunchentoot depends on a couple of other Lisp libraries which you'll need
+    to install first:
+    <ul>
+      <li>Pierre R. Mai's <a href="http://www.cliki.net/md5">MD5</a>,</li>
+      <li>Kevin Rosenberg's <a href="http://www.cliki.net/cl-base64">CL-BASE64</a>,</li>
+      <li>Janis Dzerins' <a href="http://common-lisp.net/project/rfc2388/">RFC2388</a>,</li>
+      <li>Peter Seibel's <a href="http://weitz.de/cl-fad/">CL-FAD</a>,</li>
+      <li>Gary King's <a href="http://common-lisp.net/project/trivial-backtrace/">trivial-backtrace</a>,</li>
+      <li>Erik Huelsmann's <a href="http://common-lisp.net/project/usocket">usocket</a> (unless you're using LispWorks),</li>
+      <li>Greg Pfeil's <a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux Threads</a> (unless you're using LispWorks),
+      </li>
+      <li>
+        David Lichteblau's <a href="http://common-lisp.net/project/cl-plus-ssl/">CL+SSL</a>
+        (unless you're using LispWorks),
+      </li>
+      <li>
+        and my own <a href="http://weitz.de/flexi-streams/">FLEXI-STREAMS</a> (0.12.0 or higher),
+        <a href="http://weitz.de/chunga/">Chunga</a> (1.0.0 or
+        higher), and <a href="http://weitz.de/cl-ppcre/">
+        CL-PPCRE</a> (plus
+        <a href="http://weitz.de/cl-who/">CL-WHO</a> for the <a href="#teen-age">example code</a>
+        and <a href="http://weitz.de/drakma/">Drakma</a> for the <a href="#testing">tests</a>).
+      </li>
+    </ul>
+
+    Make sure to use the <em>newest</em> versions of all of these
+    libraries (which might themselves depend on other libraries) - try
+    the repository versions if you're in doubt.  Note: You can compile
+    Hunchentoot without SSL support - and thus without the need to
+    have CL+SSL - if you add <code>:HUNCHENTOOT-NO-SSL</code> to
+    <a href="http://www.lispworks.com/documentation/HyperSpec/Body/v_featur.htm">
+      <code>*FEATURES*</code></a> <em>before</em> you compile it.
+    <p>
+      Hunchentoot will only work with Lisps where
+      the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#character_code">character
+      codes</a> of
+      all <a href="http://en.wikipedia.org/wiki/ISO/IEC_8859-1">Latin-1</a>
+      characters coincide with their
+      Unicode <a href="http://en.wikipedia.org/wiki/Code_point">code
+      points</a> (which is the case for all current implementations I
+      know).
+    </p>
+    <p>
+      Hunchentoot itself together with this documentation can be
+      downloaded from
+      <a href="http://weitz.de/files/hunchentoot.tar.gz">http://weitz.de/files/hunchentoot.tar.gz</a>.
+      The current version is <clix:library-version/>.
+    </p>
+    <p>
+      The preferred method to compile and load Hunchentoot is via <a
+      href="http://www.cliki.net/asdf">ASDF</a>.  If you want to avoid
+      downloading and installing all the dependencies manually, give
+      Zach Beane's excellent <a
+      href="http://www.quicklisp.org/">Quicklisp</a> system a try.
+    </p>
+    <p>
+      Hunchentoot and its dependencies can also be installed with <a
+      href="http://common-lisp.net/project/clbuild/">clbuild</a>.
+      There's also a port for <a
+      href="http://www.gentoo.org/proj/en/lisp/common-lisp/index.xml">Gentoo
+      Linux</a> thanks to Matthew Kennedy.
+    </p>
+    <p>
+      The current development version of Hunchentoot can be found
+      at <a href="https://github.com/edicl/hunchentoot">https://github.com/edicl/hunchentoot</a>.
+      If you want to send patches, please fork the github repository and send pull requests.
+    </p>
+
+    <clix:subchapter name="port80" title="Running Hunchentoot on port 80">
+
+      Hunchentoot does not come with code to help with running it on a
+      privileged port (i.e. port 80 or 443) on Unix-like operating
+      systems.  Modern Unix-like systems have specific, non-portable
+      ways to allow non-root users to listen to privileged ports, so
+      including such functionality in Hunchentoot was considered
+      unnecessary.  Please refer to online resources for help.  At the
+      time of this writing, the YAWS documentation has a <a
+      href="http://yaws.hyber.org/privbind.yaws">comprehensive
+      writeup</a> on the topic.
+    </clix:subchapter>
+
+    <clix:subchapter name="proxy" title="Hunchentoot behind a proxy">
+
+      If you're feeling unsecure about exposing Hunchentoot to the wild,
+      wild Internet or if your Lisp web application is part of a larger
+      website, you can hide it behind a
+      <a href="http://en.wikipedia.org/wiki/Proxy_server">proxy server</a>.
+      One approach that I have used several times is to employ Apache's
+      <a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html">mod_proxy</a>
+      module with a configuration that looks like this:
+
+<pre><a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypass" class="noborder">ProxyPass</a> /hunchentoot http://127.0.0.1:3000/hunchentoot
+<a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypassreverse" class="noborder">ProxyPassReverse</a> /hunchentoot http://127.0.0.1:3000/hunchentoot</pre>
+
+      This will tunnel all requests where the URI path begins with
+      <code>"/hunchentoot"</code> to a (Hunchentoot) server listening on
+      port 3000 on the same machine.
+
+      <p>
+        Of course, there are
+        <a href="http://www.red-bean.com/pipermail/lispweb/2006-October/001342.html">several
+          other</a> (more lightweight) web proxies that you could use
+        instead of Apache.
+      </p>
+    </clix:subchapter>
+  </clix:chapter>
+
+  <clix:chapter name="support" title="Support">
+    <p>
+      The development version of Hunchentoot can be found <a
+      href="https://github.com/edicl/hunchentoot" target="_new">on
+      github</a>.  Please use the github issue tracking system to
+      submit bug reports.  Patches are welcome, please use <a
+      href="https://github.com/edicl/hunchentoot/pulls">GitHub pull
+      requests</a>.  If you want to make a change, please <a
+      href="http://weitz.de/patches.html" target="_new">read this
+      first</a>.
+    </p>
+  </clix:chapter>
+
+  <clix:chapter name="teen-age" title="Your own webserver (the easy teen-age New York version)">
+    Starting your own web server is pretty easy.  Do something like this:
+<pre>(hunchentoot:<a class="noborder" href="#teen-age">start</a> (make-instance 'hunchentoot:<a class="noborder" href="#acceptor">easy-acceptor</a> :port 4242))</pre>
+    That's it.  Now you should be able to enter the address
+    "<a href='http://127.0.0.1:4242/'><code>http://127.0.0.1:4242/</code></a>" in
+    your browser and see something, albeit nothing very interesting
+    for now.
+
+    <p>
+      By default, Hunchentoot serves the files from the
+      <code><i>www/</i></code> directory in its source tree.  In the
+      distribution, that directory contains a HTML version of the
+      documentation as well as the error templates.  The location of
+      the document root directory can be specified when creating a new
+      <clix:ref>ACCEPTOR</clix:ref> instance by the way of the
+      <clix:ref>ACCEPTOR-DOCUMENT-ROOT</clix:ref>.  Likewise, the
+      location of the error template directory can be specified by the
+      <clix:ref>ACCEPTOR-ERROR-TEMPLATE-DIRECTORY</clix:ref>.  Both
+      <clix:ref>ACCEPTOR-DOCUMENT-ROOT</clix:ref> and
+      <clix:ref>ACCEPTOR-ERROR-TEMPLATE-DIRECTORY</clix:ref> can be
+      specified using a logical pathname, which will be translated
+      once when the <clix:ref>ACCEPTOR</clix:ref> is instantiated.
+    </p>
+
+    <p>
+      The <clix:ref>EASY-ACCEPTOR</clix:ref> class implements a
+      framework for developing web applications.  Handlers are defined
+      using the <clix:ref>DEFINE-EASY-HANDLER</clix:ref> macro.
+      Request dispatching is performed according to the list of
+      dispatch functions in <clix:ref>*DISPATCH-TABLE*</clix:ref>.
+      Each of the functions on that list is called to determine
+      whether it wants to handle the request, provided as single
+      argument.  If a dispatcher function wants to handle the request,
+      it returns another function to actually create the desired page.
+    </p>
+
+    <p>
+      <clix:ref>DEFINE-EASY-HANDLER</clix:ref> is accompanied by a set
+      of dispatcher creation functions that can be used to create
+      dispatchers for standard tasks.  These are documented in the <a
+      class="noborder" href="#easy-handlers">subchapter on easy
+      handlers</a>
+    </p>
+
+    <p>
+      Now be a bit more adventurous, try this
+<pre>(hunchentoot:<a class="noborder" href="#define-easy-handler">define-easy-handler</a> (say-yo :uri "/yo") (name)
+  (setf (hunchentoot:<a class="noborder" href="#content-type*">content-type*</a>) "text/plain")
+  (format nil "Hey~@[ ~A~]!" name))</pre>
+      and see what happens at "<a href='http://127.0.0.1:4242/yo'><code>http://127.0.0.1:4242/yo</code></a>" or
+      "<a href='http://127.0.0.1:4242/yo?name=Dude'><code>http://127.0.0.1:4242/yo?name=Dude</code></a>" .
+    </p>
+
+    <p>
+    Hunchentoot comes with a little example website which you can use
+    to see if it works and which should also demonstrate a couple of
+    the things you can do with Hunchentoot.  To start the example
+    website, enter the following code into your listener:
+
+<pre>(<a class="noborder" href="http://common-lisp.net/~mmommer/asdf-howto.shtml#sec11">asdf:oos</a> 'asdf:load-op :hunchentoot-test)</pre>
+
+    Now go to "<a href='http://127.0.0.1:4242/hunchentoot/test'><code>http://127.0.0.1:4242/hunchentoot/test</code></a>" and play a bit.
+    </p>
+  </clix:chapter>
+
+  <clix:chapter name="extras" title="Third party documentation and add-ons">
+    <p>
+      Adam Petersen has written a book called <a
+      href="http://www.adampetersen.se/articles/lispweb.htm">"Lisp for
+      the Web"</a> which explains how Hunchentoot and some other
+      libraries can be used to build web sites.
+    </p>
+    <p>
+      Here is some software which extends Hunchentoot or is based on it:
+    </p>
+    <ul>
+      <li>
+        <a href="http://weblocks-framework.info/">Weblocks</a> by
+        Slava Akhmechet is a "continuations-based web framework" which
+        is based on Hunchentoot.
+      </li>
+      <li>
+        <a href="https://github.com/slyrus/hunchentoot-cgi">hunchentoot-cgi</a>
+        (by Cyrus Harmon) provides
+        <a href="http://en.wikipedia.org/wiki/Common_Gateway_Interface">CGI</a>
+        handlers for Hunchentoot.
+      </li>
+      <li>
+        <a href="http://weitz.de/cl-webdav/">CL-WEBDAV</a> is a <a href="http://webdav.org/">WebDAV</a>
+        server based on Hunchentoot.
+      </li>
+      <li>
+        <a href="http://restas.lisper.ru/">RESTAS</a> is a web
+        framework based on Hunchentoot.
+      </li>
+    </ul>
+  </clix:chapter>
+
+  <clix:chapter name="reference" title="Function and variable reference">
+
+    <clix:subchapter name="acceptors" title="Acceptors">
+
+      If you want Hunchentoot to actually do something, you have to create and
+      <a href="#teen-age">start</a> an <a href="#acceptor">acceptor</a>.
+      You can also run several acceptors in one image, each one
+      listening on a different different port.
+
+      <clix:class name='acceptor'>
+        <clix:description>
+          To create a Hunchentoot webserver, you make an instance of
+          this class or one of its subclasses and use the generic
+          function <clix:ref>START</clix:ref> to start it (and
+          <clix:ref>STOP</clix:ref> to stop it).  Use the
+          <code>:port</code> initarg if you don&#039;t want to listen
+          on the default http port 80.  If 0 is specified for the
+          port, the system chooses a random port to listen on.  The
+          port number choosen can be retrieved using the
+          <clix:ref>ACCEPTOR-PORT</clix:ref> accessor.  The port
+          number chosen is retained across stopping and starting the
+          acceptor.
+          <p>
+            There are other initargs most of which you probably
+            won&#039;t need very often.  They are explained in detail
+            in the docstrings of the slot definitions.
+          </p>
+          <p>
+            Unless you are in a Lisp without MP capabilities, you can
+            have several active instances of
+            <clix:ref>ACCEPTOR</clix:ref> (listening on different
+            ports) at the same time.
+          </p>
+        </clix:description>
+      </clix:class>
+
+      <clix:class name='ssl-acceptor'>
+        <clix:description>Create and <clix:ref>START</clix:ref> an instance of this class
+        (instead of <clix:ref>ACCEPTOR</clix:ref>) if you want an https server.  There are two
+        required initargs, <code>:SSL-CERTIFICATE-FILE</code> and <code>:SSL-PRIVATEKEY-FILE</code>, for
+        pathname designators denoting the certificate file and the key file in
+        PEM format.  On LispWorks, you can have both in one file in which case
+        the second initarg is optional.  You can also use the
+        <code>:SSL-PRIVATEKEY-PASSWORD</code> initarg to provide a password
+        (as a string) for the key file (or <code>NIL</code>, the default, for
+        no password).
+        <p>
+          The default port for <clix:ref>SSL-ACCEPTOR</clix:ref> instances is 443 instead of 80
+        </p>
+        </clix:description>
+      </clix:class>
+
+      <clix:function generic='true' name='start'>
+        <clix:lambda-list>acceptor
+        </clix:lambda-list>
+        <clix:returns>acceptor
+        </clix:returns>
+        <clix:description>Starts <clix:arg>acceptor</clix:arg> so that it begins accepting
+        connections.  Returns the acceptor.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='stop'>
+        <clix:lambda-list>acceptor &amp;key soft</clix:lambda-list>
+        <clix:returns>acceptor
+        </clix:returns>
+        <clix:description>Stops the <clix:arg>acceptor</clix:arg> so
+        that it no longer accepts requests.  If
+        <clix:arg>soft</clix:arg> is true, and there are any requests
+        in progress, wait until all requests are fully processed, but
+        meanwhile do not accept new requests.  Note that
+        <clix:arg>soft</clix:arg> must not be set when calling
+        <clix:ref>stop</clix:ref> from within a request handler, as
+        that will deadlock.
+        </clix:description>
+      </clix:function>
+
+      <clix:special-variable name='*acceptor*'>
+        <clix:description>The current ACCEPTOR object in the context of a request.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function generic='true' name='acceptor-listen-backlog'>
+        <clix:lambda-list>listen-backlog
+        </clix:lambda-list>
+        <clix:returns>number-of-pending-connections
+        </clix:returns>
+        <clix:description>
+          Number of pending connections allowed in the listen socket
+          before the kernel rejects further incoming connections.
+          Non-LispWorks only.
+        </clix:description>
+      </clix:function>
+
+      <clix:readers generic='true'>
+        <clix:listed-reader generic='true' name='acceptor-address'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>address
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='acceptor-port'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>port
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='acceptor-read-timeout'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>read-timeout
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='acceptor-ssl-certificate-file'>
+          <clix:lambda-list>ssl-acceptor
+          </clix:lambda-list>
+          <clix:returns>ssl-certificate-file
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='acceptor-ssl-privatekey-file'>
+          <clix:lambda-list>ssl-acceptor
+          </clix:lambda-list>
+          <clix:returns>ssl-privatekey-file
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='acceptor-ssl-privatekey-password'>
+          <clix:lambda-list>ssl-acceptor
+          </clix:lambda-list>
+          <clix:returns>ssl-privatekey-password
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='acceptor-write-timeout'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>write-timeout
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:description>
+          These are readers for various slots of <clix:ref>ACCEPTOR</clix:ref>
+          objects (and some of them obviously only make sense
+          for <clix:ref>SSL-ACCEPTOR</clix:ref> objects).  See the docstrings of
+          these slots for more information and note that there are corresponding
+          initargs for all of them.
+        </clix:description>
+      </clix:readers>
+
+      <clix:accessors generic='true'>
+        <clix:listed-accessor generic='true' name='acceptor-access-log-destination'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>(or pathname null)
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='acceptor-document-root'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>(or pathname null)
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='acceptor-error-template-directory'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>(or pathname null)
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='acceptor-input-chunking-p'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>input-chunking-p
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='acceptor-message-log-destination'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>(or pathname null)
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='acceptor-name'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>name
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='acceptor-output-chunking-p'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>output-chunking-p
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='acceptor-persistent-connections-p'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>persistent-connections-p
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='acceptor-reply-class'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>reply-class
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='acceptor-request-class'>
+          <clix:lambda-list>acceptor
+          </clix:lambda-list>
+          <clix:returns>request-class
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:description>
+          These are accessors for various slots of <clix:ref>ACCEPTOR</clix:ref>
+          objects.  See the docstrings of these slots for more information and
+          note that there are corresponding initargs for all of them.
+        </clix:description>
+      </clix:accessors>
+
+      <clix:function generic='true' name='acceptor-ssl-p'>
+        <clix:lambda-list>acceptor
+        </clix:lambda-list>
+        <clix:returns>generalized-boolean
+        </clix:returns>
+        <clix:description>Returns a true value if <clix:arg>acceptor</clix:arg> uses SSL
+        connections.  The default is to unconditionally return <code>NIL</code> and
+        subclasses of <clix:ref>ACCEPTOR</clix:ref> must specialize this method to signal that
+        they&#039;re using secure connections - see the <clix:ref>SSL-ACCEPTOR</clix:ref> class.
+        </clix:description>
+      </clix:function>
+
+      <clix:special-variable name='*default-connection-timeout*'>
+        <clix:description>The default connection timeout used when an
+        acceptor is reading from and writing to a socket stream.  Note that
+        some Lisps allow you to set different timeouts for reading and writing
+        and you can specify both values via initargs when you create
+        an <a href="#acceptors">acceptor</a>.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function generic='true' name='acceptor-remove-session'>
+        <clix:lambda-list>acceptor session
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>
+          This function is called whenever a session in
+          <clix:ref>ACCEPTOR</clix:ref> is being destroyed because of
+          a session timout or an explicit
+          <clix:ref>REMOVE-SESSION</clix:ref> call.
+        </clix:description>
+      </clix:function>
+
+    </clix:subchapter>
+
+    <clix:subchapter name="acceptor-behaviour" title="Customizing acceptor behaviour">
+
+      If you want to modify what acceptors do, you should subclass
+      <clix:ref>ACCEPTOR</clix:ref> (or <clix:ref>SSL-ACCEPTOR</clix:ref>) and
+      specialize the generic functions that constitute their behaviour (see
+      example below).  The life of an acceptor looks like this: It is started
+      with the function <clix:ref>START</clix:ref> which immediately calls
+      <clix:ref>START-LISTENING</clix:ref> and then applies the function
+      <clix:ref>EXECUTE-ACCEPTOR</clix:ref> to its <a
+      href="#taskmasters">taskmaster</a>.  This function will eventually call
+      <clix:ref>ACCEPT-CONNECTIONS</clix:ref> which is responsible for setting
+      things up to wait for clients to connect.  For each incoming connection
+      which comes in, <clix:ref>HANDLE-INCOMING-CONNECTION</clix:ref> is applied
+      to the taskmaster which will either call
+      <clix:ref>PROCESS-CONNECTION</clix:ref> directly, or will create a thread
+      to call it.  <clix:ref>PROCESS-CONNECTION</clix:ref> calls
+      <clix:ref>INITIALIZE-CONNECTION-STREAM</clix:ref> before it does anything
+      else, then it selects and calls a function which handles the <a
+      href="#requests">request</a>, and finally it sends the <a
+      href="#replies">reply</a> to the client before it calls
+      <clix:ref>RESET-CONNECTION-STREAM</clix:ref>.  If the connection is
+      persistent, this procedure is repeated (except for the intialization step)
+      in a loop until the connection is closed.  The acceptor is stopped with
+      <clix:ref>STOP</clix:ref>.
+
+      <p>
+        If you just want to use the standard acceptors that come with
+        Hunchentoot, you don't need to know anything about the functions
+        listed in this section.
+      </p>
+
+      <clix:function generic='true' name='start-listening'>
+        <clix:lambda-list>acceptor
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>Sets up a listen socket for the given acceptor and
+        enables it to listen to incoming connections.  This function is called
+        from the thread that starts the acceptor initially and may return
+        errors resulting from the listening operation (like &#039;address in use&#039;
+        or similar).
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='accept-connections'>
+        <clix:lambda-list>acceptor
+        </clix:lambda-list>
+        <clix:returns>nil
+        </clix:returns>
+        <clix:description>In a loop, accepts a connection and hands it over
+        to the acceptor's taskmaster for processing using
+        <clix:ref>HANDLE-INCOMING-CONNECTION</clix:ref>. On LispWorks, this
+        function returns immediately, on other Lisps it returns only once the
+        acceptor has been stopped.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='process-connection'>
+        <clix:lambda-list>acceptor socket
+        </clix:lambda-list>
+        <clix:returns>nil
+        </clix:returns>
+        <clix:description>
+          This function is called by the taskmaster when a new client
+          connection has been established.  Its arguments are the
+          <clix:ref>ACCEPTOR</clix:ref> object and a LispWorks socket
+          handle or a usocket socket stream object in
+          <clix:arg>socket</clix:arg>.  It reads the request headers,
+          sets up the <a href="#requests">request</a> and <a
+          href="#replies">reply</a> objects, and hands over to
+          <clix:ref>PROCESS-REQUEST</clix:ref> which calls
+          <clix:ref>HANDLE-REQUEST</clix:ref> to select and call a
+          handler for the request and sends its reply to the client.
+          This is done in a loop until the stream has to be closed or
+          until a connection timeout occurs.  It is probably not a
+          good idea to re-implement this method until you really,
+          really know what you're doing.
+          <p>
+            Handlers may call to the
+            <clix:ref>DETACH-SOCKET</clix:ref> generic function to
+            indicate that no further requests should be handled on
+            the connection by Hunchentoot, and that responsibility for
+            the socket is assumed by third-party software.  This can
+            be used by specialized handlers that wish to hand over
+            connection polling or processing to functions outside of
+            Hunchentoot, i.e. for connection multiplexing or
+            implementing specialized client protocols.  Hunchentoot
+            will finish processing the request and the
+            <clix:ref>PROCESS-CONNECTION</clix:ref> function will
+            return without closing the connection.  At that point,
+            the acceptor may interact with the socket in whatever
+            fashion required.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='detach-socket'>
+        <clix:lambda-list>acceptor
+        </clix:lambda-list>
+        <clix:returns>stream
+        </clix:returns>
+        <clix:description>
+          Indicate to Hunchentoot that it should stop serving requests
+          on the current request's socket.  Hunchentoot will finish
+          processing the current request and then return from
+          <clix:ref>PROCESS-CONNECTION</clix:ref> without closing the
+          connection to the client.
+          <clix:ref>DETACH-SOCKET</clix:ref> can only be called from
+          within a request handler function.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='initialize-connection-stream'>
+        <clix:lambda-list>acceptor stream
+        </clix:lambda-list>
+        <clix:returns>stream
+        </clix:returns>
+        <clix:description>
+          Can be used to modify the stream which is used to
+          communicate between client and server before the request is
+          read.  The default method of <clix:ref>ACCEPTOR</clix:ref>
+          does nothing, but see for example the method defined for
+          <clix:ref>SSL-ACCEPTOR</clix:ref>.  All methods of this
+          generic function <em>must</em> return the stream to use.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='reset-connection-stream'>
+        <clix:lambda-list>acceptor stream
+        </clix:lambda-list>
+        <clix:returns>stream
+        </clix:returns>
+        <clix:description>
+          Resets the stream which is used to communicate
+          between client and server after one request has been served so that it
+          can be used to process the next request.  This generic function is
+          called after a request has been processed and <em>must</em> return the
+          stream.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="acceptor-log-access" generic="true">
+        <clix:lambda-list>acceptor &amp;key return-code</clix:lambda-list>
+        <clix:description>
+          Function to call to log access to the acceptor.  The
+          <clix:arg>return-code</clix:arg> keyword argument contains additional
+          information about the request to log.  In addition, it can use the
+          standard request and reply accessor functions that are available to
+          handler functions to find out more information about the request.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="acceptor-log-message" generic="true">
+        <clix:lambda-list>acceptor log-level format-string &amp;rest format-arguments</clix:lambda-list>
+        <clix:description>
+          Function to call to log messages by the <clix:arg>acceptor</clix:arg>.  It must accept
+          a severity level for the message, which will be one of :ERROR, :INFO,
+          or :WARNING, a format string and an arbitary number of formatting
+          arguments.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="acceptor-status-message" generic="true">
+        <clix:lambda-list>acceptor http-return-code &amp;key &amp;allow-other-keys</clix:lambda-list>
+        <clix:description>
+          This function is called when a request's handler has been
+          called but failed to provide content to send back to the
+          client.  It converts the
+          <clix:arg>HTTP-STATUS-CODE</clix:arg> to some request
+          contents, typically a human readable description of the
+          status code to be displayed to the user.
+
+          If an ERROR-TEMPLATE-DIRECTORY is set in the current
+          acceptor and the directory contains a file corresponding to
+          HTTP-STATUS-CODE named &lt;code&gt;.html, that file is sent
+          to the client after variable substitution.  Variables are
+          referenced by ${&lt;variable-name&gt;}.
+
+          Additional keyword arguments may be provided which are made
+          available to the templating logic as substitution variables.
+          These variables can be interpolated into error message
+          templates in, which contains the current URL relative to the
+          server and without GET parameters.
+
+          In addition to the variables corresponding to keyword
+          arguments, the script-name, lisp-implementation-type,
+          lisp-implementation-version and hunchentoot-version
+          variables are available.
+        </clix:description>
+      </clix:function>
+    </clix:subchapter>
+
+  <clix:subchapter name="subclassing-acceptors"
+                  title="An example of how to subclass ACCEPTOR">
+
+    This example shows how to subclass <clix:ref>ACCEPTOR</clix:ref> in order to
+    provide Hunchentoot with basic virtual host support.&#x00A0; It assumes
+    Hunchentoot is sitting behind an Internet-facing reverse-proxy web server
+    that maps the host (or domain) part of incoming HTTP requests to unique
+    localhost ports.
+
+    <pre>(asdf:load-system "hunchentoot")
+(asdf:load-system "drakma")
+
+;;; Subclass ACCEPTOR
+(defclass vhost (tbnl:acceptor)
+  ;; slots
+  ((dispatch-table
+    :initform '()
+    :accessor dispatch-table
+    :documentation "List of dispatch functions"))
+  ;; options
+  (:default-initargs                    ; default-initargs must be used
+   :address "127.0.0.1"))               ; because ACCEPTOR uses it
+
+;;; Specialise ACCEPTOR-DISPATCH-REQUEST for VHOSTs
+(defmethod tbnl:acceptor-dispatch-request ((vhost vhost) request)
+  ;; try REQUEST on each dispatcher in turn
+  (mapc (lambda (dispatcher)
+         (let ((handler (funcall dispatcher request)))
+           (when handler               ; Handler found. FUNCALL it and return result
+             (return-from tbnl:acceptor-dispatch-request (funcall handler)))))
+       (dispatch-table vhost))
+  (call-next-method))
+
+;;; ======================================================================
+;;; Now all we need to do is test it
+
+;;; Instantiate VHOSTs
+(defvar vhost1 (make-instance 'vhost :port 50001))
+(defvar vhost2 (make-instance 'vhost :port 50002))
+
+;;; Populate each dispatch table
+(push
+ (tbnl:create-prefix-dispatcher "/foo" 'foo1)
+ (dispatch-table vhost1))
+(push
+ (tbnl:create-prefix-dispatcher "/foo" 'foo2)
+ (dispatch-table vhost2))
+
+;;; Define handlers
+(defun foo1 () "Hello")
+(defun foo2 () "Goodbye")
+
+;;; Start VHOSTs
+(tbnl:start vhost1)
+(tbnl:start vhost2)
+
+;;; Make some requests
+(drakma:http-request "http://127.0.0.1:50001/foo")
+;;; =|
+;;; 127.0.0.1 - [2012-06-08 14:30:39] "GET /foo HTTP/1.1" 200 5 "-" "Drakma/1.2.6 (SBCL 1.0.56; Linux; 2.6.32-5-686; http://weitz.de/drakma/)"
+;;; =>
+;;; "Hello"
+;;; 200
+;;; ((:CONTENT-LENGTH . "5") (:DATE . "Fri, 08 Jun 2012 14:30:39 GMT")
+;;;  (:SERVER . "Hunchentoot 1.2.3") (:CONNECTION . "Close")
+;;;  (:CONTENT-TYPE . "text/html; charset=utf-8"))
+;;; #&lt;PURI:URI http://127.0.0.1:50001/foo&gt;
+;;; #&lt;FLEXI-STREAMS:FLEXI-IO-STREAM {CA90059}&gt;
+;;; T
+;;; "OK"
+(drakma:http-request "http://127.0.0.1:50002/foo")
+;;; =|
+;;; 127.0.0.1 - [2012-06-08 14:30:47] "GET /foo HTTP/1.1" 200 7 "-" "Drakma/1.2.6 (SBCL 1.0.56; Linux; 2.6.32-5-686; http://weitz.de/drakma/)"
+;;; =&gt;
+;;; "Goodbye"
+;;; 200
+;;; ((:CONTENT-LENGTH . "7") (:DATE . "Fri, 08 Jun 2012 14:30:47 GMT")
+;;;  (:SERVER . "Hunchentoot 1.2.3") (:CONNECTION . "Close")
+;;;  (:CONTENT-TYPE . "text/html; charset=utf-8"))
+;;; #&lt;PURI:URI http://127.0.0.1:50002/foo&gt;
+;;; #&lt;FLEXI-STREAMS:FLEXI-IO-STREAM {CAE8059}&gt;
+;;; T
+;;; "OK"</pre>
+
+    How to make each VHOST write to separate access log streams (or files) is
+    left as an exercise to the reader.
+
+  </clix:subchapter>
+
+    <clix:subchapter name="taskmasters" title="Taskmasters">
+      As a "normal" Hunchentoot user, you can completely ignore
+      taskmasters and skip this section.  But if you're still reading,
+      here are the dirty details: Each <a
+      href="#acceptors">acceptor</a> has a taskmaster associated with
+      it at creation time.  It is the taskmaster's job to distribute
+      the work of accepting and handling incoming connections.  The
+      acceptor calls the taskmaster if appropriate and the taskmaster
+      calls back into the acceptor.  This is done using the generic
+      functions described in this and the <a
+      href="#acceptor-behaviour">previous</a> section.  Hunchentoot
+      comes with two standard taskmaster implementations - one (which
+      is the default used on multi-threaded Lisps) which starts a new
+      thread for each incoming connection and one which handles all
+      requests sequentially.  It should for example be relatively
+      straightforward to create a taskmaster which allocates threads
+      from a fixed pool instead of creating a new one for each
+      connection.
+
+      <p>
+        You can control the resources consumed by a threaded taskmaster via
+        two initargs. <code>:max-thread-count</code> lets you set the maximum
+        number of request threads that can be processes simultaneously.  If
+        this is <code>nil</code>, the is no thread limit imposed.
+
+        <code>:max-accept-count</code> lets you set the maximum number of requests
+        that can be outstanding (i.e. being processed or queued for processing).
+
+        If <code>:max-thread-count</code> is supplied and <code>:max-accept-count</code>
+        is <code>NIL</code>, then a <clix:ref>+HTTP-SERVICE-UNAVAILABLE+</clix:ref>
+        error will be generated if there are more than the max-thread-count
+        threads processing requests.  If both <code>:max-thread-count</code>
+        and <code>:max-accept-count</code> are supplied, then max-thread-count
+        must be less than max-accept-count; if more than max-thread-count
+        requests are being processed, then requests up to max-accept-count
+        will be queued until a thread becomes available.  If more than
+        max-accept-count requests are outstanding, then a <clix:ref>+HTTP-SERVICE-UNAVAILABLE+</clix:ref>
+        error will be generated.
+
+        In a load-balanced environment with multiple Hunchentoot servers, it's
+        reasonable to provide <code>:max-thread-count</code> but leave
+        <code>:max-accept-count</code> null.   This will immediately result
+        in <clix:ref>+HTTP-SERVICE-UNAVAILABLE+</clix:ref> when one server is
+        out of resources, so the load balancer can try to find another server.
+
+        In an environment with a single Hunchentoot server, it's reasonable
+        to provide both <code>:max-thread-count</code> and a somewhat larger value
+        for <code>:max-accept-count</code>.  This will cause a server that's almost
+        out of resources to wait a bit; if the server is completely out of resources,
+        then the reply will be <clix:ref>+HTTP-SERVICE-UNAVAILABLE+</clix:ref>.
+        The default for these values is 100 and 120, respectively.
+      </p>
+
+      <p>
+        If you want to implement your own taskmasters, you should subclass
+        <clix:ref>TASKMASTER</clix:ref> or one of its subclasses,
+        <clix:ref>SINGLE-THREADED-TASKMASTER</clix:ref> or
+        <clix:ref>ONE-THREAD-PER-CONNECTION-TASKMASTER</clix:ref>, and
+        specialize the generic functions in this section.
+      </p>
+
+      <clix:class name='taskmaster'>
+        <clix:description>
+          An instance of this class is responsible for distributing
+          the work of handling requests for its acceptor.  This is an
+          "abstract" class in the sense that usually only instances of
+          subclasses of <clix:ref>TASKMASTER</clix:ref> will be used.
+        </clix:description>
+      </clix:class>
+
+      <clix:class name='one-thread-per-connection-taskmaster'>
+        <clix:description>
+          A taskmaster that starts one thread for listening to
+          incoming requests and one thread for each incoming
+          connection.
+          <p>
+            This is the default taskmaster implementation for multi-threaded Lisp
+            implementations.
+          </p>
+        </clix:description>
+      </clix:class>
+
+      <clix:class name='single-threaded-taskmaster'>
+        <clix:description>
+          A taskmaster that runs synchronously in the
+          thread where the <clix:ref>START</clix:ref> function was invoked (or
+          in the case of LispWorks in the thread started
+          by <a href="http://www.lispworks.com/documentation/lw51/LWRM/html/lwref-61.htm#marker-910861"><code>COMM:START-UP-SERVER</code></a>).
+          This is the simplest possible taskmaster implementation in that its
+          methods do nothing but calling their acceptor &quot;sister&quot;
+          methods - <clix:ref>EXECUTE-ACCEPTOR</clix:ref> calls <clix:ref>ACCEPT-CONNECTIONS</clix:ref>,
+          <clix:ref>HANDLE-INCOMING-CONNECTION</clix:ref> calls <clix:ref>PROCESS-CONNECTION</clix:ref>.
+        </clix:description>
+      </clix:class>
+
+      <clix:class name='multi-threaded-taskmaster'>
+        <clix:description>
+          This is an abstract class for taskmasters that use multiple threads;
+          it is not a concrete class and you should not instantiate it with
+          <code>MAKE-INSTANCE</code>.
+          Instead, you should instantiate its subclass
+          <clix:ref>ONE-THREAD-PER-CONNECTION-TASKMASTER</clix:ref> described above.
+          <clix:ref>MULTI-THREADED-TASKMASTER</clix:ref>
+          is intended to be inherited from by extensions to Hunchentoot,
+          such as <a href="http://common-lisp.net/project/qitab/">quux-hunchentoot</a>'s
+          <code>THREAD-POOLING-TASKMASTER</code>,
+          though at the moment, doing so only inherits one slot and one method,
+          on <clix:ref>EXECUTE-ACCEPTOR</clix:ref>,
+          to have it start a new thread for the acceptor,
+          then saved in said slot.
+        </clix:description>
+      </clix:class>
+
+      <clix:function generic='true' name='execute-acceptor'>
+        <clix:lambda-list>taskmaster
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>This is a callback called by the acceptor once it
+        has performed all initial processing to start listening for incoming
+        connections (see <clix:ref>START-LISTENING</clix:ref>).  It usually calls the
+        <clix:ref>ACCEPT-CONNECTIONS</clix:ref> method of the acceptor, but depending on the
+        taskmaster instance the method might be called from a new thread.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='handle-incoming-connection'>
+        <clix:lambda-list>taskmaster socket
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          This function is called by the acceptor to start
+          processing of requests on a new incoming connection.  <clix:arg>socket</clix:arg> is the
+          usocket instance that represents the new connection (or a socket
+          handle on LispWorks).  The taskmaster starts processing requests on
+          the incoming connection by calling the <clix:ref>PROCESS-CONNECTION</clix:ref>
+          method of the acceptor instance.  The <clix:arg>socket</clix:arg> argument is passed to
+          <clix:ref>PROCESS-CONNECTION</clix:ref> as an argument.
+
+          If the taskmaster is a multi-threaded taskmaster, <clix:ref>HANDLE-INCOMING-THREAD</clix:ref>
+          will call <clix:ref>CREATE-REQUEST-HANDLER-THREAD</clix:ref>, which will call
+          <clix:ref>PROCESS-CONNECTION</clix:ref> in a new thread.
+          <clix:ref>HANDLE-INCOMING-THREAD</clix:ref> might issue a
+          <clix:ref>+HTTP-SERVICE-UNAVAILABLE+</clix:ref> error
+          if there are too many request threads or it might block waiting for a
+          request thread to finish.
+              </clix:description>
+            </clix:function>
+
+            <clix:function generic='true' name='start-thread'>
+            <clix:lambda-list>taskmaster thunk &amp;key
+            </clix:lambda-list>
+            <clix:returns>thread
+            </clix:returns>
+              <clix:description>This function is a callback that
+                starts a new thread that will call the given <clix:arg>thunk</clix:arg>
+                in the context of the proper <clix:arg>taskmaster</clix:arg>,
+                with appropriate context-dependent keyword arguments.
+                <clix:ref>ONE-THREAD-PER-CONNECTION-TASKMASTER</clix:ref> uses it in
+                <clix:ref>EXECUTE-ACCEPTOR</clix:ref>
+                and <clix:ref>CREATE-REQUEST-HANDLER-THREAD</clix:ref>,
+                but specialized taskmasters may define more functions that use it.
+                By default, it just creates a thread calling the thunk
+                with a specified <clix:arg>name</clix:arg> keyword argument.
+                Specialized taskmasters may wrap special bindings and condition handlers
+                around the thunk call, register the thread in a management table, etc.
+              </clix:description>
+            </clix:function>
+
+            <clix:function generic='true' name='create-request-handler-thread'>
+            <clix:lambda-list>taskmaster socket
+            </clix:lambda-list>
+            <clix:returns>thread
+            </clix:returns>
+              <clix:description>This function is called by <clix:ref>HANDLE-INCOMING-THREAD</clix:ref>
+          to create a new thread which calls <clix:ref>PROCESS-CONNECTION</clix:ref>.
+          If you specialize this function, you must be careful to have the thread
+          call <clix:ref>DECREMENT-TASKMASTER-REQUEST-COUNT</clix:ref> before
+          it exits.  A typical method will look like this:
+
+                <pre>(defmethod create-request-handler-thread ((taskmaster monitor-taskmaster) socket)
+  (bt:make-thread
+   (lambda ()
+     (with-monitor-error-handlers
+         (unwind-protect
+              (with-monitor-variable-bindings
+                  (process-connection (taskmaster-acceptor taskmaster) socket))
+           (decrement-taskmaster-request-count taskmaster))))))</pre>
+
+
+
+
+
+
+
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='shutdown'>
+        <clix:lambda-list>taskmaster
+        </clix:lambda-list>
+        <clix:returns>taskmaster
+        </clix:returns>
+        <clix:description>Shuts down the taskmaster, i.e. frees all resources
+        that were set up by it.  For example, a multi-threaded taskmaster
+        might terminate all threads that are currently associated with it.
+        This function is called by the acceptor's <clix:ref>STOP</clix:ref> method.
+        </clix:description>
+      </clix:function>
+
+      <clix:accessor generic='true' name='taskmaster-acceptor'>
+        <clix:lambda-list>taskmaster
+        </clix:lambda-list>
+        <clix:returns>acceptor
+        </clix:returns>
+        <clix:description>
+          This is an accessor for the slot of a <clix:ref>TASKMASTER</clix:ref>
+          object that links back to the <a href="#acceptors">acceptor</a> it is
+          associated with.
+        </clix:description>
+      </clix:accessor>
+
+    </clix:subchapter>
+
+    <clix:subchapter name="request-dispatch" title="Request dispatch and handling">
+
+      The main job of <clix:ref>HANDLE-REQUEST</clix:ref> is to select
+      and call a function which handles the request, i.e. which looks
+      at the data the client has sent and prepares an appropriate
+      reply to send back.  This is by default implemented as follows:
+      <p>
+        The ACCEPTOR class defines a
+        <clix:ref>ACCEPTOR-DISPATCH-REQUEST</clix:ref> generic
+        function which is used to actually dispatch the request.  This
+        function is called by the default method of
+        <clix:ref>HANDLE-REQUEST</clix:ref>.  Each
+        <clix:ref>ACCEPTOR-DISPATCH-REQUEST</clix:ref> method looks at
+        the request object and depending on its contents decides to
+        either handle the request or call the next method.
+      </p>
+      <p>
+        In order to dispatch a request, Hunchentoot calls the
+        <clix:ref>ACCEPTOR-DISPATCH-REQUEST</clix:ref> generic
+        functions.  The method for <clix:ref>ACCEPTOR</clix:ref> tries
+        to serve a static file relative to it's
+        <clix:ref>ACCEPTOR-DOCUMENT-ROOT</clix:ref>.  Application
+        specific acceptor subclasses will typically perform URL
+        parsing and dispatching according to the policy that is
+        required.
+      </p>
+      <p>
+        The default method of <clix:ref>HANDLE-REQUEST</clix:ref> sets
+        up <a href="#logging">standard logging and error handling</a>
+        before it calls the acceptor's request dispatcher.
+      </p>
+      <p>
+        Request handlers do their work by modifying
+        the <a href="#replies">reply object</a> if necessary and by eventually
+        returning the response body in the form of a string or a binary
+        sequence.  As an alternative, they can also
+        call <clix:ref>SEND-HEADERS</clix:ref> and write directly to a stream.
+      </p>
+    </clix:subchapter>
+
+    <clix:subchapter name="easy-handlers" title="Using the easy-handler framework">
+      <p>
+        The <clix:ref>EASY-ACCEPTOR</clix:ref> class defines a method
+        for <clix:ref>ACCEPTOR-DISPATCH-REQUEST</clix:ref> that walks
+        through the list <clix:ref>*DISPATCH-TABLE*</clix:ref> which
+        consists of <em>dispatch functions</em>.  Each of these
+        functions accepts the request object as its only argument and
+        either returns a request handler to handle the request or
+        <code>NIL</code> which means that the next dispatcher in the
+        list will be tried. A <em>request handler</em> is a function
+        of zero arguments which relies on the special variable
+        <clix:ref>*REQUEST*</clix:ref> to access the request instance
+        being serviced. If all dispatch functions return
+        <code>NIL</code>, the next
+        <clix:ref>ACCEPTOR-DISPATCH-REQUEST</clix:ref> will be called.
+      </p>
+      <p>
+        <strong>N.B.</strong> All functions and variables in this
+        section are related to the easy request dispatch mechanism and
+        are meaningless if you're using your own request dispatcher.
+      </p>
+
+      <clix:class name='easy-acceptor'>
+        <clix:description>
+          This class defines no additional slots with respect to
+          <clix:ref>ACCEPTOR</clix:ref>.  It only serves as an
+          additional type for dispatching calls to
+          <clix:ref>ACCEPTOR-DISPATCH-REQUEST</clix:ref>.  In order to
+          use the easy handler framework, acceptors of this class or
+          one of its subclasses must be used.
+        </clix:description>
+      </clix:class>
+
+      <clix:class name='easy-ssl-acceptor'>
+        <clix:description>
+          This class mixes the <clix:ref>SSL-ACCEPTOR</clix:ref> and
+          the <clix:ref>EASY-ACCEPTOR</clix:ref> classes.  It is used
+          when both ssl and the easy handler framework are required.
+        </clix:description>
+      </clix:class>
+
+      <clix:special-variable name='*dispatch-table*'>
+        <clix:description>
+          A global list of dispatch functions.  The initial value is a
+          list consisting of the symbol
+          <clix:ref>DISPATCH-EASY-HANDLERS</clix:ref>.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function name="create-prefix-dispatcher">
+        <clix:lambda-list>prefix handler</clix:lambda-list>
+        <clix:returns>dispatch-fn</clix:returns>
+        <clix:description>
+          A convenience function which will return a dispatcher that
+          returns <clix:arg>handler</clix:arg> whenever the path part of
+          the request URI starts with the
+          string <clix:arg>prefix</clix:arg>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="create-regex-dispatcher">
+        <clix:lambda-list>regex handler</clix:lambda-list>
+        <clix:returns>dispatch-fn</clix:returns>
+        <clix:description>
+          A convenience function which will return a dispatcher that
+          returns <clix:arg>handler</clix:arg> whenever the path part of
+          the request URI matches
+          the <a href="http://weitz.de/cl-ppcre/">CL-PPCRE</a> regular
+          expression <clix:arg>regex</clix:arg> (which can be a string, an
+          s-expression, or a scanner).
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="create-folder-dispatcher-and-handler">
+        <clix:lambda-list>uri-prefix base-path <clix:lkw>optional</clix:lkw> content-type</clix:lambda-list>
+        <clix:returns>dispatch-fn</clix:returns>
+        <clix:description>
+          Creates and returns a dispatch function which will dispatch to
+          a handler function which emits the file relative
+          to <clix:arg>base-path</clix:arg> that is denoted by the URI of
+          the request relative
+          to <clix:arg>uri-prefix</clix:arg>.  <clix:arg>uri-prefix</clix:arg>
+          must be a string ending with a
+          slash, <clix:arg>base-path</clix:arg> must be a pathname
+          designator for an existing directory.
+          Uses <clix:ref>HANDLE-STATIC-FILE</clix:ref> internally.
+          <p>
+            If <clix:arg>content-type</clix:arg> is <em>not</em>
+            <code>NIL</code>, it will be used as a the content type for
+            all files in the folder.  Otherwise (which is the default)
+            the content type of each file will be
+            determined <a href="#handle-static-file">as usual</a>.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='create-static-file-dispatcher-and-handler'>
+        <clix:lambda-list>uri path
+        <clix:lkw>optional
+        </clix:lkw> content-type
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          Creates and returns a request dispatch function which will
+          dispatch to a handler function which emits the file denoted
+          by the pathname designator PATH with content type
+          CONTENT-TYPE if the SCRIPT-NAME of the request matches the
+          string URI.  If CONTENT-TYPE is NIL, tries to determine the
+          content type via the file&#039;s suffix.
+        </clix:description>
+      </clix:function>
+
+      <clix:function macro="true" name="define-easy-handler">
+        <clix:lambda-list>description lambda-list [[declaration* | documentation]] form*</clix:lambda-list>
+        <clix:description>
+          Defines a handler as if
+          by <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_defun.htm">
+            <code>DEFUN</code></a> and optionally registers it with a
+          URI so that it will be found
+          by <clix:ref>DISPATCH-EASY-HANDLERS</clix:ref>.
+          <p>
+            <clix:arg>description</clix:arg> is either a
+            symbol <clix:arg>name</clix:arg> or a list matching the
+            <a href="http://www.lispworks.com/documentation/HyperSpec/Body/03_de.htm">destructuring
+              lambda list</a>
+          </p>
+          <pre>(name &amp;key uri acceptor-names default-parameter-type default-request-type).</pre>
+          <clix:arg>lambda-list</clix:arg> is a list the elements of which
+          are either a symbol <clix:arg>var</clix:arg> or a list matching
+          the destructuring lambda list
+          <pre>(var &amp;key real-name parameter-type init-form request-type).</pre>
+          The resulting handler will be a Lisp function with the
+          name <clix:arg>name</clix:arg> and keyword parameters named by
+          the <clix:arg>var</clix:arg> symbols.
+          Each <clix:arg>var</clix:arg> will be bound to the value of the
+          GET or POST parameter called <clix:arg>real-name</clix:arg> (a
+          string) before the body of the function is executed.
+          If <clix:arg>real-name</clix:arg> is not provided, it will be
+          computed
+          by <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_stg_up.htm#string-downcase">downcasing</a>
+          the symbol name of <clix:arg>var</clix:arg>.
+          <p>
+            If <clix:arg>uri</clix:arg> (which is evaluated) is provided,
+            then it must be a string or
+            a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#function_designator">function
+              designator</a> for a unary function.  In this case, the
+            handler will be returned
+            by <clix:ref>DISPATCH-EASY-HANDLERS</clix:ref>,
+            if <clix:arg>uri</clix:arg> is a string and
+            the <a href="#script-name">script name</a> of the current
+            request is <clix:arg>uri</clix:arg>, or
+            if <clix:arg>uri</clix:arg> designates a function and applying
+            this function to
+            the <a href="#*request*">current <code>REQUEST</code>
+              object</a> returns a true value.
+          </p>
+          <p>
+            <clix:arg>acceptor-names</clix:arg> (which is evaluated) can be a
+            list of symbols which means that the handler will only be
+            returned by <clix:ref>DISPATCH-EASY-HANDLERS</clix:ref> in
+            acceptors which have one of these names
+            (see <clix:ref>ACCEPTOR-NAME</clix:ref>).  <clix:arg>acceptor-names</clix:arg> can also be the
+            symbol <code>T</code> which means that the handler will be
+            returned by <clix:ref>DISPATCH-EASY-HANDLERS</clix:ref>
+            in <em>every</em> acceptor.
+          </p>
+          <p>
+            Whether the GET or POST parameter (or both) will be taken into
+            consideration, depends on <clix:arg>request-type</clix:arg>
+            which can
+            be <code>:GET</code>, <code>:POST</code>, <code>:BOTH</code>,
+            or <code>NIL</code>.  In the last case, the value of
+            <clix:arg>default-request-type</clix:arg> (the default of which
+            is <code>:BOTH</code>) will be used.
+          </p>
+          <p>
+            The value of <clix:arg>var</clix:arg> will usually be a string
+            (unless it resulted from a <a href="#upload">file upload</a>
+            in which case it won't be converted at all), but
+            if <clix:arg>parameter-type</clix:arg> (which is evaluated) is
+            provided, the string will be converted to another Lisp type by
+            the following rules:
+          </p>
+          <p>
+            If the corresponding GET or POST parameter wasn't provided by
+            the client, <clix:arg>var</clix:arg>'s value will
+            be <code>NIL</code>.  If <clix:arg>parameter-type</clix:arg>
+            is <code>'STRING</code>,
+            <clix:arg>var</clix:arg>'s value remains as is.
+            If <clix:arg>parameter-type</clix:arg> is <code>'INTEGER</code>
+            and the parameter string consists solely of decimal
+            digits, <clix:arg>var</clix:arg>'s value will be the
+            corresponding integer, otherwise <code>NIL</code>.
+            If <clix:arg>parameter-type</clix:arg> is
+            <code>'KEYWORD</code>, <clix:arg>var</clix:arg>'s value will be
+            the keyword obtained
+            by <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_intern.htm">interning</a>
+            the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_stg_up.htm#string-upcase">upcased</a>
+            parameter string into
+            the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/11_abc.htm">keyword
+              package</a>.  If <clix:arg>parameter-type</clix:arg>
+            is <code>'CHARACTER</code> and the parameter string is of
+            length one, <clix:arg>var</clix:arg>'s value will be the single
+            character of this string, otherwise <code>NIL</code>.
+            If <clix:arg>parameter-type</clix:arg>
+            is <code>'BOOLEAN</code>, <clix:arg>var</clix:arg>'s value will
+            always be <code>T</code> (unless it is <code>NIL</code> by the
+            first rule above, of course).
+            If <clix:arg>parameter-type</clix:arg> is any other atom, it is
+            supposed to be
+            a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#function_designator">function
+              designator</a> for a unary function which will be called to
+            convert the string to something else.
+          </p>
+          <p>
+            Those were the rules for <em>simple</em> parameter types, but
+            <clix:arg>parameter-type</clix:arg> can also be a list starting
+            with one of the symbols
+            <code>LIST</code>, <code>ARRAY</code>,
+            or <code>HASH-TABLE</code>.  The second value of the list must
+            always be a simple parameter type as in the last paragraph -
+            we'll call it the <em>inner type</em> below.
+          </p>
+          <p>
+            In the case of <code>'LIST</code>, all GET/POST parameters
+            called <clix:arg>real-name</clix:arg> will be collected,
+            converted to the inner type as by the rules above, and
+            assembled into a list which will be the value of
+            <clix:arg>var</clix:arg>.
+          </p>
+          <p>
+            In the case of <code>'ARRAY</code>, all GET/POST parameters
+            which have a name like the result of
+          </p>
+          <pre>(format nil "~A[~A]" real-name n)</pre>
+          where <clix:arg>n</clix:arg> is a non-negative integer, will be
+          assembled into an array where the <clix:arg>n</clix:arg>th element
+          will be set accordingly, after conversion to the inner type.
+          The array, which will become the value
+          of <clix:arg>var</clix:arg>, will be big enough to hold all
+          matching parameters, but not bigger.  Array elements not set as
+          described above will be <code>NIL</code>.  Note
+          that <code>VAR</code> will always be bound to an array, which
+          may be empty, so it will never be <code>NIL</code>, even if no
+          appropriate GET/POST parameters are found.
+          <p>
+            The full form of a <code>'HASH-TABLE</code> parameter type is
+          </p>
+          <pre>(hash-table inner-type key-type test-function)</pre>
+          but <clix:arg>key-type</clix:arg>
+          and <clix:arg>test-function</clix:arg> can be left out in which
+          case they default to <code>'STRING</code>
+          and <code>'EQUAL</code>, respectively.  For this parameter type,
+          all GET/POST parameters which have a name like the result of
+          <pre>(format nil "~A{~A}" real-name key)</pre>
+          (where <clix:arg>key</clix:arg> is a string that doesn't contain
+          curly brackets) will become the values (after conversion
+          to <clix:arg>inner-type</clix:arg>) of a hash table with test
+          function <clix:arg>test-function</clix:arg>
+          where <clix:arg>key</clix:arg> (after conversion
+          to <clix:arg>key-type</clix:arg>) will be the corresponding key.
+          Note that <clix:arg>var</clix:arg> will always be bound to a hash
+          table, which may be empty, so it will never be <code>NIL</code>,
+          even if no appropriate GET/POST parameters are found.
+          <p>
+            To make matters even more complicated, the three compound
+            parameter types also have an abbreviated form - just one of
+            the symbols <code>LIST</code>, <code>ARRAY</code>,
+            or <code>HASH-TABLE</code>.  In this case, the inner type will
+            default to <code>'STRING</code>.
+          </p>
+          <p>
+            If <clix:arg>parameter-type</clix:arg> is not provided
+            or <code>NIL</code>, <clix:arg>default-parameter-type</clix:arg>
+            (the default of which is <code>'STRING</code>) will be used
+            instead.
+          </p>
+          <p>
+            If the result of the computations above would be
+            that <clix:arg>var</clix:arg> would be bound
+            to <code>NIL</code>, then <clix:arg>init-form</clix:arg> (if
+            provided) will be evaluated instead,
+            and <clix:arg>var</clix:arg> will be bound to the result of this
+            evaluation.
+          </p>
+          <p>
+            Handlers built with this macro are constructed in such a way
+            that the resulting Lisp function is useful even outside of
+            Hunchentoot.  Specifically, all the parameter computations
+            above will only happen if <clix:ref>*REQUEST*</clix:ref> is
+            bound, i.e. if we're within a Hunchentoot request.
+            Otherwise, <clix:arg>var</clix:arg> will always be bound to the
+            result of evaluating <clix:arg>init-form</clix:arg> unless a
+            corresponding keyword argument is provided.
+          </p>
+          <p>
+            The <a href="#example">example code</a> that comes with
+            Hunchentoot contains an example which demonstrates some of the
+            features of <clix:ref>DEFINE-EASY-HANDLER</clix:ref>.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='dispatch-easy-handlers'>
+        <clix:lambda-list>request
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>This is a dispatcher which returns the appropriate handler
+        defined with <clix:ref>DEFINE-EASY-HANDLER</clix:ref>, if there is one.
+        </clix:description>
+      </clix:function>
+
+    </clix:subchapter>
+
+    <clix:subchapter name="requests" title="Request objects">
+
+      For each incoming request, the <a href="#acceptors">acceptor</a> (in
+      <clix:ref>PROCESS-CONNECTION</clix:ref>) creates a
+      <clix:ref>REQUEST</clix:ref> object and makes it available to <a
+      href="#request-dispatch">handlers</a> via the special variable
+      <clix:ref>*REQUEST*</clix:ref>.  This object contains all relevant
+      information about the request and this section collects the functions
+      which can be used to query such an object.  In all function where
+      <clix:arg>request</clix:arg> is an optional or keyword parameter, the
+      default is <clix:ref>*REQUEST*</clix:ref>.
+
+      <p>
+        If you need more fine-grained control over the behaviour of request
+        objects, you can subclass <clix:ref>REQUEST</clix:ref> and initialize
+        the <a href="#acceptor-request-class"><code>REQUEST-CLASS</code></a>
+        slot of the <clix:ref>ACCEPTOR</clix:ref> class accordingly.  The
+        acceptor will generate request objects of the class named by this
+        slot.
+      </p>
+
+      <clix:class name='request'>
+        <clix:description>
+          Objects of this class hold all the information
+          about an incoming request.  They are created automatically by
+          acceptors and can be accessed by the
+          corresponding <a href="#request-dispatch">handler</a>.
+
+          You should not mess with the slots of these objects directly, but you
+          can subclass <clix:ref>REQUEST</clix:ref> in order to implement your
+          own behaviour.  See
+          the <a href="#acceptor-request-class"><code>REQUEST-CLASS</code></a>
+          slot of the <clix:ref>ACCEPTOR</clix:ref> class.
+        </clix:description>
+      </clix:class>
+
+      <clix:special-variable name='*request*'>
+        <clix:description>The current REQUEST object while in the context of a request.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function name='real-remote-addr'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>string{, list}
+        </clix:returns>
+        <clix:description>
+          Returns the &#039;<code>X-Forwarded-For</code>&#039; incoming http header as the
+          second value in the form of a list of IP addresses and the first
+          element of this list as the first value if this header exists.
+          Otherwise returns the value of <clix:ref>REMOTE-ADDR</clix:ref> as the only value.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='parameter'>
+        <clix:lambda-list>name
+        <clix:lkw>optional
+        </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>string
+        </clix:returns>
+        <clix:description>
+          Returns the GET or the POST parameter with name
+          <clix:arg>name</clix:arg> (a string) - or <code>NIL</code>
+          if there is none.  If both a GET and a POST parameter with
+          the same name exist the GET parameter is returned.  Search
+          is case-sensitive.  See also
+          <clix:ref>GET-PARAMETER</clix:ref> and
+          <clix:ref>POST-PARAMETER</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="get-parameter">
+        <clix:lambda-list>name <clix:lkw>optional</clix:lkw> request</clix:lambda-list>
+        <clix:returns>string</clix:returns>
+        <clix:description>
+          Returns the value of the GET parameter (as provided via the
+          request URI) named by the string <clix:arg>name</clix:arg> as a
+          string (or <code>NIL</code> if there ain't no GET parameter
+          with this name). Note that only the first value will be
+          returned if the client provided more than one GET parameter
+          with the name <clix:arg>name</clix:arg>. See
+          also <clix:ref>GET-PARAMETERS*</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="post-parameter">
+        <clix:lambda-list>name <clix:lkw>optional</clix:lkw> request</clix:lambda-list>
+        <clix:returns>string</clix:returns>
+        <clix:description>
+          Returns the value of the POST parameter (as provided in the
+          request's body) named by the
+          string <clix:arg>name</clix:arg>. Note that only the first value
+          will be returned if the client provided more than one POST
+          parameter with the name <clix:arg>name</clix:arg>.  This value
+          will usually be a string (or <code>NIL</code> if there ain't
+          no POST parameter with this name). If, however, the browser
+          sent a <a class="none" name="upload">file</a> through
+          a <a href="http://www.faqs.org/rfcs/rfc2388.html">
+            <code>multipart/form-data</code>
+          </a> form, the value of this function is a three-element list
+          <pre>(path file-name content-type)</pre>
+          where <clix:arg>path</clix:arg> is a pathname denoting the place
+          were the uploaded file was
+          stored, <clix:arg>file-name</clix:arg> (a string) is the file
+          name sent by the browser, and <clix:arg>content-type</clix:arg>
+          (also a string) is the content type sent by the browser. The
+          file denoted by <clix:arg>path</clix:arg> will be deleted after
+          the request has been handled - you have to move or copy it
+          somewhere else if you want to keep it.
+          <p>
+            POST parameters will only be computed if the content type of
+            the request body was <code>multipart/form-data</code>
+            or <code>application/x-www-form-urlencoded</code>.  Although
+            this function is called <code>POST-PARAMETER</code>, you can
+            instruct Hunchentoot to compute these parameters for other
+            request methods by
+            setting <clix:ref>*METHODS-FOR-POST-PARAMETERS*</clix:ref>.
+          </p>
+          <p>
+            See also <clix:ref>POST-PARAMETERS</clix:ref>
+            and <clix:ref>*TMP-DIRECTORY*</clix:ref>.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="get-parameters*">
+        <clix:lambda-list><clix:lkw>optional</clix:lkw> request</clix:lambda-list>
+        <clix:returns>alist</clix:returns>
+        <clix:description>
+          Returns
+          an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#alist">alist</a>
+          of all GET parameters (as provided via the request
+          URI). The <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#car">car</a>
+          of each element of this list is the parameter's name while
+          the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#cdr">cdr</a>
+          is its value (as a string). The elements of this list are in
+          the same order as they were within the request URI. See
+          also <clix:ref>GET-PARAMETER</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="post-parameters*">
+        <clix:lambda-list><clix:lkw>optional</clix:lkw> request</clix:lambda-list>
+        <clix:returns>alist</clix:returns>
+        <clix:description>
+          Returns
+          an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#alist">alist</a>
+          of all POST parameters (as provided via the request's
+          body). The <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#car">car</a>
+          of each element of this list is the parameter's name while
+          the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#cdr">cdr</a>
+          is its value. The elements of this list are in the same order
+          as they were within the request's body.
+          <p>
+            See also <clix:ref>POST-PARAMETER</clix:ref>.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:special-variable name='*methods-for-post-parameters*'>
+        <clix:description>A list of the request method types (as keywords) for which
+        Hunchentoot will try to compute <clix:arg>post-parameters</clix:arg>.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function name='cookie-in'>
+        <clix:lambda-list>name
+        <clix:lkw>optional
+        </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>string
+        </clix:returns>
+        <clix:description>
+          Returns the cookie with the name <clix:arg>name</clix:arg> (a string) as sent by the
+          browser - or <code>NIL</code> if there is none.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='cookies-in*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>alist
+        </clix:returns>
+        <clix:description>Returns an alist of all cookies associated with the <clix:ref>REQUEST</clix:ref> object
+        <clix:arg>request</clix:arg>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='host'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>host
+        </clix:returns>
+        <clix:description>Returns the &#039;Host&#039; incoming http header value.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='query-string*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>string
+        </clix:returns>
+        <clix:description>
+          Returns the query string of the <clix:ref>REQUEST</clix:ref> object <clix:arg>request</clix:arg>. That&#039;s
+          the part behind the question mark (i.e. the GET parameters).
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='referer'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          Returns the &#039;Referer&#039; (sic!) http header.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='request-method*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>keyword
+        </clix:returns>
+        <clix:description>
+          Returns the request method as a Lisp keyword.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='request-uri*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>uri
+        </clix:returns>
+        <clix:description>
+          Returns the request URI.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='server-protocol*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>keyword
+        </clix:returns>
+        <clix:description>
+          Returns the request protocol as a Lisp keyword.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='user-agent'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          Returns the &#039;User-Agent&#039; http header.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='header-in*'>
+        <clix:lambda-list>name
+        <clix:lkw>optional
+        </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>header
+        </clix:returns>
+        <clix:description>
+          Returns the incoming header with name
+          <clix:arg>name</clix:arg>.  <clix:arg>name</clix:arg> can be
+          a keyword (recommended) or a string.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='headers-in*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>alist
+        </clix:returns>
+        <clix:description>
+          Returns an alist of the incoming headers associated with the
+          <clix:ref>REQUEST</clix:ref> object
+          <clix:arg>request</clix:arg>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='remote-addr*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>address
+        </clix:returns>
+        <clix:description>
+          Returns the address the current request originated from.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='remote-port*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>port
+        </clix:returns>
+        <clix:description>
+          Returns the port the current request originated from.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='local-addr*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>address
+        </clix:returns>
+        <clix:description>
+          The IP address of the local system that the client connected to.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='local-port*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>port
+        </clix:returns>
+        <clix:description>
+          The TCP port number of the local system that the client connected to.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='script-name*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>script-name
+        </clix:returns>
+        <clix:description>
+          Returns the file name of the <clix:ref>REQUEST</clix:ref>
+          object <clix:arg>request</clix:arg>. That&#039;s the
+          requested URI without the query string (i.e the GET
+          parameters).
+        </clix:description>
+      </clix:function>
+
+      <clix:accessor name='aux-request-value'>
+        <clix:lambda-list>symbol
+        <clix:lkw>optional
+        </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>value, present-p
+        </clix:returns>
+        <clix:description>
+          This accessor can be used to associate arbitrary
+          data with the the symbol <clix:arg>symbol</clix:arg> in the <clix:ref>REQUEST</clix:ref> object
+          <clix:arg>request</clix:arg>. <clix:arg>present-p</clix:arg> is true if such data was found, otherwise <code>NIL</code>.
+        </clix:description>
+      </clix:accessor>
+
+      <clix:function name='delete-aux-request-value'>
+        <clix:lambda-list>symbol
+        <clix:lkw>optional
+        </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>
+          Removes the value associated with <clix:arg>symbol</clix:arg> from the <clix:ref>REQUEST</clix:ref> object
+          <clix:arg>request</clix:arg>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='authorization'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> request
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          Returns as two values the user and password (if any) as
+          encoded in the &#039;AUTHORIZATION&#039; header.  Returns
+          <code>NIL</code> if there is no such header.
+        </clix:description>
+      </clix:function>
+
+      <clix:special-variable name='*hunchentoot-default-external-format*'>
+        <clix:description>
+          The external format used to compute the <clix:ref>REQUEST</clix:ref> object.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*file-upload-hook*'>
+        <clix:description>
+          If this is not <code>NIL</code>, it should be a unary
+          function which will be called with a pathname for each file
+          which is <a href="#upload">uploaded</a> to Hunchentoot.  The
+          pathname denotes the temporary file to which the uploaded
+          file is written.  The hook is called directly before the
+          file is created. At this point,
+          <clix:ref>*REQUEST*</clix:ref> is already bound to the
+          current <clix:ref>REQUEST</clix:ref> object, but obviously
+          you can't access the post parameters yet.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function name="raw-post-data">
+        <clix:lambda-list>
+          <clix:lkw>key</clix:lkw>
+          request external-format force-text force-binary want-stream
+        </clix:lambda-list>
+        <clix:returns>raw-body-or-stream</clix:returns>
+        <clix:description>
+          Returns the content sent by the client in the request body if
+          there was any (unless the content type
+          was <code>multipart/form-data</code> in which
+          case <code>NIL</code> is returned).  By default, the result is
+          a string if the type of the <code>Content-Type</code>
+          <a href="http://www.faqs.org/rfcs/rfc1590.html">media type</a>
+          is <code>"text"</code>, and a vector of octets otherwise.  In
+          the case of a string, the external format to be used to decode
+          the content will be determined from the <code>charset</code>
+          parameter sent by the client (or
+          otherwise <clix:ref>*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*</clix:ref>
+          will be used).
+          <p>
+            You can also provide an external format explicitly (through
+            <clix:arg>external-format</clix:arg>) in which case the result
+            will unconditionally be a string.  Likewise, you can provide
+            a true value for <clix:arg>force-text</clix:arg> which will
+            force Hunchentoot to act as if the type of the media type
+            had been <code>"text"</code>
+            (with <clix:arg>external-format</clix:arg> taking precedence
+            if provided).  Or you can provide a true value
+            for <clix:arg>force-binary</clix:arg> which means that you
+            want a vector of octets at any rate.  (If both
+            <clix:arg>force-text</clix:arg>
+            and <clix:arg>force-binary</clix:arg> are true, an error will
+            be signaled.)
+          </p>
+          <p>
+            If, however, you provide a true value
+            for <clix:arg>want-stream</clix:arg>, the other parameters are
+            ignored and you'll get the content (flexi) stream to read
+            from it yourself.  It is then your responsibility to read
+            the correct amount of data, because otherwise you won't be
+            able to return a response to the client.  The stream will
+            have
+            its <a href="http://weitz.de/flexi-streams/#flexi-streams">octet
+              position</a> set to <code>0</code>.  If the client provided
+            a <code>Content-Length</code> header, the stream will also
+            have a
+            corresponding <a href="http://weitz.de/flexi-streams/#flexi-streams">bound</a>,
+            so no matter whether the client used chunked encoding or
+            not, you can always read until EOF.
+          </p>
+          <p>
+            If the content type of the request
+            was <code>multipart/form-data</code>
+            or <code>application/x-www-form-urlencoded</code>, the
+            content has been read by Hunchentoot already and you can't
+            read from the stream anymore.
+          </p>
+          <p>
+            You can call <clix:ref>RAW-POST-DATA</clix:ref> more than once
+            per request, but you can't mix calls which have different
+            values for <clix:arg>want-stream</clix:arg>.
+          </p>
+          <p>
+            Note that this function is slightly misnamed because a
+            client can send content even if the request method is not
+            POST.
+          </p>
+        </clix:description>
+      </clix:function>
+
+
+      <clix:function name='recompute-request-parameters'>
+        <clix:lambda-list>
+          <clix:lkw>key
+          </clix:lkw> request external-format
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>
+          Recomputes the GET and POST parameters for the <clix:ref>REQUEST</clix:ref> object
+          <clix:arg>request</clix:arg>.  This only makes sense if you&#039;re switching external formats
+          during the request.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='process-request'>
+        <clix:lambda-list>request
+        </clix:lambda-list>
+        <clix:returns>nil
+        </clix:returns>
+        <clix:description>
+          This function is called by <clix:ref>PROCESS-CONNECTION</clix:ref>
+          after the incoming headers have been read.  It
+          calls <clix:ref>HANDLE-REQUEST</clix:ref> (and is more or less just a
+          thin wrapper around it) to select and call a
+          <a href="#request-dispatch">handler</a> and send the output of this handler to
+          the client.  Note that <clix:ref>PROCESS-CONNECTION</clix:ref> is
+          called once per connection and loops in case of a persistent
+          connection while <clix:ref>PROCESS-REQUEST</clix:ref> is called anew
+          for each request.
+          <p>
+            The return value of this function is ignored.
+          </p>
+          <p>
+            Like <clix:ref>PROCESS-CONNECTION</clix:ref>, this is another function
+            the behaviour of which you should only modify if you really, really
+            know what you're doing.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='handle-request'>
+        <clix:lambda-list>acceptor request
+        </clix:lambda-list>
+        <clix:returns>content
+        </clix:returns>
+        <clix:description>
+          This function is called by <clix:ref>PROCESS-REQUEST</clix:ref> once
+          the request has been read and a <clix:ref>REQUEST</clix:ref> object
+          has been created.  Its job is to actually handle the request, i.e. to
+          return something to the client.
+          <p>
+            The default method calls the
+            acceptor's <a href="#request-dispatch">request dispatcher</a>, but you
+            can of course implement a different behaviour.  The default method
+            also sets up <a href="#logging">standard error handling</a> for
+            the <a href="#request-dispatch">handler</a>.
+          </p>
+          <p>
+            Might be a good place to bind or rebind special variables which can
+            then be accessed by your <a href="#request-dispatch">handlers</a>.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='acceptor-dispatch-request'>
+        <clix:lambda-list>acceptor request
+        </clix:lambda-list>
+        <clix:returns>content
+        </clix:returns>
+        <clix:description>
+          This function is called to actually dispatch the request
+          once the standard logging and error handling has been set
+          up.  <clix:ref>ACCEPTOR</clix:ref> subclasses implement
+          methods for this function in order to perform their own
+          request routing.  If a method does not want to handle the
+          request, it is supposed to invoke <a
+          href="http://www.lispworks.com/documentation/HyperSpec/Body/f_call_n.htm">CALL-NEXT-METHOD</a>
+          so that the next <clix:ref>ACCEPTOR</clix:ref> in the
+          inheritance chain gets a chance to handle the request.
+        </clix:description>
+      </clix:function>
+
+      <clix:readers generic='true'>
+        <clix:listed-reader generic='true' name='cookies-in'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>cookies
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='get-parameters'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>get-parameters
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='header-in'>
+          <clix:lambda-list>name request
+          </clix:lambda-list>
+          <clix:returns>result
+          </clix:returns>
+          <clix:description>
+          </clix:description>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='headers-in'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>headers
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='post-parameters'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>post-parameters
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='query-string'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>query-string
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='remote-addr'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>address
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='remote-port'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>port
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='local-addr'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>address
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='local-port'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>port
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='request-acceptor'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>acceptor
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='request-method'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>method
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='request-uri'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>uri
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='server-protocol'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>protocol
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='script-name'>
+          <clix:lambda-list>request
+          </clix:lambda-list>
+          <clix:returns>result
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:description>
+          These are various generic readers which are used
+          to read information about a <clix:ref>REQUEST</clix:ref> object.  If you are writing a
+          <a href="#request-dispatch">handler</a>, you should <em>not</em> use these readers but instead utilize the
+          corresponding functions with an asterisk at the end of their name,
+          also listed in this section.  These generic readers are only
+          exported for users who want to create their own subclasses of
+          <clix:ref>REQUEST</clix:ref>.
+        </clix:description>
+
+      </clix:readers>
+
+    </clix:subchapter>
+
+    <clix:subchapter name="replies" title="Reply objects">
+
+      For each incoming request, the <a href="#acceptors">acceptor</a>
+      (in <clix:ref>PROCESS-CONNECTION</clix:ref>) creates
+      a <clix:ref>REPLY</clix:ref> object and makes it available
+      to <a href="#request-dispatch">handlers</a> via the special variable
+      <clix:ref>*REPLY*</clix:ref>.  This object contains all relevant
+      information (except for the content body) about the reply that will be
+      sent to the client and this section collects the functions which can
+      be used to query and modify such an object.  In all function
+      where <clix:arg>reply</clix:arg> is an optional or keyword parameter,
+      the default is <clix:ref>*REPLY*</clix:ref>.
+
+      <p>
+        If you need more fine-grained control over the behaviour of reply
+        objects, you can subclass <clix:ref>REPLY</clix:ref> and initialize
+        the <a href="#acceptor-reply-class"><code>REPLY-CLASS</code></a>
+        slot of the <clix:ref>ACCEPTOR</clix:ref> class accordingly.  The
+        acceptor will generate reply objects of the class named by this
+        slot.
+      </p>
+
+      <clix:class name='reply'>
+        <clix:description>
+          Objects of this class hold all the information about an
+          outgoing reply.  They are created automatically by
+          Hunchentoot and can be accessed and modified by the
+          corresponding <a href="#request-dispatch">handler</a>.
+        <p>
+          You should not mess with the slots of these objects directly, but you
+          can subclass <clix:ref>REPLY</clix:ref> in order to implement your own behaviour.  See the
+          <a href="#acceptor-reply-class"><code>:reply-class</code></a> initarg
+          of the <clix:ref>ACCEPTOR</clix:ref> class.
+        </p>
+        </clix:description>
+      </clix:class>
+
+      <clix:special-variable name='*reply*'>
+        <clix:description>
+          The current <clix:ref>REPLY</clix:ref> object in the context of a request.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:accessor name='header-out'>
+        <clix:lambda-list>name
+        <clix:lkw>optional
+        </clix:lkw> reply
+        </clix:lambda-list>
+        <clix:returns>string
+        </clix:returns>
+        <clix:description>
+          <clix:ref>HEADER-OUT</clix:ref> returns the outgoing http
+          header named by the keyword <clix:arg>name</clix:arg> if
+          there is one, otherwise <code>NIL</code>. <code>SETF</code>
+          of <clix:ref>HEADER-OUT</clix:ref> changes the current value
+          of the header named <clix:arg>name</clix:arg>. If no header
+          named <clix:arg>name</clix:arg> exists, it is created. For
+          backwards compatibility, <clix:arg>name</clix:arg> can also
+          be a string in which case the association between a header
+          and its name is case-insensitive.
+          <p>
+            Note that the header 'Set-Cookie' cannot be queried by
+            <clix:ref>HEADER-OUT</clix:ref> and must not be set by
+            <code>SETF</code> of <clix:ref>HEADER-OUT</clix:ref>.  See
+            also <clix:ref>HEADERS-OUT*</clix:ref>,
+            <clix:ref>CONTENT-TYPE*</clix:ref>,
+            <clix:ref>CONTENT-LENGTH*</clix:ref>,
+            <clix:ref>COOKIES-OUT*</clix:ref>, and
+            <clix:ref>COOKIE-OUT</clix:ref>.
+          </p>
+        </clix:description>
+      </clix:accessor>
+
+      <clix:function name='headers-out*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> reply
+        </clix:lambda-list>
+        <clix:returns>alist
+        </clix:returns>
+        <clix:description>Returns an alist of the outgoing headers associated with the
+        <clix:ref>REPLY</clix:ref> object <clix:arg>reply</clix:arg>.  See also <clix:ref>HEADER-OUT</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:accessor name='content-length*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> reply
+        </clix:lambda-list>
+        <clix:returns>content-length
+        </clix:returns>
+        <clix:description>
+          The outgoing &#039;Content-Length&#039; http header of <clix:arg>reply</clix:arg>.
+        </clix:description>
+      </clix:accessor>
+
+      <clix:accessor name='content-type*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> reply
+        </clix:lambda-list>
+        <clix:returns>content-type
+        </clix:returns>
+        <clix:description>
+          The outgoing &#039;Content-Type&#039; http header of <clix:arg>reply</clix:arg>.
+        </clix:description>
+      </clix:accessor>
+
+      <clix:function name='cookie-out'>
+        <clix:lambda-list>name
+        <clix:lkw>optional
+        </clix:lkw> reply
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          Returns the current value of the outgoing <a
+          href="#cookies">cookie</a> named
+          <clix:arg>name</clix:arg>. Search is case-sensitive.
+        </clix:description>
+      </clix:function>
+
+      <clix:accessor name='cookies-out*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> reply
+        </clix:lambda-list>
+        <clix:returns>alist
+        </clix:returns>
+        <clix:description>
+          Returns or sets an alist of the outgoing <a
+          href="#cookies">cookies</a> associated with the
+          <clix:ref>REPLY</clix:ref> object
+          <clix:arg>reply</clix:arg>.
+        </clix:description>
+      </clix:accessor>
+
+      <clix:accessor name='return-code*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> reply
+        </clix:lambda-list>
+        <clix:returns>return-code
+        </clix:returns>
+        <clix:description>
+          Gets or sets the http return code of
+          <clix:arg>reply</clix:arg>.  The return code of each
+          <clix:ref>REPLY</clix:ref> object is initially set to
+          <clix:ref>+HTTP-OK+</clix:ref>.
+        </clix:description>
+      </clix:accessor>
+
+      <clix:function name="send-headers">
+        <clix:returns>stream</clix:returns>
+        <clix:description>
+          Sends the initial status line and all headers as determined
+          by the <clix:ref>REPLY</clix:ref>
+          object <clix:ref>*REPLY*</clix:ref>.  Returns
+          a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_b.htm#binary">binary</a>
+          stream to which the body of the reply can be written.  Once
+          this function has been called, further changes
+          to <clix:ref>*REPLY*</clix:ref> don't have any effect.
+          Also, automatic handling of errors (i.e. sending the
+          corresponding status code to the browser, etc.) is turned
+          off for this request and functions
+          like <clix:ref>REDIRECT</clix:ref> or
+          to <clix:ref>ABORT-REQUEST-HANDLER</clix:ref> won't have the
+          desired effect once the headers are sent.
+          <p>
+            If your handlers return the full body as a string or as an
+            array of octets, you should <em>not</em> call this function.
+            If a handler calls <clix:ref>SEND-HEADERS</clix:ref> , its
+            return value is ignored.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:accessor name='reply-external-format*'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> reply
+        </clix:lambda-list>
+        <clix:returns>external-format
+        </clix:returns>
+        <clix:description>
+          Gets or sets the external format of <clix:arg>reply</clix:arg> which is used for character output.
+        </clix:description>
+      </clix:accessor>
+
+      <clix:special-variable name='*default-content-type*'>
+        <clix:description>
+          The default content-type header which is returned to the client.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:constants>
+        <clix:listed-constant name="+http-continue+"/>
+        <clix:listed-constant name="+http-switching-protocols+"/>
+        <clix:listed-constant name="+http-ok+"/>
+        <clix:listed-constant name="+http-created+"/>
+        <clix:listed-constant name="+http-accepted+"/>
+        <clix:listed-constant name="+http-non-authoritative-information+"/>
+        <clix:listed-constant name="+http-no-content+"/>
+        <clix:listed-constant name="+http-reset-content+"/>
+        <clix:listed-constant name="+http-partial-content+"/>
+        <clix:listed-constant name="+http-multi-status+"/>
+        <clix:listed-constant name="+http-multiple-choices+"/>
+        <clix:listed-constant name="+http-moved-permanently+"/>
+        <clix:listed-constant name="+http-moved-temporarily+"/>
+        <clix:listed-constant name="+http-see-other+"/>
+        <clix:listed-constant name="+http-not-modified+"/>
+        <clix:listed-constant name="+http-use-proxy+"/>
+        <clix:listed-constant name="+http-temporary-redirect+"/>
+        <clix:listed-constant name="+http-bad-request+"/>
+        <clix:listed-constant name="+http-authorization-required+"/>
+        <clix:listed-constant name="+http-payment-required+"/>
+        <clix:listed-constant name="+http-forbidden+"/>
+        <clix:listed-constant name="+http-not-found+"/>
+        <clix:listed-constant name="+http-method-not-allowed+"/>
+        <clix:listed-constant name="+http-not-acceptable+"/>
+        <clix:listed-constant name="+http-proxy-authentication-required+"/>
+        <clix:listed-constant name="+http-request-time-out+"/>
+        <clix:listed-constant name="+http-conflict+"/>
+        <clix:listed-constant name="+http-gone+"/>
+        <clix:listed-constant name="+http-length-required+"/>
+        <clix:listed-constant name="+http-precondition-failed+"/>
+        <clix:listed-constant name="+http-request-entity-too-large+"/>
+        <clix:listed-constant name="+http-request-uri-too-large+"/>
+        <clix:listed-constant name="+http-unsupported-media-type+"/>
+        <clix:listed-constant name="+http-requested-range-not-satisfiable+"/>
+        <clix:listed-constant name="+http-expectation-failed+"/>
+        <clix:listed-constant name="+http-failed-dependency+"/>
+        <clix:listed-constant name="+http-internal-server-error+"/>
+        <clix:listed-constant name="+http-not-implemented+"/>
+        <clix:listed-constant name="+http-bad-gateway+"/>
+        <clix:listed-constant name="+http-service-unavailable+"/>
+        <clix:listed-constant name="+http-gateway-time-out+"/>
+        <clix:listed-constant name="+http-version-not-supported+"/>
+        <clix:description>
+          The values of these constants are 100, 101, 200, 201, 202,
+          203, 204, 205, 206, 207, 300, 301, 302, 303, 304, 305, 307,
+          400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411,
+          412, 413, 414, 415, 416, 417, 424, 500, 501, 502, 503, 504,
+          and 505. See <clix:ref>RETURN-CODE</clix:ref>.
+        </clix:description>
+      </clix:constants>
+
+      <clix:readers generic='true'>
+        <clix:listed-reader generic='true' name='content-length'>
+          <clix:lambda-list>reply
+          </clix:lambda-list>
+          <clix:returns>content-length
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='content-type'>
+          <clix:lambda-list>reply
+          </clix:lambda-list>
+          <clix:returns>content-type
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:listed-reader generic='true' name='headers-out'>
+          <clix:lambda-list>reply
+          </clix:lambda-list>
+          <clix:returns>headers-out
+          </clix:returns>
+        </clix:listed-reader>
+
+        <clix:description>
+          These are various generic readers which are used
+          to read information about a <clix:ref>REPLY</clix:ref> object.  If you are writing a
+          <a href="#request-dispatch">handler</a>, you should <em>not</em> use these readers but instead utilize the
+          corresponding functions with an asterisk at the end of their name,
+          also listed in this section.  These generic readers are only
+          exported for users who want to create their own subclasses of
+          <clix:ref>REPLY</clix:ref>.
+        </clix:description>
+      </clix:readers>
+
+      <clix:accessors generic='true'>
+        <clix:listed-accessor generic='true' name='cookies-out'>
+          <clix:lambda-list>reply
+          </clix:lambda-list>
+          <clix:returns>result
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='return-code'>
+          <clix:lambda-list>reply
+          </clix:lambda-list>
+          <clix:returns>result
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:listed-accessor generic='true' name='reply-external-format'>
+          <clix:lambda-list>reply
+          </clix:lambda-list>
+          <clix:returns>result
+          </clix:returns>
+        </clix:listed-accessor>
+
+        <clix:description>
+          These are various generic accessors which are
+          used to query and modify a <clix:ref>REPLY</clix:ref> objects.  If
+          you are writing a
+          <a href="#request-dispatch">handler</a>, you should <em>not</em> use these
+          accessors but instead utilize the corresponding functions with an
+          asterisk at the end of their name, also listed in this section.
+          These generic accessors are only exported for users who want to
+          create their own subclasses of
+          <clix:ref>REPLY</clix:ref>.
+        </clix:description>
+      </clix:accessors>
+
+
+    </clix:subchapter>
+
+    <clix:subchapter name="sessions" title="Sessions">
+      Hunchentoot supports <em>sessions</em>: Once a <a href="#request-dispatch">request
+      handler</a> has called <clix:ref>START-SESSION</clix:ref>, Hunchentoot
+      uses either cookies or (if the client doesn't send the cookies
+      back) <a href="#*rewrite-for-session-urls*">rewrites URLs</a> to keep
+      track of this client, i.e. to provide a kind of 'state' for the
+      stateless http protocol. The session associated with the client is a
+      <a href="#session">CLOS object</a> which can be used
+      to <a href="#session-value">store arbitrary data</a> between requests.
+      <p>
+        Hunchentoot makes some reasonable effort to prevent eavesdroppers from
+        hijacking sessions (see below), but this should not be considered
+        really secure. Don't store sensitive data in sessions and rely solely
+        on the session mechanism as a safeguard against malicious users who
+        want to get at this data!
+      </p>
+      <p>
+        For each request there's one <clix:ref>SESSION</clix:ref> object which is accessible to the
+        <a href="#handler">handler</a> via the special
+        variable <clix:ref>*SESSION*</clix:ref>. This object holds all the
+        information available about the session and can be accessed with the
+        functions described in this chapter. Note that the internal structure
+        of <clix:ref>SESSION</clix:ref> objects should be considered opaque
+        and may change in future releases of Hunchentoot.
+      </p>
+      <p>
+        Sessions are automatically <a href="#session-verify">verified</a> for
+        validity and age when the <clix:ref>REQUEST</clix:ref> object is
+        instantiated, i.e. if <clix:ref>*SESSION*</clix:ref> is not NIL then
+        this session is valid (as far as Hunchentoot is concerned) and
+        not <a href="#session-too-old-p">too old</a>.  Old sessions
+        are <a href="#session-gc">automatically removed</a>.
+      </p>
+      <p>
+        Hunchentoot also provides a <clix:ref>SESSION-REGENERATE-COOKIE-VALUE</clix:ref>
+        function that creates a new cookie value. This helps to prevent against
+        <a href="https://www.owasp.org/index.php/Session_fixation">session fixation
+        attacks</a>, and should be used when a user logs in according to the application.
+      </p>
+
+      <clix:class name='session'>
+        <clix:description>
+          <clix:ref>SESSION</clix:ref> objects are
+          automatically maintained by Hunchentoot.  They should not be created
+          explicitly with <code>MAKE-INSTANCE</code> but implicitly
+          with <clix:ref>START-SESSION</clix:ref> and they should be treated as
+          opaque objects.
+          <p>
+            You can ignore Hunchentoot's <clix:ref>SESSION</clix:ref> objects and
+            <a href="#session-behaviour">implement your own sessions</a> if you provide corresponding methods for
+            <clix:ref>SESSION-COOKIE-VALUE</clix:ref>
+            and <clix:ref>SESSION-VERIFY</clix:ref>.
+          </p>
+        </clix:description>
+      </clix:class>
+
+      <clix:function name='start-session'>
+        <clix:lambda-list>
+        </clix:lambda-list>
+        <clix:returns>session
+        </clix:returns>
+        <clix:description>
+          Returns the current <clix:ref>SESSION</clix:ref>
+          object. If there is no current session, creates one and updates the
+          corresponding data structures. In this case the function will also
+          send a session cookie to the browser.
+        </clix:description>
+      </clix:function>
+
+      <clix:accessor name='session-value'>
+        <clix:lambda-list>symbol
+        <clix:lkw>optional
+        </clix:lkw> session
+        </clix:lambda-list>
+        <clix:returns>value, present-p
+        </clix:returns>
+        <clix:description>
+          This accessor can be used to associate arbitrary data with the the
+          symbol <clix:arg>symbol</clix:arg> in the <clix:ref>SESSION</clix:ref>
+          object <clix:arg>session</clix:arg>. <clix:arg>present-p</clix:arg> is
+          true if such data was found, otherwise <code>NIL</code>. The default
+          value for <clix:arg>session</clix:arg> is
+          <clix:ref>*SESSION*</clix:ref>.
+          <p>
+            If <code>SETF</code> of <clix:ref>SESSION-VALUE</clix:ref> is called
+            with <clix:arg>session</clix:arg> being <code>NIL</code> then a
+            session is automatically instantiated
+            with <clix:ref>START-SESSION</clix:ref>.
+          </p>
+        </clix:description>
+      </clix:accessor>
+
+      <clix:function name='delete-session-value'>
+        <clix:lambda-list>symbol
+        <clix:lkw>optional
+        </clix:lkw> session
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>
+          Removes the value associated with
+          <clix:arg>symbol</clix:arg> from
+          <clix:arg>session</clix:arg> if there is one.
+        </clix:description>
+      </clix:function>
+
+      <clix:special-variable name='*session*'>
+        <clix:description>
+          The current session while in the context of a request, or
+          <code>NIL</code>.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function name='remove-session'>
+        <clix:lambda-list>session
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>
+          Completely removes the <clix:ref>SESSION</clix:ref> object
+          <clix:arg>session</clix:arg> from Hunchentoot&#039;s
+          internal <a href="#session-db">session database</a>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='reset-sessions'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> acceptor
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>
+          Removes <em>all</em> stored sessions of
+          <clix:arg>acceptor</clix:arg>.  The default for
+          <clix:arg>acceptor</clix:arg> is
+          <clix:ref>*ACCEPTOR*</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='regenerate-session-cookie-value'>
+        <clix:lambda-list>session
+        </clix:lambda-list>
+        <clix:returns>cookie
+        </clix:returns>
+        <clix:description>
+          Regenerates the session cookie value. This should be used
+          when a user logs in according to the application to prevent
+          against session fixation attacks. The cookie value being
+          dependent on ID, USER-AGENT, REMOTE-ADDR, START, and
+          *SESSION-SECRET*, the only value we can change is START to
+          regenerate a new value. Since we're generating a new cookie,
+          it makes sense to have the session being restarted, in
+          time. That said, because of this fact, calling this function
+          twice in the same second will regenerate twice the same
+          value.
+        </clix:description>
+      </clix:function>
+
+      <clix:special-variable name='*rewrite-for-session-urls*'>
+        <clix:description>
+          Whether HTML pages should possibly be rewritten for cookie-less
+          session-management.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*content-types-for-url-rewrite*'>
+        <clix:description>
+          The content types for which url-rewriting is OK. See
+          <clix:ref>*REWRITE-FOR-SESSION-URLS*</clix:ref>.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*use-remote-addr-for-sessions*'>
+        <clix:description>
+          Whether the client&#039;s remote IP (as returned by <clix:ref>REAL-REMOTE-ADDR</clix:ref>)
+          should be encoded into the session string.  If this value is true, a
+          session will cease to be accessible if the client&#039;s remote IP changes.
+          <p>
+            This might for example be an issue if the client uses a proxy server
+            which doesn&#039;t send correct &#039;X-Forwarded-For&#039; headers.
+          </p>
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function generic='true' name='session-remote-addr'>
+        <clix:lambda-list>session
+        </clix:lambda-list>
+        <clix:returns>remote-addr
+        </clix:returns>
+        <clix:description>
+          The remote IP address of the client when this session was started (as
+          returned by <clix:ref>REAL-REMOTE-ADDR</clix:ref>).
+        </clix:description>
+      </clix:function>
+
+      <clix:special-variable name='*use-user-agent-for-sessions*'>
+        <clix:description>Whether the &#039;User-Agent&#039; header should
+        be encoded into the session string.  If this value is true, a session
+        will cease to be accessible if the client sends a different
+        &#039;User-Agent&#039; header.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function generic='true' name='session-user-agent'>
+        <clix:lambda-list>session
+        </clix:lambda-list>
+        <clix:returns>user-agent
+        </clix:returns>
+        <clix:description>
+          The incoming &#039;User-Agent&#039; header that
+          was sent when this session was created.
+        </clix:description>
+      </clix:function>
+
+      <clix:accessor generic='true' name='session-max-time'>
+        <clix:lambda-list>session
+        </clix:lambda-list>
+        <clix:returns>max-time
+        </clix:returns>
+        <clix:description>
+          Gets or sets the time (in seconds) after
+          which <clix:arg>session</clix:arg> expires if it's not used.
+        </clix:description>
+      </clix:accessor>
+
+
+      <clix:special-variable name='*session-max-time*'>
+        <clix:description>
+          The default time (in seconds) after which a session times out.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*session-gc-frequency*'>
+        <clix:description>
+          A session GC (see function <clix:ref>SESSION-GC</clix:ref>) will happen every
+          <clix:ref>*SESSION-GC-FREQUENCY*</clix:ref> requests (counting only
+          requests which create a new session) if this variable is
+          not <code>NIL</code>.  See <clix:ref>SESSION-CREATED</clix:ref>.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function name='session-gc'>
+        <clix:lambda-list>
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>
+          Removes sessions from the current session database which are
+          too old - see <clix:ref>SESSION-TOO-OLD-P</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='session-too-old-p'>
+        <clix:lambda-list>session
+        </clix:lambda-list>
+        <clix:returns>generalized-boolean
+        </clix:returns>
+        <clix:description>
+          Returns true if the <clix:ref>SESSION</clix:ref> object <clix:arg>session</clix:arg> has not been active in
+          the last <code>(session-max-time session)</code> seconds.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='session-id'>
+        <clix:lambda-list>session
+        </clix:lambda-list>
+        <clix:returns>session-id
+        </clix:returns>
+        <clix:description>
+          The unique ID (an INTEGER) of the session.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='session-start'>
+        <clix:lambda-list>session
+        </clix:lambda-list>
+        <clix:returns>universal-time
+        </clix:returns>
+        <clix:description>
+          The time this session was started.
+        </clix:description>
+      </clix:function>
+
+    </clix:subchapter>
+
+    <clix:subchapter name="session-behaviour" title="Customizing session behaviour">
+
+      For everyday session usage, you will probably just
+      use <clix:ref>START-SESSION</clix:ref>,
+      <clix:ref>SESSION-VALUE</clix:ref>,
+      and maybe <clix:ref>DELETE-SESSION-VALUE</clix:ref>
+      and <clix:ref>*SESSION*</clix:ref>.  However, there are two ways to
+      customize the way Hunchentoot maintains sessions.
+      <p>
+        One way is to mostly leave the session mechanism intact but to tweak
+        it a bit:
+        <ul>
+          <li>The publicly visible part of a session is encoded using a
+          <a href="#*session-secret*">secret</a> which you can set yourself.</li>
+          <li>And it is stored using a cookie (or GET
+          parameter) <a href="#session-cookie-name">name</a> that you can
+          override.</li>
+          <li>Each session receives a <a href="#next-session-id">new ID</a> when
+          it is created and you can implement a more robust way to do that.</li>
+          <li>You can arrange to be called whenever a session
+          is <a href="#session-created">created</a> to trigger some action.  You
+          might also do this to invent your own
+          session <a href="#session-gc">garbage collection</a>.</li>
+          <li>By default, all sessions are stored in a global alist in memory.
+          You can't change the alist part, but you can distribute your sessions
+          over different <a href="#session-db">"databases"</a>.</li>
+          <li>By default, every operation which modifies sessions or one of the
+          session databases is guarded by a global lock, but you can arrange to
+          <a href="#session-db-lock">provide</a> different locks for this.</li>
+        </ul>
+      </p>
+      <p>
+        The other way to customize Hunchentoot's sessions is to completely
+        replace them.  This is actually pretty easy: Create your own class to
+        store state (which doesn't have to and probably shouldn't inherit
+        from <clix:ref>SESSION</clix:ref>) and implement methods for
+        <clix:ref>SESSION-VERIFY</clix:ref>
+        and <clix:ref>SESSION-COOKIE-VALUE</clix:ref> - that's it.
+        Hunchentoot will continue to use cookies and/or to rewrite URLs to
+        keep track of session state and it will store "the current session"
+        (whatever that is in your implementation)
+        in <clix:ref>*SESSION*</clix:ref>.  Everything else (like persisting
+        sessions, GC, getting and setting values) you'll have to take care of
+        yourself and the other session functions
+        (like <clix:ref>START-SESSION</clix:ref> or
+        <clix:ref>SESSION-VALUE</clix:ref>) won't work anymore.  (Almost)
+        total freedom, but a lot of responsibility as well... :)
+      </p>
+
+      <clix:special-variable name='*session-secret*'>
+        <clix:description>
+          A random ASCII string that&#039;s used to encode the public
+          session data.  This variable is initially unbound and will
+          be set (using <clix:ref>RESET-SESSION-SECRET</clix:ref>) the
+          first time a session is created, if necessary.  You can
+          prevent this from happening if you set the value yourself
+          before starting <a href="#acceptors">acceptors</a>.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function name='reset-session-secret'>
+        <clix:lambda-list>
+        </clix:lambda-list>
+        <clix:returns>secret
+        </clix:returns>
+        <clix:description>
+          Sets <clix:ref>*SESSION-SECRET*</clix:ref> to a
+          new random value. All old sessions will cease to be valid.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='session-cookie-name'>
+        <clix:lambda-list>acceptor
+        </clix:lambda-list>
+        <clix:returns>name
+        </clix:returns>
+        <clix:description>
+          Returns the name (a string) of the cookie (or
+          the GET parameter) which is used to store a session on the client
+          side.  The default is to use the
+          string <code>&quot;hunchentoot-session&quot;</code>, but you can
+          specialize this function if you want another name.
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='session-created'>
+        <clix:lambda-list>acceptor new-session
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          This function is called whenever a new session
+          has been created.  There&#039;s a default method which might trigger
+          a <a href="#session-gc">session GC</a> based on the value of
+          <clix:ref>*SESSION-GC-FREQUENCY*</clix:ref>.
+          <p>
+            The return value is ignored.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='next-session-id'>
+        <clix:lambda-list>acceptor
+        </clix:lambda-list>
+        <clix:returns>id
+        </clix:returns>
+        <clix:description>
+          Returns the next sequential session ID, an
+          integer, which should be unique per session.  The default method uses
+          a simple global counter and isn&#039;t guarded by a lock.  For a
+          high-performance production environment you might consider using a
+          more robust implementation.
+        </clix:description>
+      </clix:function>
+
+      <clix:accessor generic='true' name='session-db'>
+        <clix:lambda-list>acceptor
+        </clix:lambda-list>
+        <clix:returns>database
+        </clix:returns>
+        <clix:description>
+          Returns the current session database which is an
+          alist where each car is a session&#039;s ID and the cdr is the
+          corresponding <clix:ref>SESSION</clix:ref> object itself.  The default
+          is to use a global list for all acceptors.
+        </clix:description>
+      </clix:accessor>
+
+      <clix:function generic='true' name='session-db-lock'>
+        <clix:lambda-list>acceptor
+        <clix:lkw>key
+        </clix:lkw> whole-db-p
+        </clix:lambda-list>
+        <clix:returns>lock
+        </clix:returns>
+        <clix:description>
+          A function which returns a lock that will be
+          used to prevent concurrent access to sessions.  The first argument
+          will be the <a href="#acceptors">acceptor</a> that handles the
+          current <a href="#requests">request</a>, the second argument is true
+          if the whole (current) session database is modified.  If it
+          is <code>NIL</code>, only one existing session in the database is
+          modified.
+          <p>
+            This function can return <code>NIL</code> which means that sessions or
+            session databases will be modified without a lock held (for example
+            for single-threaded environments).  The default is to always return a
+            global lock (ignoring the <clix:arg>acceptor</clix:arg> argument) for
+            Lisps that support threads and <code>NIL</code> otherwise.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='session-verify'>
+        <clix:lambda-list>request
+        </clix:lambda-list>
+        <clix:returns>session-or-nil
+        </clix:returns>
+        <clix:description>
+          Tries to get a session identifier from the cookies
+          (or alternatively from the GET parameters) sent by the client (see
+          <clix:ref>SESSION-COOKIE-NAME</clix:ref>
+          and <clix:ref>SESSION-COOKIE-VALUE</clix:ref>).  This identifier is
+          then checked for validity against the <clix:ref>REQUEST</clix:ref>
+          object
+          <clix:arg>request</clix:arg>.  On success the corresponding session object (if not too
+          old) is returned (and updated).  Otherwise <code>NIL</code> is returned.
+          <p>
+            A default method is provided and you only need to write your own one
+            if you want to maintain your own sessions.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function generic='true' name='session-cookie-value'>
+        <clix:lambda-list>session
+        </clix:lambda-list>
+        <clix:returns>string
+        </clix:returns>
+        <clix:description>
+          Returns a string which can be used to safely
+          restore the session <clix:arg>session</clix:arg> if as session has
+          already been established.  This is used as the value stored in the
+          session cookie or in the corresponding GET parameter and verified
+          by <clix:ref>SESSION-VERIFY</clix:ref>.
+          <p>
+            A default
+            method is provided and there&#039;s no reason to change it unless you
+            want to use your own session objects.
+          </p>
+        </clix:description>
+      </clix:function>
+
+    </clix:subchapter>
+
+    <clix:subchapter name="cookies" title="Cookies">
+
+      Outgoing cookies are stored in the request's <clix:ref>REPLY</clix:ref>
+      object (see <clix:ref>COOKIE-OUT</clix:ref>
+      and <clix:ref>COOKIES-OUT*</clix:ref>). They are CLOS objects
+      defined like this:
+
+      <pre>(defclass cookie ()
+  ((name :initarg :name
+         :reader <a class="noborder" name="cookie-name">cookie-name</a>
+         :type string
+         :documentation "The name of the cookie - a string.")
+   (value :initarg :value
+          :accessor <a class="noborder" name="cookie-value">cookie-value</a>
+          :initform ""
+          :documentation "The value of the cookie. Will be URL-encoded when sent to the browser.")
+   (expires :initarg :expires
+            :initform nil
+            :accessor <a class="noborder" name="cookie-expires">cookie-expires</a>
+            :documentation "The time (a universal time) when the cookie expires (or NIL).")
+   (max-age :initarg :max-age
+            :initform nil
+            :accessor <a class="noborder" name="cookie-max-age">cookie-max-age</a>
+            :documentation "The time delta (in seconds) after which the cookie expires (or NIL).")
+   (path :initarg :path
+         :initform nil
+         :accessor <a class="noborder" name="cookie-path">cookie-path</a>
+         :documentation "The path this cookie is valid for (or NIL).")
+   (domain :initarg :domain
+           :initform nil
+           :accessor <a class="noborder" name="cookie-domain">cookie-domain</a>
+           :documentation "The domain this cookie is valid for (or NIL).")
+   (secure :initarg :secure
+           :initform nil
+           :accessor <a class="noborder" name="cookie-secure">cookie-secure</a>
+           :documentation "A generalized boolean denoting whether this is a secure cookie.")
+   (http-only :initarg :http-only
+              :initform nil
+              :accessor <a class="noborder" name="cookie-http-only">cookie-http-only</a>
+              :documentation "A generalized boolean denoting whether this is a <a href="http://msdn2.microsoft.com/en-us/library/ms533046.aspx">HttpOnly</a> cookie.")))
+      </pre>
+
+      The <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_r.htm#reader">reader</a>
+      <clix:ref>COOKIE-NAME</clix:ref> and
+      the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#accessor">accessors</a>
+      <clix:ref>COOKIE-VALUE</clix:ref>, <clix:ref>COOKIE-EXPIRES</clix:ref>, <clix:ref>COOKIE-MAX-AGE</clix:ref>,
+      <clix:ref>COOKIE-PATH</clix:ref>, <clix:ref>COOKIE-DOMAIN</clix:ref>, <clix:ref>COOKIE-SECURE</clix:ref>,
+      and <clix:ref>COOKIE-HTTP-ONLY</clix:ref> are all exported from
+      the <code>HUNCHENTOOT</code> package.  For now, the class name itself is <em>not</em> exported.
+
+      <clix:function name="set-cookie">
+        <clix:lambda-list>
+          name <clix:lkw>key</clix:lkw> value expires path
+          domain secure http-only reply
+        </clix:lambda-list>
+        <clix:returns>cookie</clix:returns>
+        <clix:description>
+          Creates a <code>COOKIE</code> object from the parameters
+          provided to this function and adds it to the outgoing cookies
+          of the <a href="#replies"><code>REPLY</code> object</a>
+          <clix:arg>reply</clix:arg>. If a cookie with the same name
+          (case-sensitive) already exists, it is replaced. The default
+          for <clix:arg>reply</clix:arg>
+          is <clix:ref>*REPLY*</clix:ref>. The default
+          for <clix:arg>value</clix:arg> is the empty string.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="set-cookie*">
+        <clix:lambda-list>cookie <clix:lkw>optional</clix:lkw> reply</clix:lambda-list>
+        <clix:returns>cookie</clix:returns>
+        <clix:description>
+          Adds the <code>COOKIE</code> object <clix:arg>cookie</clix:arg>
+          to the outgoing cookies of
+          the <a href="#replies"><code>REPLY</code> object</a>
+          <clix:arg>reply</clix:arg>. If a cookie with the same name
+          (case-sensitive) already exists, it is replaced. The default
+          for <clix:arg>reply</clix:arg> is <clix:ref>*REPLY*</clix:ref>.
+        </clix:description>
+      </clix:function>
+    </clix:subchapter>
+
+    <clix:subchapter name="logging" title="Logging">
+      Hunchentoot can log accesses and diagnostic messages to two
+      separate destinations, which can be either files in the file
+      system or streams.  Logging can also be disabled by setting the
+      <clix:code>ACCESS-LOG-DESTINATION</clix:code> and
+      <clix:code>MESSAGE-LOG-DESTINATION</clix:code> slots in the
+      <clix:ref>ACCEPTOR</clix:ref> to <code>NIL</code>.  The two
+      slots can be initialized by providing the
+      :ACCESS-LOG-DESTINATION and :MESSAGE-LOG-DESTINATION
+      initialization arguments when creating the acceptor or set by
+      setting the slots through its
+      <clix:ref>ACCEPTOR-MESSAGE-LOG-DESTINATION</clix:ref> and
+      <clix:ref>ACCEPTOR-ACCESS-LOG-DESTINATION</clix:ref> accessors.
+      <p>
+        When the path for the message or accept log is set to a
+        variable holding an output stream, hunchentoots writes
+        corresponding log entries to that stream.  By default,
+        Hunchentoot logs to *STANDARD-ERROR*.
+      </p>
+      <p>
+        Access logging is done in a format similar to what
+        the Apache web server can write so that logfile analysis using
+        standard tools is possible.  Errors during request processing are
+        logged to a separate file.
+      </p>
+      <p>
+        The standard logging mechanism is deliberately simple and slow.  The
+        log files are opened for each log entry and closed again after
+        writing, and access to them is protected by a global lock.  Derived
+        acceptor classes can implement methods for the
+        <clix:ref>ACCEPTOR-LOG-MESSAGE</clix:ref> and
+        <clix:ref>ACCEPTOR-LOG-ACCESS</clix:ref> generic functions in order to
+        log differently (e.g. to a central logging server or in a different
+        file format.
+      </p>
+      <p>
+        Errors happening within a <a href="#request-dispatch">handler</a>
+        which are not caught by the handler itself are handled by
+        Hunchentoot by logging them to the established
+        <clix:ref>ACCEPTOR-MESSAGE-LOG-DESTINATION</clix:ref>.
+      </p>
+
+      <clix:function name='log-message*'>
+        <clix:lambda-list>log-level format-string
+        <clix:lkw>rest
+        </clix:lkw> format-arguments
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          Convenience function which calls the message
+          logger of the current acceptor (if there is one) with the same
+          arguments it accepts.  Returns <code>NIL</code> if there is no message
+          logger or whatever the message logger returns.
+          <p>
+            This is the function which Hunchentoot itself uses to log errors it
+            catches during request processing.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:special-variable name='*log-lisp-errors-p*'>
+        <clix:description>
+          Whether Lisp errors in request handlers should be logged.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*log-lisp-backtraces-p*'>
+        <clix:description>
+          Whether Lisp backtraces should be logged.  Only
+          has an effect if <clix:ref>*LOG-LISP-ERRORS-P*</clix:ref> is true
+          as well.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*log-lisp-warnings-p*'>
+        <clix:description>
+          Whether Lisp warnings in request handlers should be logged.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*lisp-errors-log-level*'>
+        <clix:description>
+          Log level for Lisp errors.  Should be one
+          of <code>:ERROR</code> (the default), <code>:WARNING</code>,
+          or <code>:INFO</code>.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*lisp-warnings-log-level*'>
+        <clix:description>
+          Log level for Lisp warnings.
+          Should be one of <code>:ERROR</code>, <code>:WARNING</code>
+          (the default), or <code>:INFO</code>.
+        </clix:description>
+      </clix:special-variable>
+    </clix:subchapter>
+
+    <clix:subchapter name="conditions" title="Conditions and error handling">
+      <p>
+        This section describes how Hunchentoot deals with exceptional
+        situations.  See also the secion about <a href="#logging">logging</a>.
+      </p>
+      <p>
+        When an error occurs while processing a request, Hunchentoot's
+        default behavior is to catch the error, log it and
+        optionally display it to the client in the HTML response.
+        This behavior can be customized through the values of a number
+        of special variables, which are documented below.
+      </p>
+
+      <clix:special-variable name='*catch-errors-p*'>
+        <clix:description>
+          If the value of this variable is <code>NIL</code>
+          (the default is <code>T</code>), then errors which happen while a
+          request is handled aren't <a href="#logging">caught as usual</a>, but
+          instead your
+          Lisp's <a
+          href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_d.htm#debugger">debugger</a>
+          is <a
+          href="http://www.lispworks.com/documentation/HyperSpec/Body/f_invoke.htm">invoked</a>.
+          This variable should obviously always be set to a <em>true</em> value
+          in a production environment.
+          See <clix:ref>MAYBE-INVOKE-DEBUGGER</clix:ref>
+          if you want to fine-tune this behaviour.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*show-lisp-errors-p*'>
+        <clix:description>
+          Whether Lisp errors should be shown in HTML output.  Note
+          that this only affects canned responses generated by Lisp.
+          If an error template is present for the "internal server
+          error" status code, this special variable is not used (see
+          <clix:ref>acceptor-status-message</clix:ref>).
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*show-lisp-backtraces-p*'>
+        <clix:description>
+          Whether Lisp backtraces should be shown in HTML output if
+          <clix:ref>*SHOW-LISP-ERRORS-P*</clix:ref> is true and an error occurs.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:function generic='true' name='maybe-invoke-debugger'>
+        <clix:lambda-list>condition
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>
+          This generic function is called whenever a
+          <a
+              href="http://www.lispworks.com/documentation/HyperSpec/Body/09_.htm">condition</a> <code><i>condition</i></code>
+          is signaled in Hunchentoot.  You might want to specialize it on
+          specific condition classes for debugging purposes.  The default
+          method <a
+          href="http://www.lispworks.com/documentation/HyperSpec/Body/f_invoke.htm">invokes
+          the debugger</a> with <clix:arg>condition</clix:arg> if
+          <clix:ref>*CATCH-ERRORS-P*</clix:ref> is <code>NIL</code>.
+        </clix:description>
+      </clix:function>
+
+      <clix:condition name='hunchentoot-condition'>
+        <clix:description>
+          Superclass for all conditions related to Hunchentoot.
+        </clix:description>
+      </clix:condition>
+
+      <clix:condition name='hunchentoot-error'>
+        <clix:description>
+          Superclass for all errors related to Hunchentoot and a subclass of <clix:ref>HUNCHENTOOT-CONDITION</clix:ref>.
+        </clix:description>
+      </clix:condition>
+
+      <clix:condition name='parameter-error'>
+        <clix:description>
+          Signalled if a function was called with incosistent or illegal parameters.  A subclass of <clix:ref>HUNCHENTOOT-ERROR</clix:ref>.
+        </clix:description>
+      </clix:condition>
+
+      <clix:condition name='hunchentoot-warning'>
+        <clix:description>
+          Superclass for all warnings related to Hunchentoot and a subclass of <clix:ref>HUNCHENTOOT-CONDITION</clix:ref>.
+        </clix:description>
+      </clix:condition>
+
+    </clix:subchapter>
+
+    <clix:subchapter name="misc" title="Miscellaneous">
+
+      Various functions and variables which didn't fit into one of the
+      other categories.
+
+      <clix:function name='abort-request-handler'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> result
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          This function can be called by a request handler
+          at any time to immediately abort handling the request.  This works as
+          if the handler had returned <clix:arg>result</clix:arg>.  See the
+          source code of <clix:ref>REDIRECT</clix:ref> for an example.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="handle-if-modified-since">
+        <clix:lambda-list>time <clix:lkw>optional</clix:lkw> request</clix:lambda-list>
+        <clix:returns>|</clix:returns>
+        <clix:description>
+          This function is designed to be used inside
+          a <a href="#request-dispatch">handler</a>. If the client has sent an
+          'If-Modified-Since' header
+          (see <a href="http://www.faqs.org/rfcs/rfc2616.html">RFC 2616</a>,
+          section 14.25) and the time specified matches the universal
+          time
+          <clix:arg>time</clix:arg> then the
+          header <clix:ref>+HTTP-NOT-MODIFIED+</clix:ref> with no content
+          is immediately returned to the client.
+          <p>
+            Note that for this function to be useful you should usually
+            send 'Last-Modified' headers back to the client. See the
+            code
+            of <clix:ref>CREATE-STATIC-FILE-DISPATCHER-AND-HANDLER</clix:ref>
+            for an example.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="handle-static-file">
+        <clix:lambda-list>path <clix:lkw>optional</clix:lkw> content-type</clix:lambda-list>
+        <clix:returns>nil</clix:returns>
+        <clix:description>
+          Sends the file denoted by the pathname designator
+          <clix:arg>path</clix:arg> with content type
+          <clix:arg>content-type</clix:arg> to the client.  Sets the
+          necessary handlers.  In particular the function employs
+          <clix:ref>HANDLE-IF-MODIFIED-SINCE</clix:ref>.
+          <p>
+            If <clix:arg>content-type</clix:arg> is <code>NIL</code> the
+            function tries to determine the correct content type from
+            the file's suffix or falls back
+            to <code>"application/octet-stream"</code> as a last resort.
+          </p>
+          <p>
+            Note that this function
+            calls <clix:ref>SEND-HEADERS</clix:ref> internally, so after
+            you've called it, the headers are sent and the return value
+            of your handler is ignored.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="redirect">
+        <clix:lambda-list>target <clix:lkw>key</clix:lkw> host port protocol add-session-id code</clix:lambda-list>
+        <clix:returns>|</clix:returns>
+        <clix:description>
+          Sends back appropriate headers to redirect the client
+          to <clix:arg>target</clix:arg> (a string).
+          <p>
+            If <clix:arg>target</clix:arg> is a full URL starting with a
+            scheme, <clix:arg>host</clix:arg>, <clix:arg>port</clix:arg>,
+            and <clix:arg>protocol</clix:arg> are ignored.
+            Otherwise, <clix:arg>target</clix:arg> should denote the path
+            part of a URL, <clix:arg>protocol</clix:arg> must be one of
+            the keywords <code>:HTTP</code> or <code>:HTTPS</code>, and
+            the URL to redirect to will be constructed
+            from <clix:arg>host</clix:arg>, <clix:arg>port</clix:arg>, <clix:arg>protocol</clix:arg>,
+            and <clix:arg>target</clix:arg>.
+          </p>
+          <p>
+            <clix:arg>code</clix:arg> must be a 3xx HTTP redirection
+            status code to send to the client.  It defaults to 302
+            ("Found").  If <clix:arg>host</clix:arg> is not provided,
+            the current host (see <clix:ref>HOST</clix:ref>) will be
+            used. If <clix:arg>protocol</clix:arg> is the keyword
+            <code>:HTTPS</code>, the client will be redirected to a
+            https URL, if it's <code>:HTTP</code> it'll be sent to a
+            http URL.  If both <clix:arg>host</clix:arg> and
+            <clix:arg>protocol</clix:arg> aren't provided, then the
+            value of <clix:arg>protocol</clix:arg> will match the
+            current request.
+          </p>
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="require-authorization">
+        <clix:lambda-list><clix:lkw>optional</clix:lkw> realm</clix:lambda-list>
+        <clix:returns>|</clix:returns>
+        <clix:description>
+          Sends back appropriate headers to require basic HTTP
+          authentication
+          (see <a href="http://www.faqs.org/rfcs/rfc2617.html">RFC 2617</a>)
+          for the realm <clix:arg>realm</clix:arg>. The default value
+          for <clix:arg>realm</clix:arg> is <code>"Hunchentoot"</code>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='no-cache'>
+        <clix:lambda-list>
+        </clix:lambda-list>
+        <clix:returns>|
+        </clix:returns>
+        <clix:description>
+          Adds appropriate headers to completely prevent caching on most browsers.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='ssl-p'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> acceptor
+        </clix:lambda-list>
+        <clix:returns>generalized-boolean
+        </clix:returns>
+        <clix:description>
+          Whether the current connection to the client is secure.  See <clix:ref>ACCEPTOR-SSL-P</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='reason-phrase'>
+        <clix:lambda-list>return-code
+        </clix:lambda-list>
+        <clix:returns>string
+        </clix:returns>
+        <clix:description>
+          Returns a reason phrase for the HTTP return code <clix:arg>return-code</clix:arg>
+          (which should be an integer) or <code>NIL</code> for return codes Hunchentoot
+          doesn&#039;t know.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='rfc-1123-date'>
+        <clix:lambda-list>
+          <clix:lkw>optional
+          </clix:lkw> time
+        </clix:lambda-list>
+        <clix:returns>string
+        </clix:returns>
+        <clix:description>
+          Generates a time string according to <a href="http://www.faqs.org/rfcs/rfc1123.html">RFC 1123</a>.  Default is current time.
+          This can be used to send a 'Last-Modified' header - see <clix:ref>HANDLE-IF-MODIFIED-SINCE</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='url-encode'>
+        <clix:lambda-list>string
+        <clix:lkw>optional
+        </clix:lkw> external-format
+        </clix:lambda-list>
+        <clix:returns>string
+        </clix:returns>
+        <clix:description>
+          URL-encodes a string using the external format <clix:arg>external-format</clix:arg>.  The default for <clix:arg>external-format</clix:arg> is the value of <clix:ref>*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='url-decode'>
+        <clix:lambda-list>string
+        <clix:lkw>optional
+        </clix:lkw> external-format
+        </clix:lambda-list>
+        <clix:returns>string
+        </clix:returns>
+        <clix:description>
+          Decodes a URL-encoded string which is assumed to
+          be encoded using the external
+          format <clix:arg>external-format</clix:arg>, i.e. this is the inverse
+          of <clix:ref>URL-ENCODE</clix:ref>. It is assumed that you'll rarely
+          need this function, if ever. But just in case - here it is.  The
+          default for <clix:arg>external-format</clix:arg> is the value
+          of <clix:ref>*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*</clix:ref>.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='escape-for-html'>
+        <clix:lambda-list>string
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          Escapes the characters #\&lt;, #\&gt;, #\&#039;, #\&quot;, and #\&amp; for HTML output.
+        </clix:description>
+      </clix:function>
+
+      <clix:function name="http-token-p">
+        <clix:lambda-list>object</clix:lambda-list>
+        <clix:returns>generalized-boolean</clix:returns>
+        <clix:description>
+          This function tests whether <clix:arg>object</clix:arg> is a
+          non-empty string which is a <em>token</em> according
+          to <a href="http://www.faqs.org/rfcs/rfc2068.html">RFC
+          2068</a> (i.e. whether it may be used for, say, cookie names).
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='mime-type'>
+        <clix:lambda-list>pathspec
+        </clix:lambda-list>
+        <clix:returns>result
+        </clix:returns>
+        <clix:description>
+          Given a pathname designator <clix:arg>pathspec</clix:arg> returns the <a href="http://en.wikipedia.org/wiki/Internet_media_type">MIME type</a>
+          (as a string) corresponding to the suffix of the file denoted by
+          <clix:arg>pathspec</clix:arg> (or <code>NIL</code>).
+        </clix:description>
+      </clix:function>
+
+      <clix:function name='within-request-p'>
+        <clix:lambda-list>
+        </clix:lambda-list>
+        <clix:returns>generalized-boolean
+        </clix:returns>
+        <clix:description>
+          Returns true if in the context of a request. Otherwise, <code>NIL</code>.
+        </clix:description>
+      </clix:function>
+
+      <clix:special-variable name="*tmp-directory*">
+        <clix:description>
+          This should be a pathname denoting a directory where temporary
+          files can be stored. It is used for <a href="#upload">file
+          uploads</a>.
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*header-stream*'>
+        <clix:description>
+          If this variable is not <code>NIL</code>, it should be bound to a stream to
+          which incoming and outgoing headers will be written for debugging
+          purposes.
+        </clix:description>
+      </clix:special-variable>
+
+
+      <clix:special-variable name='*cleanup-function*'>
+        <clix:description>
+          A designator for a function without arguments which is called on a
+          regular basis if <clix:ref>*CLEANUP-INTERVAL*</clix:ref> is not <code>NIL</code>.  The initial value is
+          the name of a function which invokes a garbage collection on 32-bit
+          versions of LispWorks.
+          <p>
+            This variable is only available on LispWorks.
+          </p>
+        </clix:description>
+      </clix:special-variable>
+
+      <clix:special-variable name='*cleanup-interval*'>
+        <clix:description>
+          Should be <code>NIL</code> or a positive integer.  The system calls
+          <clix:ref>*CLEANUP-FUNCTION*</clix:ref>
+          whenever <clix:ref>*CLEANUP-INTERVAL*</clix:ref> new worker threads
+          (counted globally across all acceptors) have been created unless the
+          value is <code>NIL</code>.  The initial value is 100.
+          <p>
+            This variable is only available on LispWorks.
+          </p>
+        </clix:description>
+      </clix:special-variable>
+    </clix:subchapter>
+  </clix:chapter>
+
+  <clix:chapter name="testing" title="Testing">
+    Hunchentoot comes with a test script which verifies that the
+    example web server responds as expected.  This test script uses the
+    <a href="http://weitz.de/drakma/">Drakma</a> HTTP client library
+    and thus shares a significant amount of its base code with
+    Hunchentoot itself.  Still, running the test script is a useful
+    confidence test, and it is also possible to run the script across
+    machines in order to verify a new Hunchentoot (or, for that matter
+    Drakma) port.
+    <p>
+      To run the confidence test, <a href="#teen-age">start
+      the example web server</a>.  Then, in your Lisp
+      listener, type
+<pre>(<a class="noborder" href="hunchentoot-test:test-hunchentoot">hunchentoot-test:test-hunchentoot</a> "http://localhost:4242")</pre>
+You will see some diagnostic output and a summary line that
+reports whether any tests have failed.  (You can also use the
+example certificate and key files in the test directory and
+start and test an https server instead.)
+    </p>
+
+    <clix:function name="hunchentoot-test:test-hunchentoot">
+      <clix:lambda-list>base-url <clix:lkw>key</clix:lkw></clix:lambda-list>
+      <clix:returns>|</clix:returns>
+      <clix:description>
+        Runs the built-in confidence
+        test.  <clix:arg>base-url</clix:arg> is the base URL to use
+        for testing, it should not have a trailing slash.  The keyword
+        arguments accepted are for future extension and should not
+        currently be used.
+        <p>
+          The script expects the Hunchentoot example test server to be
+          running at the given <clix:arg>base-url</clix:arg> and
+          retrieves various pages from that server, expecting certain
+          responses.
+        </p>
+      </clix:description>
+    </clix:function>
+  </clix:chapter>
+
+  <clix:chapter name="debugging" title="Debugging">
+    By default, Hunchentoot intercepts all errors that occur while
+    executing request handlers, logs them to the log file and displays
+    a static error page to the user.  While developing applications,
+    you may want to change that behavior so that the debugger is
+    invoked when an error occurs.  You can set
+    the <clix:ref>*CATCH-ERRORS-P*</clix:ref> to <code>NIL</code> to
+    make that happen.  Alternatively, you may want to have Hunchentoot
+    display detailed error information in the error response page.
+    You can set the <clix:ref>*SHOW-LISP-ERRORS-P*</clix:ref> to a
+    true value to make that happen.  If you don't want to see Lisp
+    backtraces in these error pages, you can
+    set <clix:ref>*SHOW-LISP-BACKTRACES-P*</clix:ref>
+    to <code>NIL</code>.
+  </clix:chapter>
+
+  <clix:chapter name="history" title="History">
+
+    Hunchentoot's predecessor <a href="http://weitz.de/tbnl/">TBNL</a>
+    (which is short for "To Be Named Later") grew over the years as a
+    toolkit that I used for various commercial and private
+    projects. In August 2003, Daniel Barlow started
+    a <a href="http://article.gmane.org/gmane.lisp.web/148">review of
+      web APIs</a> on
+    the <a href="http://www.red-bean.com/lispweb/">lispweb</a> mailing
+    list and
+    I <a href="http://article.gmane.org/gmane.lisp.web/153">described</a>
+    the API of my hitherto-unreleased bunch of code (and christened it
+    "TBNL").
+    <p>
+      It turned out that
+      <a href="http://www.jeffcaldwell.com/">Jeff Caldwell</a> had
+      worked on something similar so he emailed me and proposed to
+      join our efforts. As I had no immediate plans to release my code
+      (which was poorly organized, undocumented, and mostly
+      CMUCL-specific), I gave it to Jeff and he worked towards a
+      release. He added docstrings, refactored, added some stuff, and
+      based it on KMRCL to make it portable across several Lisp
+      implementations.
+    </p>
+    <p>
+      Unfortunately, Jeff is at least as busy as I am so he didn't
+      find the time to finish a full release.  But in spring 2004 I
+      needed a documented version of the code for a client of mine who
+      thought it would be good if the toolkit were publicly available
+      under an open source license. So I took Jeff's code, refactored
+      again (to sync with the changes I had done in the meantime), and
+      added documentation.  This resulted in TBNL 0.1.0 (which
+      initially required mod_lisp as its front-end).
+    </p>
+    <p>
+      In March 2005, Bob Hutchinson sent patches which enabled TBNL to
+      use other front-ends than mod_lisp.  This made me aware that
+      TBNL was already <em>almost</em> a full web server, so
+      eventually I wrote Hunchentoot which <em>was</em> a full web
+      server, implemented as a wrapper around TBNL.  Hunchentoot 0.1.0
+      was released at the end of 2005 and was originally
+      LispWorks-only.
+    </p>
+    <p>
+      Hunchentoot 0.4.0, released in October 2006, was the first
+      release which also worked with other Common Lisp
+      implementations.  It is a major rewrite and also incorporates
+      most of TBNL and replaces it completely.
+    </p>
+    <p>
+      Hunchentoot 1.0.0, released in February 2009, is again a major
+      rewrite and should be considered work in progress.  It moved to
+      using
+      the <a href="http://common-lisp.net/project/usocket/">usocket</a>
+      and <a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux
+      Threads</a> libraries for non-LispWorks Lisps, thereby removing most of
+      the platform dependent code.  Threading behaviour was made
+      controllable through the introduction of
+      taskmasters.  <a href="http://www.cliki.net/mod_lisp">mod_lisp</a>
+      support and several other things were removed in this release to
+      simplify the code base (and partly due to the lack of interest).
+      Several architectural changes (lots of them not
+      backwards-compatible) were made to ease customization of
+      Hunchentoot's behaviour.  A significant part of the 1.0.0
+      redesign was done
+      by <a href="http://netzhansa.blogspot.com/">Hans Hübner</a>.
+    </p>
+  </clix:chapter>
+
+  <clix:chapter name="index" title="Symbol index">
+
+    Here are all exported symbols of the <code>HUNCHENTOOT</code>
+    package in alphabetical order linked to their corresponding
+    documentation entries:
+
+    <clix:index/>
+
+  </clix:chapter>
+
+  <clix:chapter name="ack" title="Acknowledgements">
+
+    Thanks to Jeff Caldwell - TBNL would not have been released
+    without his efforts.  Thanks
+    to <a href="http://www.cliki.net/Stefan%20Scholl">Stefan
+    Scholl</a> and Travis Cross for various additions and fixes to
+    TBNL, to <a href="http://www.foldr.org/~michaelw/">Michael
+    Weber</a> for initial file upload code, and
+    to <a href="http://www.ltn.lv/~jonis/">Janis Dzerins</a> for
+    his <a href="http://common-lisp.net/project/rfc2388/">RFC 2388
+    code</a>. Thanks to Bob Hutchison for his code for multiple
+    front-ends (which made me realize that TBNL was already pretty
+    close to a "real" web server) and the initial UTF-8 example.
+    Thanks to <a href="http://netzhansa.blogspot.com/">Hans Hübner</a>
+    for a lot of architectural and implementation enhancements for the
+    1.0.0 release and also for transferring the documentation to sane
+    XHTML.  Thanks to John
+    Foderaro's <a href="http://opensource.franz.com/aserve/index.html">AllegroServe</a>
+    for inspiration.  Thanks to <a href="http://www.htg1.de/">Uwe von
+    Loh</a> for
+    the <a href="http://www.htg1.de/hunchentoot/hunchentoot.html">Hunchentoot
+    logo</a>.
+
+    <p>
+      Hunchentoot originally used code
+      from <a href="http://www.cliki.net/ACL-COMPAT">ACL-COMPAT</a>,
+      specifically the chunking code from Jochen Schmidt.  (This has been
+      replaced by <a href="http://weitz.de/chunga/">Chunga</a>.)  When I ported
+      Hunchentoot to other Lisps than LispWorks, I stole code from
+      ACL-COMPAT, <a href="http://www.cliki.net/kmrcl">KMRCL</a>,
+      and <a href="http://www.cliki.net/trivial-sockets">trivial-sockets</a> for
+      implementation-dependent stuff like sockets and MP.  (This has been replaced by
+      <a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux
+      Threads</a>
+      and <a href="http://common-lisp.net/project/usocket/">usocket</a>.)
+    </p>
+    <p>
+      Parts of this documentation were prepared
+      with <a href="http://weitz.de/documentation-template/">DOCUMENTATION-TEMPLATE</a>,
+      no animals were harmed.
+    </p>
+  </clix:chapter>
+  <p>
+    <a href='http://weitz.de/index.html'>BACK TO MY HOMEPAGE
+    </a>
+  </p>
+</clix:documentation>
diff --git a/deps/hunchentoot/easy-handlers.lisp b/deps/hunchentoot/easy-handlers.lisp
new file mode 100644 (file)
index 0000000..2874585
--- /dev/null
@@ -0,0 +1,347 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defvar *dispatch-table* (list 'dispatch-easy-handlers)
+  "A global list of dispatch functions.")
+
+(defvar *easy-handler-alist* nil
+  "An alist of \(URI acceptor-names function) lists defined by
+DEFINE-EASY-HANDLER.")
+
+(defun compute-real-name (symbol)
+  "Computes the `real' paramater name \(a string) from the Lisp
+symbol SYMBOL.  Used in cases where no parameter name is
+provided."
+  ;; we just downcase the symbol's name
+  (string-downcase symbol))
+
+(defun convert-parameter (argument type)
+  "Converts the string ARGUMENT to TYPE where TYPE is one of the
+symbols STRING, CHARACTERS, INTEGER, KEYWORD, or BOOLEAN - or
+otherwise a function designator for a function of one argument.
+ARGUMENT can also be NIL in which case this function also returns
+NIL unconditionally."
+  (when (listp argument)
+    ;; this if for the case that ARGUMENT is NIL or the result of a
+    ;; file upload
+    (return-from convert-parameter argument))
+  (case type
+    (string argument)
+    (character (and (= (length argument) 1)
+                    (char argument 0)))
+    (integer (ignore-errors* (parse-integer argument :junk-allowed t)))
+    (keyword (as-keyword argument :destructivep nil))
+    (boolean t)
+    (otherwise (funcall type argument))))
+
+(defun compute-simple-parameter (parameter-name type parameter-reader)
+  "Retrieves the parameter named PARAMETER-NAME using the reader
+PARAMETER-READER and converts it to TYPE."
+  (convert-parameter (funcall parameter-reader parameter-name) type))
+
+(defun compute-list-parameter (parameter-name type parameters)
+  "Retrieves all parameters from PARAMETERS which are named
+PARAMETER-NAME, converts them to TYPE, and returns a list of
+them."
+  (loop for (name . value) in parameters
+        when (string= name parameter-name)
+        collect (convert-parameter value type)))
+
+(defun compute-array-parameter (parameter-name type parameters)
+  "Retrieves all parameters from PARAMETERS which are named like
+\"PARAMETER-NAME[N]\" \(where N is a non-negative integer),
+converts them to TYPE, and returns an array where the Nth element
+is the corresponding value."
+  ;; see <http://common-lisp.net/pipermail/tbnl-devel/2006-September/000660.html>
+  #+:sbcl (declare (sb-ext:muffle-conditions warning))
+  (let* ((index-value-list
+          (loop for (full-name . value) in parameters
+                for index = (register-groups-bind (name index-string)
+                                ("^(.*)\\[(\\d+)\\]$" full-name)
+                              (when (string= name parameter-name)
+                                (parse-integer index-string)))
+                when index
+                collect (cons index (convert-parameter value type))))
+         (array (make-array (1+ (reduce #'max index-value-list
+                                        :key #'car
+                                        :initial-value -1))
+                            :initial-element nil)))
+    (loop for (index . value) in index-value-list
+          do (setf (aref array index) value))
+    array))
+
+(defun compute-hash-table-parameter (parameter-name type parameters key-type test-function)
+  "Retrieves all parameters from PARAMETERS which are named like
+\"PARAMETER-NAME{FOO}\" \(where FOO is any sequence of characters
+not containing curly brackets), converts them to TYPE, and
+returns a hash table with test function TEST-FUNCTION where the
+corresponding value is associated with the key FOO \(converted to
+KEY-TYPE)."
+  (let ((hash-table (make-hash-table :test test-function)))
+    (loop for (full-name . value) in parameters
+          for key = (register-groups-bind (name key-string)
+                        ("^(.*){([^{}]+)}$" full-name)
+                      (when (string= name parameter-name)
+                        (convert-parameter key-string key-type)))
+          when key
+          do (setf (gethash key hash-table)
+                   (convert-parameter value type)))
+    hash-table))
+
+(defun compute-parameter (parameter-name parameter-type request-type)
+  "Computes and returns the parameter\(s) called PARAMETER-NAME
+and converts it/them according to the value of PARAMETER-TYPE.
+REQUEST-TYPE is one of :GET, :POST, or :BOTH."
+  (when (member parameter-type '(list array hash-table))
+    (setq parameter-type (list parameter-type 'string)))
+  (let ((parameter-reader (ecase request-type
+                              (:get #'get-parameter)
+                              (:post #'post-parameter)
+                              (:both #'parameter)))
+        (parameters (and (listp parameter-type)
+                         (case request-type
+                           (:get (get-parameters*))
+                           (:post (post-parameters*))
+                           (:both (append (get-parameters*) (post-parameters*)))))))
+    (cond ((atom parameter-type)
+           (compute-simple-parameter parameter-name parameter-type parameter-reader))
+          ((and (null (cddr parameter-type))
+                (eq (first parameter-type) 'list))
+           (compute-list-parameter parameter-name (second parameter-type) parameters))
+          ((and (null (cddr parameter-type))
+                (eq (first parameter-type) 'array))
+           (compute-array-parameter parameter-name (second parameter-type) parameters))
+          ((and (null (cddddr parameter-type))
+                (eq (first parameter-type) 'hash-table))
+           (compute-hash-table-parameter parameter-name (second parameter-type) parameters
+                                         (or (third parameter-type) 'string)
+                                         (or (fourth parameter-type) 'equal)))
+          (t (parameter-error "Don't know what to do with parameter type ~S." parameter-type)))))
+
+(defun make-defun-parameter (description default-parameter-type default-request-type)
+  "Creates a keyword parameter to be used by DEFINE-EASY-HANDLER.
+DESCRIPTION is one of the elements of DEFINE-EASY-HANDLER's
+LAMBDA-LIST and DEFAULT-PARAMETER-TYPE and DEFAULT-REQUEST-TYPE
+are the global default values."
+  (when (atom description)
+    (setq description (list description)))
+  (destructuring-bind (parameter-name &key (real-name (compute-real-name parameter-name))
+                                           parameter-type init-form request-type)
+      description
+    `(,parameter-name (or (and (boundp '*request*)
+                               (compute-parameter ,real-name
+                                                  ,(or parameter-type default-parameter-type)
+                                                  ,(or request-type default-request-type)))
+                          ,init-form))))
+
+(defmacro define-easy-handler (description lambda-list &body body)
+  "Defines a handler with the body BODY and optionally registers
+it with a URI so that it will be found by DISPATCH-EASY-HANDLERS.
+DESCRIPTION is either a symbol NAME or a list matching the
+destructuring lambda list
+
+  (name &key uri acceptor-names default-parameter-type default-request-type).
+
+LAMBDA-LIST is a list the elements of which are either a symbol
+VAR or a list matching the destructuring lambda list
+
+  (var &key real-name parameter-type init-form request-type).
+
+The resulting handler will be a Lisp function with the name NAME
+and keyword parameters named by the VAR symbols.  Each VAR will
+be bound to the value of the GET or POST parameter called
+REAL-NAME \(a string) before BODY is executed.  If REAL-NAME is
+not provided, it will be computed by downcasing the symbol name
+of VAR.
+
+If URI \(which is evaluated) is provided, then it must be a string or
+a function designator for a function of one argument.  In this case,
+the handler will be returned by DISPATCH-EASY-HANDLERS, if URI is a
+string and the script name of a request is URI, or if URI designates a
+function and applying this function to the current request object
+returns a true value.
+
+ACCEPTOR-NAMES \(which is evaluated) can be a list of symbols which
+means that the handler will be returned by DISPATCH-EASY-HANDLERS in
+acceptors which have one of these names \(see ACCEPTOR-NAME).
+ACCEPTOR-NAMES can also be the symbol T which means that the handler
+will be returned by DISPATCH-EASY-HANDLERS in every acceptor.
+
+Whether the GET or POST parameter \(or both) will be taken into
+consideration, depends on REQUEST-TYPE which can
+be :GET, :POST, :BOTH, or NIL.  In the last case, the value of
+DEFAULT-REQUEST-TYPE \(the default of which is :BOTH) will be
+used.
+
+The value of VAR will usually be a string \(unless it resulted from a
+file upload in which case it won't be converted at all), but if
+PARAMETER-TYPE \(which is evaluated) is provided, the string will be
+converted to another Lisp type by the following rules:
+
+If the corresponding GET or POST parameter wasn't provided by the
+client, VAR's value will be NIL.  If PARAMETER-TYPE is 'STRING, VAR's
+value remains as is.  If PARAMETER-TYPE is 'INTEGER and the parameter
+string consists solely of decimal digits, VAR's value will be the
+corresponding integer, otherwise NIL.  If PARAMETER-TYPE is 'KEYWORD,
+VAR's value will be the keyword obtained by interning the upcased
+parameter string into the keyword package.  If PARAMETER-TYPE is
+'CHARACTER and the parameter string is of length one, VAR's value will
+be the single character of this string, otherwise NIL.  If
+PARAMETER-TYPE is 'BOOLEAN, VAR's value will always be T \(unless it
+is NIL by the first rule above, of course).  If PARAMETER-TYPE is any
+other atom, it is supposed to be a function designator for a unary
+function which will be called to convert the string to something else.
+
+Those were the rules for `simple' types, but PARAMETER-TYPE can
+also be a list starting with one of the symbols LIST, ARRAY, or
+HASH-TABLE.  The second value of the list must always be a simple
+parameter type as in the last paragraph - we'll call it the
+`inner type' below.
+
+In the case of 'LIST, all GET/POST parameters called REAL-NAME
+will be collected, converted to the inner type, and assembled
+into a list which will be the value of VAR.
+
+In the case of 'ARRAY, all GET/POST parameters which have a name
+like the result of
+
+  (format nil \"~A[~A]\" real-name n)
+
+where N is a non-negative integer, will be assembled into an
+array where the Nth element will be set accordingly, after
+conversion to the inner type.  The array, which will become the
+value of VAR, will be big enough to hold all matching parameters,
+but not bigger.  Array elements not set as described above will
+be NIL.  Note that VAR will always be bound to an array, which
+may be empty, so it will never be NIL, even if no appropriate
+GET/POST parameters are found.
+
+The full form of a 'HASH-TABLE parameter type is
+
+  (hash-table inner-type key-type test-function),
+
+but KEY-TYPE and TEST-FUNCTION can be left out in which case they
+default to 'STRING and 'EQUAL, respectively.  For this parameter
+type, all GET/POST parameters which have a name like the result
+of
+
+  (format nil \"~A{~A}\" real-name key)
+
+\(where KEY is a string that doesn't contain curly brackets) will
+become the values \(after conversion to INNER-TYPE) of a hash
+table with test function TEST-FUNCTION where KEY \(after
+conversion to KEY-TYPE) will be the corresponding key.  Note that
+VAR will always be bound to a hash table, which may be empty, so
+it will never be NIL, even if no appropriate GET/POST parameters
+are found.
+
+To make matters even more complicated, the three compound
+parameter types also have an abbreviated form - just one of the
+symbols LIST, ARRAY, or HASH-TABLE.  In this case, the inner type
+will default to 'STRING.
+
+If PARAMETER-TYPE is not provided or NIL, DEFAULT-PARAMETER-TYPE
+\(the default of which is 'STRING) will be used instead.
+
+If the result of the computations above would be that VAR would
+be bound to NIL, then INIT-FORM \(if provided) will be evaluated
+instead, and VAR will be bound to the result of this evaluation.
+
+Handlers built with this macro are constructed in such a way that
+the resulting Lisp function is useful even outside of
+Hunchentoot.  Specifically, all the parameter computations above
+will only happen if *REQUEST* is bound, i.e. if we're within a
+Hunchentoot request.  Otherwise, VAR will always be bound to the
+result of evaluating INIT-FORM unless a corresponding keyword
+argument is provided."
+  (when (atom description)
+    (setq description (list description)))
+  (destructuring-bind (name &key uri (acceptor-names t)
+                            (default-parameter-type ''string)
+                            (default-request-type :both))
+      description
+    `(progn
+       ,@(when uri
+           (list
+            (with-rebinding (uri)
+              `(progn
+                 (setq *easy-handler-alist*
+                       (delete-if (lambda (list)
+                                    (and (or (equal ,uri (first list))
+                                             (eq ',name (third list)))
+                                         (or (eq ,acceptor-names t)
+                                             (intersection ,acceptor-names
+                                                           (second list)))))
+                                  *easy-handler-alist*))
+                 (push (list ,uri ,acceptor-names ',name) *easy-handler-alist*)))))
+       (defun ,name (&key ,@(loop for part in lambda-list
+                                  collect (make-defun-parameter part
+                                                                default-parameter-type
+                                                                default-request-type)))
+         ,@body))))
+
+;; help the LispWorks IDE to find these definitions
+#+:lispworks
+(dspec:define-form-parser define-easy-handler (description)
+  `(,define-easy-handler ,(if (atom description) description (first description))))
+
+#+:lispworks
+(dspec:define-dspec-alias define-easy-handler (name)
+  `(defun ,name))
+
+(defun dispatch-easy-handlers (request)
+  "This is a dispatcher which returns the appropriate handler
+defined with DEFINE-EASY-HANDLER, if there is one."
+  (loop for (uri acceptor-names easy-handler) in *easy-handler-alist*
+        when (and (or (eq acceptor-names t)
+                      (find (acceptor-name *acceptor*) acceptor-names :test #'eq))
+                  (cond ((stringp uri)
+                         (string= (script-name request) uri))
+                        (t (funcall uri request))))
+        do (return easy-handler)))
+
+(defclass easy-acceptor (acceptor)
+  ()
+  (:documentation "This is the acceptor of the ``easy'' Hunchentoot framework."))
+
+(defmethod acceptor-dispatch-request ((acceptor easy-acceptor) request)
+  "The easy request dispatcher which selects a request handler
+based on a list of individual request dispatchers all of which can
+either return a handler or neglect by returning NIL."
+  (loop for dispatcher in *dispatch-table*
+     for action = (funcall dispatcher request)
+     when action return (funcall action)
+     finally (call-next-method)))
+
+#-:hunchentoot-no-ssl
+(defclass easy-ssl-acceptor (easy-acceptor ssl-acceptor)
+  ()
+  (:documentation "This is an acceptor that mixes the ``easy''
+  Hunchentoot with SSL connections."))
diff --git a/deps/hunchentoot/headers.lisp b/deps/hunchentoot/headers.lisp
new file mode 100644 (file)
index 0000000..d4fd8e1
--- /dev/null
@@ -0,0 +1,279 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defgeneric write-header-line (key value stream)
+  (:documentation "Accepts a string KEY and a Lisp object VALUE and
+writes them directly to the client as an HTTP header line.")
+  (:method (key (string string) stream)
+    (write-string key stream)
+    (write-char #\: stream)
+    (write-char #\Space stream)
+    (let ((start 0))
+      (loop
+         (let ((end (or (position #\Newline string :start start)
+                        (length string))))
+           ;; skip empty lines, as they confuse certain HTTP clients
+           (unless (eql start end)
+             (unless (zerop start)
+               (write-char #\Tab stream))
+             (write-string string stream :start start :end end)
+             (write-char #\Return stream)
+             (write-char #\Linefeed stream))
+           (setf start (1+ end))
+           (when (<= (length string) start)
+             (return))))))
+  (:method (key (number number) stream)
+    (write-header-line key (write-to-string number :escape nil :readably nil :base 10) stream))
+  (:method (key value stream)
+    (write-header-line key (princ-to-string value) stream)))
+
+(defun maybe-add-charset-to-content-type-header (content-type external-format)
+  "Given the contents of a CONTENT-TYPE header, add a charset=
+  attribute describing the given EXTERNAL-FORMAT if no charset=
+  attribute is already present and the content type is a text content
+  type.  Returns the augmented content type."
+  (if (and (cl-ppcre:scan "(?i)^text" content-type)
+           (not (cl-ppcre:scan "(?i);\\s*charset=" content-type)))
+      (format nil "~A; charset=~(~A~)" content-type (flex:external-format-name external-format))
+      content-type))
+
+(defun start-output (return-code &optional (content nil content-provided-p))
+  "Sends all headers and maybe the content body to
+*HUNCHENTOOT-STREAM*.  Returns immediately and does nothing if called
+more than once per request.  Called by PROCESS-REQUEST and/or
+SEND-HEADERS.  The RETURN-CODE argument represents the integer return
+code of the request.  The corresponding reason phrase is determined by
+calling the REASON-PHRASE function.  The CONTENT provided represents
+the body data to send to the client, if any.  If it is not specified,
+no body is written to the client.  The handler function is expected to
+directly write to the stream in this case.
+
+Returns the stream that is connected to the client."
+  (let* ((chunkedp (and (acceptor-output-chunking-p *acceptor*)
+                        (eq (server-protocol *request*) :http/1.1)
+                        ;; only turn chunking on if the content
+                        ;; length is unknown at this point...
+                        (null (or (content-length*) content-provided-p))))
+         (request-method (request-method *request*))
+         (head-request-p (eq request-method :head))
+         content-modified-p)
+    (multiple-value-bind (keep-alive-p keep-alive-requested-p)
+        (keep-alive-p *request*)
+      (when keep-alive-p
+        (setq keep-alive-p
+              ;; use keep-alive if there's a way for the client to
+              ;; determine when all content is sent (or if there
+              ;; is no content)
+              (or chunkedp
+                  head-request-p
+                  (eql (return-code*) +http-not-modified+)
+                  (content-length*)
+                  content)))
+      ;; now set headers for keep-alive and chunking
+      (when chunkedp
+        (setf (header-out :transfer-encoding) "chunked"))
+      (cond (keep-alive-p
+             (setf *finish-processing-socket* nil)
+             (when (and (acceptor-read-timeout *acceptor*)
+                        (or (not (eq (server-protocol *request*) :http/1.1))
+                            keep-alive-requested-p))
+               ;; persistent connections are implicitly assumed for
+               ;; HTTP/1.1, but we return a 'Keep-Alive' header if the
+               ;; client has explicitly asked for one
+               (unless (header-out :connection) ; allowing for handler overriding
+                 (setf (header-out :connection) "Keep-Alive"))
+               (setf (header-out :keep-alive)
+                     (format nil "timeout=~D" (acceptor-read-timeout *acceptor*)))))
+            ((not (header-out-set-p :connection))
+             (setf (header-out :connection) "Close"))))
+    (unless (and (header-out-set-p :server)
+                 (null (header-out :server)))
+      (setf (header-out :server) (or (header-out :server)
+                                     (acceptor-server-name *acceptor*))))
+    (setf (header-out :date) (rfc-1123-date))
+    (when (and (stringp content)
+               (not content-modified-p)
+               (starts-with-one-of-p (or (content-type*) "")
+                                     *content-types-for-url-rewrite*))
+      ;; if the Content-Type header starts with one of the strings
+      ;; in *CONTENT-TYPES-FOR-URL-REWRITE* then maybe rewrite the
+      ;; content
+      (setq content (maybe-rewrite-urls-for-session content)))
+    (when (stringp content)
+      ;; if the content is a string, convert it to the proper external format
+      (setf content (string-to-octets content :external-format (reply-external-format*))
+            (content-type*) (maybe-add-charset-to-content-type-header (content-type*)
+                                                                      (reply-external-format*))))
+    (when content
+      ;; whenever we know what we're going to send out as content, set
+      ;; the Content-Length header properly; maybe the user specified
+      ;; a different content length, but that will wrong anyway
+      (setf (header-out :content-length) (length content)))
+    ;; send headers only once
+    (when *headers-sent*
+      (return-from start-output))
+    (setq *headers-sent* t)
+    (send-response *acceptor*
+                   *hunchentoot-stream*
+                   return-code
+                   :headers (headers-out*)
+                   :cookies (cookies-out*)
+                   :content (unless head-request-p
+                              content))
+    ;; when processing a HEAD request, exit to return from PROCESS-REQUEST
+    (when head-request-p
+      (throw 'request-processed nil))
+    (when chunkedp
+      ;; turn chunking on after the headers have been sent
+      (unless (typep *hunchentoot-stream* 'chunked-stream)
+        (setq *hunchentoot-stream* (make-chunked-stream *hunchentoot-stream*)))
+      (setf (chunked-stream-output-chunking-p *hunchentoot-stream*) t))
+    *hunchentoot-stream*))
+
+(defun send-response (acceptor stream status-code
+                      &key headers cookies content)
+  "Send a HTTP response to the STREAM and log the event in ACCEPTOR.
+  STATUS-CODE is the HTTP status code used in the response.  HEADERS
+  and COOKIES are used to create the response header.  If CONTENT is
+  provided, it is sent as the response body.
+
+  If *HEADER-STREAM* is not NIL, the response headers are written to
+  that stream when they are written to the client.
+
+  STREAM is returned."
+  (when content
+    (setf (content-length*) (length content)))
+  (when (content-length*)
+    (if (assoc :content-length headers)
+        (setf (cdr (assoc :content-length headers)) (content-length*))
+        (push (cons :content-length (content-length*)) headers)))
+  ;; access log message
+  (acceptor-log-access acceptor :return-code status-code)
+  ;; Read post data to clear stream - Force binary mode to avoid OCTETS-TO-STRING overhead.
+  (raw-post-data :force-binary t)
+  (let* ((client-header-stream (flex:make-flexi-stream stream :external-format +latin-1+))
+         (header-stream (if *header-stream*
+                            (make-broadcast-stream *header-stream* client-header-stream)
+                            client-header-stream)))
+    ;; start with status line
+    (format header-stream "HTTP/1.1 ~D ~A~C~C" status-code (reason-phrase status-code) #\Return #\Linefeed)
+    ;; write all headers from the REPLY object
+    (loop for (key . value) in headers
+       when value
+       do (write-header-line (as-capitalized-string key) value header-stream))
+    ;; now the cookies
+    (loop for (nil . cookie) in cookies
+       do (write-header-line "Set-Cookie" (stringify-cookie cookie) header-stream))
+    (format header-stream "~C~C" #\Return #\Linefeed))
+  ;; now optional content
+  (when content
+    (write-sequence content stream)
+    (finish-output stream))
+  stream)
+
+(defun send-headers ()
+  "Sends the initial status line and all headers as determined by the
+REPLY object *REPLY*.  Returns a binary stream to which the body of
+the reply can be written.  Once this function has been called, further
+changes to *REPLY* don't have any effect.  Also, automatic handling of
+errors \(i.e. sending the corresponding status code to the browser,
+etc.) is turned off for this request.  If your handlers return the
+full body as a string or as an array of octets you should NOT call
+this function.
+
+This function does not return control to the caller during HEAD
+request processing."
+  (start-output (return-code*)))
+
+(defun read-initial-request-line (stream)
+  "Reads and returns the initial HTTP request line, catching permitted
+errors and handling *BREAK-EVEN-WHILE-READING-REQUEST-TYPE-P*.  If no
+request could be read, returns NIL.  At this point, both an
+end-of-file as well as a timeout condition are normal; end-of-file
+will occur when the client has decided to not send another request but
+to close the connection instead, a timeout indicates that the
+connection timeout established by Hunchentoot has expired and we do
+not want to wait for another request any longer."
+  (handler-case
+      (let ((*current-error-message* "While reading initial request line:"))
+        (with-mapped-conditions ()
+          (read-line* stream)))
+    ((or end-of-file #-:lispworks usocket:timeout-error) ())))
+
+(defun send-bad-request-response (stream &optional additional-info)
+  "Send a ``Bad Request'' response to the client."
+  (write-sequence (flex:string-to-octets
+                   (format nil "HTTP/1.0 ~D ~A~C~CConnection: close~C~C~C~CYour request could not be interpreted by this HTTP server~C~C~@[~A~]~C~C"
+                           +http-bad-request+ (reason-phrase +http-bad-request+) #\Return #\Linefeed
+                           #\Return #\Linefeed #\Return #\Linefeed #\Return #\Linefeed additional-info #\Return #\Linefeed))
+                  stream))
+
+(defun printable-ascii-char-p (char)
+  (<= 32 (char-code char) 126))
+
+(defun get-request-data (stream)
+  "Reads incoming headers from the client via STREAM.  Returns as
+multiple values the headers as an alist, the method, the URI, and the
+protocol of the request."
+  (with-character-stream-semantics
+   (let ((first-line (read-initial-request-line stream)))
+     (when first-line
+       (unless (every #'printable-ascii-char-p first-line)
+         (send-bad-request-response stream "Non-ASCII character in request line")
+         (return-from get-request-data nil))
+       (destructuring-bind (&optional method url-string protocol)
+           (split "\\s+" first-line :limit 3)
+         (unless url-string
+           (send-bad-request-response stream)
+           (return-from get-request-data nil))
+         (when *header-stream*
+           (format *header-stream* "~A~%" first-line))
+         (let ((headers (and protocol (read-http-headers stream *header-stream*))))
+           ;; maybe handle 'Expect: 100-continue' header
+           (when-let (expectations (cdr (assoc* :expect headers)))
+             (when (member "100-continue" (split "\\s*,\\s*" expectations) :test #'equalp)
+               ;; according to 14.20 in the RFC - we should actually
+               ;; check if we have to respond with 417 here
+               (let ((continue-line
+                       (format nil "HTTP/1.1 ~D ~A"
+                               +http-continue+
+                               (reason-phrase +http-continue+))))
+                 (write-sequence (map 'list #'char-code continue-line) stream)
+                 (write-sequence +crlf+ stream)
+                 (write-sequence +crlf+ stream)
+                 (force-output stream)
+                 (when *header-stream*
+                   (format *header-stream* "~A~%" continue-line)))))
+           (values headers
+                   (as-keyword method)
+                   url-string
+                   (if protocol
+                       (as-keyword (trim-whitespace protocol))
+                       :http/0.9))))))))
diff --git a/deps/hunchentoot/hunchentoot.asd b/deps/hunchentoot/hunchentoot.asd
new file mode 100644 (file)
index 0000000..e0c045a
--- /dev/null
@@ -0,0 +1,102 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(defpackage :hunchentoot-asd
+  (:use :cl :asdf))
+
+(in-package :hunchentoot-asd)
+
+(defsystem :hunchentoot
+  :serial t
+  :version "1.2.38"
+  :description "Hunchentoot is a HTTP server based on USOCKET and
+  BORDEAUX-THREADS.  It supports HTTP 1.1, serves static files, has a
+  simple framework for user-defined handlers and can be extended
+  through subclassing."
+  :license "BSD-2-Clause"
+  :depends-on (:chunga
+               :cl-base64
+               :cl-fad
+               :cl-ppcre
+               :flexi-streams
+               #-(or :lispworks :hunchentoot-no-ssl) :cl+ssl
+               :md5
+               :rfc2388
+               :trivial-backtrace
+               #-:lispworks :usocket
+               #-:lispworks :bordeaux-threads)
+  :components ((:module url-rewrite
+                :serial t
+                :components ((:file "packages")
+                             (:file "specials")
+                             (:file "primitives")
+                             (:file "util")
+                             (:file "url-rewrite")))
+               (:file "packages")
+               #+:lispworks (:file "lispworks")
+               #-:lispworks (:file "compat")
+               (:file "specials")
+               (:file "conditions")
+               (:file "mime-types")
+               (:file "util")
+               (:file "log")
+               (:file "cookie")
+               (:file "reply")
+               (:file "request")
+               (:file "session")
+               (:file "misc")
+               (:file "headers")
+               (:file "set-timeouts")
+               (:file "taskmaster")
+               (:file "acceptor")
+               #-:hunchentoot-no-ssl (:file "ssl")
+               (:file "easy-handlers")))
+
+(defsystem :hunchentoot-test
+  :description "Self test functionality for the Hunchentoot HTTP server."
+  :components ((:module "test"
+                        :serial t
+                        :components ((:file "packages")
+                                     (:file "test-handlers")
+                                     (:file "script-engine")
+                                     (:file "script"))))
+  :depends-on (:hunchentoot :cl-who :cl-ppcre :drakma))
+
+(defmethod perform ((o test-op) (c (eql (find-system 'hunchentoot))))
+  (load (merge-pathnames "run-test.lisp" (system-source-directory c))))
+
+(defsystem :hunchentoot-dev
+    :description "Development tools for Hunchentoot development and releases"
+    :components ((:file "make-docstrings"))
+    :depends-on (:hunchentoot
+                 :hunchentoot-test
+                 :xpath
+                 :cxml-stp
+                 :swank))
diff --git a/deps/hunchentoot/lispworks.lisp b/deps/hunchentoot/lispworks.lisp
new file mode 100755 (executable)
index 0000000..4fd20a3
--- /dev/null
@@ -0,0 +1,145 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  ;; make sure socket code is loaded
+  (require "comm"))
+
+(defun get-env-variable-as-directory (name)
+  "Retrieves the environment variable named NAME and interprets it as
+the pathname of a directory which is returned."
+  (lw:when-let (string (lw:environment-variable name))
+    (when (plusp (length string))
+      (cond ((find (char string (1- (length string))) "\\/" :test #'char=) string)
+            (t (lw:string-append string "/"))))))
+
+(defmacro with-rebinding (bindings &body body)
+  "Renaming LW:REBINDING for better indentation."
+  `(lw:rebinding ,bindings ,@body))
+
+#+(and :lispworks4.4 (or :win32 :linux))
+(let ((id :system-cons-free-chain))
+  (unless (scm::patch-id-loaded-p id)
+    (error "You need a patch to improve the performance of this code. Request patch ~S for ~A for ~A from lisp-support@lispworks.com using the Report Bug command."
+          id (lisp-implementation-type)
+          #+:win32 "Windows"
+          #+:linux "Linux")))
+
+(defvar *cleanup-interval* 100
+  "Should be NIL or a positive integer.  The system calls
+*CLEANUP-FUNCTION* whenever *CLEANUP-INTERVAL* new worker threads
+\(counted globally across all acceptors) have been created unless the
+value is NIL.  The initial value is 100.
+
+This variable is only available on LispWorks.")
+
+(defvar *cleanup-function* 'cleanup-function
+  "A designator for a function without arguments which is called on a
+regular basis if *CLEANUP-INTERVAL* is not NIL.  The initial value is
+the name of a function which invokes a garbage collection on 32-bit
+versions of LispWorks.
+
+This variable is only available on LispWorks.")
+
+(defvar *worker-counter* 0
+  "Internal counter used to count worker threads.  Needed for
+*CLEANUP-FUNCTION*.")
+
+(defun cleanup-function ()
+  "The default for *CLEANUP-FUNCTION*.  Invokes a GC on 32-bit
+LispWorks."
+  #-:lispworks-64bit
+  (hcl:mark-and-sweep 2))
+
+(defun get-peer-address-and-port (socket)
+  "Returns the peer address and port of the socket SOCKET as two
+values.  The address is returned as a string in dotted IP address
+notation."
+  (multiple-value-bind (peer-addr peer-port)
+      (comm:get-socket-peer-address socket)
+    (values (ignore-errors (comm:ip-address-string peer-addr)) peer-port)))
+
+(defun get-local-address-and-port (socket)
+  "Returns the local address and port of the socket SOCKET as two
+values.  The address is returned as a string in dotted IP address
+notation."
+  (multiple-value-bind (local-addr local-port)
+      (comm:get-socket-address socket)
+    (values (ignore-errors (comm:ip-address-string local-addr)) local-port)))
+
+(eval-when (:compile-toplevel :load-toplevel)
+  (when (let ((sym (find-symbol "STREAM-READ-TIMEOUT" :stream)))
+          (and sym (fboundp sym)))
+    (pushnew :stream-has-timeouts *features*)))
+
+(defun make-socket-stream (socket acceptor)
+  "Returns a stream for the socket SOCKET.  The ACCEPTOR argument is
+used to set the timeouts."
+  #-stream-has-timeouts
+  (when (acceptor-write-timeout acceptor)
+    (parameter-error "You need LispWorks 5 or higher for write timeouts."))
+  (make-instance 'comm:socket-stream
+                 :socket socket
+                 :direction :io
+                 :read-timeout (acceptor-read-timeout acceptor)
+                 #+stream-has-timeouts #+stream-has-timeouts
+                 :write-timeout (acceptor-write-timeout acceptor)
+                 :element-type 'octet))
+
+(defun make-lock (name)
+  "Simple wrapper to allow LispWorks and Bordeaux Threads to coexist."
+  (mp:make-lock :name name))
+
+(defmacro with-lock-held ((lock) &body body)
+  "Simple wrapper to allow LispWorks and Bordeaux Threads to coexist."
+  `(mp:with-lock (,lock) ,@body))
+
+;; some help for the IDE
+(dspec:define-dspec-alias defvar-unbound (name)
+  `(defparameter ,name))
+
+(dspec:define-dspec-alias def-http-return-code (name)
+  `(defconstant ,name))
+
+(editor:setup-indent "defvar-unbound" 1 2 4)
+
+(editor:setup-indent "def-http-return-code" 1 2 4)
+
+(editor:setup-indent "handler-case*" 1 2 4)
+
+(defun make-condition-variable (&key name)
+  (declare (ignore name))
+  (mp:make-condition-variable))
+
+(defun condition-variable-signal (condition-variable)
+  (mp:condition-variable-signal condition-variable))
+
+(defun condition-variable-wait (condition-variable lock)
+  (mp:condition-variable-wait condition-variable lock))
diff --git a/deps/hunchentoot/log.lisp b/deps/hunchentoot/log.lisp
new file mode 100644 (file)
index 0000000..d761692
--- /dev/null
@@ -0,0 +1,66 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defmacro with-log-stream ((stream-var destination lock) &body body)
+  "Helper macro to write log entries.  STREAM-VAR is a symbol that
+will be bound to the logging stream during the execution of BODY.
+DESTINATION is the logging destination, which can be either a pathname
+designator of the log file, a symbol designating an open stream or NIL
+if no logging should be done.  LOCK refers to the lock that should be
+held during the logging operation.  If DESTINATION is a pathname, a
+flexi stream with UTF-8 encoding will be created and bound to
+STREAM-VAR.  If an error occurs while writing to the log file, that
+error will be logged to *ERROR-OUTPUT*.
+
+Note that logging to a file involves opening and closing the log file
+for every logging operation, which is overall costly.  Web servers
+with high throughput demands should make use of a specialized logging
+function rather than relying on Hunchentoot's default logging
+facility."
+  (with-unique-names (binary-stream)
+    (with-rebinding (destination)
+      (let ((body body))
+        `(when ,destination
+           (with-lock-held (,lock)
+             (etypecase ,destination
+               ((or string pathname)
+                (with-open-file (,binary-stream ,destination
+                                                :direction :output
+                                                :element-type 'octet
+                                                :if-does-not-exist :create
+                                                :if-exists :append
+                                                #+:openmcl :sharing #+:openmcl :lock)
+                  (let ((,stream-var (make-flexi-stream ,binary-stream :external-format +utf-8+)))
+                    ,@body)))
+               (stream
+                (let ((,stream-var ,destination))
+                  (prog1 (progn ,@body)
+                    (finish-output ,destination)))))))))))
+  
diff --git a/deps/hunchentoot/make-docstrings.lisp b/deps/hunchentoot/make-docstrings.lisp
new file mode 100644 (file)
index 0000000..993a295
--- /dev/null
@@ -0,0 +1,228 @@
+;; -*- Lisp -*-
+
+(defpackage :make-docstrings
+  (:use :cl)
+  (:export #:parse-doc))
+
+(in-package :make-docstrings)
+
+(defclass formatting-stream (trivial-gray-streams:fundamental-character-input-stream)
+  ((understream :initarg :understream
+                :reader understream)
+   (width :initarg :width
+          :initform (error "missing :width argument to formatting-stream creation")
+          :reader width)
+   (column :initform 0
+           :accessor column)
+   (word-wrap-p :initform t
+                :accessor word-wrap-p)
+   (word-buffer :initform (make-array 1000
+                                      :element-type 'character
+                                      :adjustable t
+                                      :fill-pointer 0)
+                :reader word-buffer)))
+
+(defun write-char% (char stream)
+  (incf (column stream))
+  (write-char char (understream stream)))
+
+(defun print-newline (stream)
+  (write-char #\Newline (understream stream))
+  (setf (column stream) 0))
+
+(defun buffer-not-empty-p (stream)
+  (plusp (length (word-buffer stream))))
+
+(defun maybe-flush-word (stream)
+  (when (buffer-not-empty-p stream)
+    (cond
+      ((< (width stream) (+ (column stream) (length (word-buffer stream))))
+       (print-newline stream))
+      ((plusp (column stream))
+       (write-char% #\Space stream)))
+    (loop for char across (word-buffer stream)
+          do (write-char% char stream))
+    (setf (fill-pointer (word-buffer stream)) 0)))
+
+(defmethod trivial-gray-streams:stream-write-char ((stream formatting-stream) char)
+  (if (word-wrap-p stream)
+      (cond
+        ((eql #\Space char)
+         (maybe-flush-word stream))
+        ((eql #\Newline char)
+         (maybe-flush-word stream)
+         (print-newline stream))
+        (t
+         (vector-push-extend char (word-buffer stream))))
+      (write-char char (understream stream))))
+
+(defmethod trivial-gray-streams:stream-line-column (stream)
+  (+ (column stream) (length (word-buffer stream))))
+
+(defmethod trivial-gray-streams:stream-write-string ((stream formatting-stream) string &optional start end)
+  (loop for i from (or start 0) below (or end (length string))
+        do (write-char (char string i) stream)))
+
+(defmethod trivial-gray-streams:stream-terpri ((stream formatting-stream))
+  (write-char #\Newline stream))
+
+(defmethod close ((stream formatting-stream) &key abort)
+  (unless abort
+    (maybe-flush-word stream)))
+
+(defmethod (setf word-wrap-p) :before (new-value (stream formatting-stream))
+  (maybe-flush-word stream)
+  (when (buffer-not-empty-p stream)
+    (print-newline stream)))
+
+(defun test-wrap-stream (text)
+  (with-output-to-string (s)
+    (with-open-stream (s (make-instance 'formatting-stream :understream s :width 20))
+      (write-string text s)
+      (setf (word-wrap-p s) nil)
+      (format s "~&OFF~%")
+      (write-string text s)
+      (format s "~&ON~%")
+      (setf (word-wrap-p s) t)
+      (write-string text s))))
+
+(defmacro replace-regexp (place regex replacement)
+  `(setf ,place (cl-ppcre:regex-replace-all ,regex ,place ,replacement)))
+
+(defun collapse-whitespace (string)
+  (replace-regexp string "[ \\t]*\\n[ \\t]*" #.(make-string 1 :initial-element #\Newline))
+  (replace-regexp string "(?<!\\n)\\n" " ")
+  (remove #\Newline string))
+
+(defvar *output*)
+
+(defun xml-to-docstring% (node transform)
+  (stp:do-children (child node)
+    (typecase child
+      (stp:text
+       (write-string (funcall transform (stp:data child)) *output*))
+      (stp:element
+       (ecase (intern (string-upcase (stp:local-name child)) :keyword)
+         (:p
+          (terpri *output*)
+          (terpri *output*)
+          (xml-to-docstring% child transform))
+         ((:a :code :tt :blockquote :span :ul)
+          (xml-to-docstring% child transform))
+         ((:li)
+           (xml-to-docstring% child transform)
+           (terpri *output*))
+         ((:ref :arg :em :i)
+          (xml-to-docstring% child (alexandria:compose #'string-upcase transform)))
+         ((:sup)
+          ;; skip
+          )
+         (:pre
+          (terpri *output*)
+          (terpri *output*)
+          (setf (word-wrap-p *output*) nil)
+          (xml-to-docstring% child #'identity)
+          (setf (word-wrap-p *output*) t)
+          (terpri *output*)))))))
+
+(defun xml-to-docstring (description-node)
+  (with-output-to-string (s)
+    (with-open-stream (*output* (make-instance 'formatting-stream :understream s :width 75))
+      (xml-to-docstring% description-node #'collapse-whitespace))))
+
+(defun maybe-qualify-name (name package-name)
+  (if (find #\: name)
+      name
+      (format nil "~A:~A" package-name name)))
+
+(defun get-doc-entry-type (node)
+  (let ((basic-type (intern (string-upcase (stp:local-name node)) :keyword)))
+    (if (eq basic-type :function)
+        (if (stp:attribute-value node "generic") ; FIXME: "no" not recognized
+            :generic-function
+            :function)
+        basic-type)))
+
+(defun skip-to (stream char)
+  (loop until (eql char (peek-char nil stream))
+        do (read-char stream)))
+
+(defun get-simple-def-docstring (source-string position)
+  (with-input-from-string (s source-string :start (1+ position))
+    (read s)                            ; DEFUN/DEFVAR/DEFPARAMETER
+    (read s)                            ; name
+    (read s)                            ; argument list/initial value
+    (skip-to s #\")
+    (list :start (file-position s)
+          :text (read s)
+          :end (file-position s))))
+
+(defun get-complex-def-docstring (source-string position)
+  (with-input-from-string (s source-string :start (1+ position))
+    (read s)                            ; DEFCLASS/DEFINE-CONDITION/DEFGENERIC
+    (read s)                            ; name
+    (read s)                            ; arguments/supers
+    (loop
+      (let* ((start-of-clause (file-position s))
+             (clause (read s)))
+        (when (eql (first clause) :documentation)
+          (file-position s start-of-clause)
+          (skip-to s #\()
+          (read-char s)
+          (read s)                      ; :DOCUMENTATION
+          (skip-to s #\")
+          (return (list :start (file-position s)
+                        :text (read s)
+                        :end (file-position s))))))))
+
+(defun get-doc-function (type)
+  (case type
+    ((:function :special-variable) 'get-simple-def-docstring)
+    ((:generic-function :class) 'get-complex-def-docstring)))
+
+(defun source-location-flatten (location-info)
+  (apply #'append (rest (find :location (rest location-info) :key #'first))))
+
+(defvar *files*)
+
+(defclass file ()
+  ((file-pathname :initarg :file-pathname
+                  :reader file-pathname)
+   (docstrings :initform nil
+               :accessor docstrings)
+   (contents :accessor contents)))
+
+(defmethod initialize-instance :after ((file file) &key file-pathname)
+  (setf (slot-value file 'contents) (alexandria:read-file-into-string file-pathname)))
+
+(defun get-file (pathname)
+  (or (gethash pathname *files*)
+      (setf (gethash pathname *files*)
+            (make-instance 'file
+                           :file-pathname pathname))))
+
+(defun record-docstring (doc-docstring get-doc-function symbol-name)
+  (let ((definitions (remove-if (lambda (definition)
+                                  (or (cl-ppcre:scan "(?i)^\\s*\\(defmethod\\s" (first definition))
+                                      (eql (first (second definition)) :error)))
+                                (swank:find-definitions-for-emacs symbol-name))))
+    (case (length definitions)
+      (0 (warn "no source location for ~A" symbol-name))
+      (1 (let* ((source-location (source-location-flatten (first definitions)))
+                (file (get-file (getf source-location :file))))
+           (push (list* :doc-docstring doc-docstring
+                        (funcall get-doc-function (contents file) (getf source-location :position)))
+                 (docstrings file))))
+      (2 (warn "multiple source locations for ~A" symbol-name)))))
+
+(defun parse-doc (pathname default-package-name)
+  (let ((*files* (make-hash-table :test #'equal)))
+    (xpath:with-namespaces (("clix" "http://bknr.net/clixdoc"))
+      (xpath:do-node-set (node (xpath:evaluate "//*[clix:description!='']" (cxml:parse pathname (stp:make-builder))))
+        (let ((type (get-doc-entry-type node))
+              (symbol-name (maybe-qualify-name (stp:attribute-value node "name") default-package-name)))
+          (xpath:do-node-set (description (xpath:evaluate "clix:description" node))
+            (alexandria:when-let (get-doc-function (get-doc-function type))
+              (record-docstring (xml-to-docstring description)
+                                get-doc-function symbol-name))))))
+    *files*))
diff --git a/deps/hunchentoot/mime-types.lisp b/deps/hunchentoot/mime-types.lisp
new file mode 100644 (file)
index 0000000..5765358
--- /dev/null
@@ -0,0 +1,363 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defparameter *mime-type-list* '(("application/andrew-inset" "ez")
+                                 ("application/cu-seeme" "cu")
+                                 ("application/dsptype" "tsp")
+                                 ("application/futuresplash" "spl")
+                                 ("application/hta" "hta")
+                                 ("application/java-archive" "jar")
+                                 ("application/java-serialized-object" "ser")
+                                 ("application/java-vm" "class")
+                                 ("application/mac-binhex40" "hqx")
+                                 ("application/mac-compactpro" "cpt")
+                                 ("application/mathematica" "nb")
+                                 ("application/msaccess" "mdb")
+                                 ("application/msword" "doc" "dot")
+                                 ("application/octet-stream" "bin")
+                                 ("application/oda" "oda")
+                                 ("application/ogg" "ogg")
+                                 ("application/pdf" "pdf")
+                                 ("application/pgp-keys" "key")
+                                 ("application/pgp-signature" "pgp")
+                                 ("application/pics-rules" "prf")
+                                 ("application/postscript" "ps" "ai" "eps")
+                                 ("application/rar" "rar")
+                                 ("application/rdf+xml" "rdf")
+                                 ("application/rss+xml" "rss")
+                                 ("application/smil" "smi" "smil")
+                                 ("application/wordperfect" "wpd")
+                                 ("application/wordperfect5.1" "wp5")
+                                 ("application/xhtml+xml" "xhtml" "xht")
+                                 ("application/xml" "fo" "xml" "xsl")
+                                 ("application/zip" "zip")
+                                 ("application/vnd.cinderella" "cdy")
+                                 ("application/vnd.mozilla.xul+xml" "xul")
+                                 ("application/vnd.ms-excel" "xls" "xlb" "xlt")
+                                 ("application/vnd.ms-pki.seccat" "cat")
+                                 ("application/vnd.ms-pki.stl" "stl")
+                                 ("application/vnd.ms-powerpoint" "ppt" "pps")
+                                 ("application/vnd.oasis.opendocument.chart" "odc")
+                                 ("application/vnd.oasis.opendocument.database" "odb")
+                                 ("application/vnd.oasis.opendocument.formula" "odf")
+                                 ("application/vnd.oasis.opendocument.graphics" "odg")
+                                 ("application/vnd.oasis.opendocument.graphics-template" "otg")
+                                 ("application/vnd.oasis.opendocument.image" "odi")
+                                 ("application/vnd.oasis.opendocument.presentation" "odp")
+                                 ("application/vnd.oasis.opendocument.presentation-template" "otp")
+                                 ("application/vnd.oasis.opendocument.spreadsheet" "ods")
+                                 ("application/vnd.oasis.opendocument.spreadsheet-template" "ots")
+                                 ("application/vnd.oasis.opendocument.text" "odt")
+                                 ("application/vnd.oasis.opendocument.text-master" "odm")
+                                 ("application/vnd.oasis.opendocument.text-template" "ott")
+                                 ("application/vnd.oasis.opendocument.text-web" "oth")
+                                 ("application/vnd.rim.cod" "cod")
+                                 ("application/vnd.smaf" "mmf")
+                                 ("application/vnd.stardivision.calc" "sdc")
+                                 ("application/vnd.stardivision.draw" "sda")
+                                 ("application/vnd.stardivision.impress" "sdd" "sdp")
+                                 ("application/vnd.stardivision.math" "smf")
+                                 ("application/vnd.stardivision.writer" "sdw" "vor")
+                                 ("application/vnd.stardivision.writer-global" "sgl")
+                                 ("application/vnd.sun.xml.calc" "sxc")
+                                 ("application/vnd.sun.xml.calc.template" "stc")
+                                 ("application/vnd.sun.xml.draw" "sxd")
+                                 ("application/vnd.sun.xml.draw.template" "std")
+                                 ("application/vnd.sun.xml.impress" "sxi")
+                                 ("application/vnd.sun.xml.impress.template" "sti")
+                                 ("application/vnd.sun.xml.math" "sxm")
+                                 ("application/vnd.sun.xml.writer" "sxw")
+                                 ("application/vnd.sun.xml.writer.global" "sxg")
+                                 ("application/vnd.sun.xml.writer.template" "stw")
+                                 ("application/vnd.symbian.install" "sis")
+                                 ("application/vnd.visio" "vsd")
+                                 ("application/vnd.wap.wbxml" "wbxml")
+                                 ("application/vnd.wap.wmlc" "wmlc")
+                                 ("application/vnd.wap.wmlscriptc" "wmlsc")
+                                 ("application/x-123" "wk")
+                                 ("application/x-abiword" "abw")
+                                 ("application/x-apple-diskimage" "dmg")
+                                 ("application/x-bcpio" "bcpio")
+                                 ("application/x-bittorrent" "torrent")
+                                 ("application/x-cdf" "cdf")
+                                 ("application/x-cdlink" "vcd")
+                                 ("application/x-chess-pgn" "pgn")
+                                 ("application/x-cpio" "cpio")
+                                 ("application/x-csh" "csh")
+                                 ("application/x-debian-package" "deb" "udeb")
+                                 ("application/x-director" "dcr" "dir" "dxr")
+                                 ("application/x-dms" "dms")
+                                 ("application/x-doom" "wad")
+                                 ("application/x-dvi" "dvi")
+                                 ("application/x-flac" "flac")
+                                 ("application/x-font" "pfa" "pfb" "gsf" "pcf")
+                                 ("application/x-freemind" "mm")
+                                 ("application/x-futuresplash" "spl")
+                                 ("application/x-gnumeric" "gnumeric")
+                                 ("application/x-go-sgf" "sgf")
+                                 ("application/x-graphing-calculator" "gcf")
+                                 ("application/x-gtar" "gtar" "tgz" "taz")
+                                 ("application/x-hdf" "hdf")
+                                 ("application/x-httpd-php" "phtml" "pht" "php")
+                                 ("application/x-httpd-php-source" "phps")
+                                 ("application/x-httpd-php3" "php3")
+                                 ("application/x-httpd-php3-preprocessed" "php3p")
+                                 ("application/x-httpd-php4" "php4")
+                                 ("application/x-ica" "ica")
+                                 ("application/x-internet-signup" "ins" "isp")
+                                 ("application/x-iphone" "iii")
+                                 ("application/x-iso9660-image" "iso")
+                                 ("application/x-java-jnlp-file" "jnlp")
+                                 ("application/x-javascript" "js")
+                                 ("application/x-jmol" "jmz")
+                                 ("application/x-kchart" "chrt")
+                                 ("application/x-killustrator" "kil")
+                                 ("application/x-koan" "skp" "skd" "skt" "skm")
+                                 ("application/x-kpresenter" "kpr" "kpt")
+                                 ("application/x-kspread" "ksp")
+                                 ("application/x-kword" "kwd" "kwt")
+                                 ("application/x-latex" "latex")
+                                 ("application/x-lha" "lha")
+                                 ("application/x-lzh" "lzh")
+                                 ("application/x-lzx" "lzx")
+                                 ("application/x-maker" "frm" "maker" "frame" "fm" "fb" "book" "fbdoc")
+                                 ("application/x-mif" "mif")
+                                 ("application/x-ms-wmd" "wmd")
+                                 ("application/x-ms-wmz" "wmz")
+                                 ("application/x-msdos-program" "com" "exe" "bat" "dll")
+                                 ("application/x-msi" "msi")
+                                 ("application/x-netcdf" "nc")
+                                 ("application/x-ns-proxy-autoconfig" "pac")
+                                 ("application/x-nwc" "nwc")
+                                 ("application/x-object" "o")
+                                 ("application/x-oz-application" "oza")
+                                 ("application/x-pkcs7-certreqresp" "p7r")
+                                 ("application/x-pkcs7-crl" "crl")
+                                 ("application/x-python-code" "pyc" "pyo")
+                                 ("application/x-quicktimeplayer" "qtl")
+                                 ("application/x-redhat-package-manager" "rpm")
+                                 ("application/x-sh" "sh")
+                                 ("application/x-shar" "shar")
+                                 ("application/x-shockwave-flash" "swf" "swfl")
+                                 ("application/x-stuffit" "sit")
+                                 ("application/x-sv4cpio" "sv4cpio")
+                                 ("application/x-sv4crc" "sv4crc")
+                                 ("application/x-tar" "tar")
+                                 ("application/x-tcl" "tcl")
+                                 ("application/x-tex-gf" "gf")
+                                 ("application/x-tex-pk" "pk")
+                                 ("application/x-texinfo" "texinfo" "texi")
+                                 ("application/x-trash" "~%" "" "bak" "old" "sik")
+                                 ("application/x-troff" "tt" "r" "roff")
+                                 ("application/x-troff-man" "man")
+                                 ("application/x-troff-me" "me")
+                                 ("application/x-troff-ms" "ms")
+                                 ("application/x-ustar" "ustar")
+                                 ("application/x-wais-source" "src")
+                                 ("application/x-wingz" "wz")
+                                 ("application/x-x509-ca-cert" "crt")
+                                 ("application/x-xcf" "xcf")
+                                 ("application/x-xfig" "fig")
+                                 ("application/x-xpinstall" "xpi")
+                                 ("audio/basic" "au" "snd")
+                                 ("audio/midi" "mid" "midi" "kar")
+                                 ("audio/mpeg" "mpga" "mpega" "mp2" "mp3" "m4a")
+                                 ("audio/mpegurl" "m3u")
+                                 ("audio/prs.sid" "sid")
+                                 ("audio/x-aiff" "aif" "aiff" "aifc")
+                                 ("audio/x-gsm" "gsm")
+                                 ("audio/x-mpegurl" "m3u")
+                                 ("audio/x-ms-wma" "wma")
+                                 ("audio/x-ms-wax" "wax")
+                                 ("audio/x-pn-realaudio" "ra" "rm" "ram")
+                                 ("audio/x-realaudio" "ra")
+                                 ("audio/x-scpls" "pls")
+                                 ("audio/x-sd2" "sd2")
+                                 ("audio/x-wav" "wav")
+                                 ("chemical/x-alchemy" "alc")
+                                 ("chemical/x-cache" "cac" "cache")
+                                 ("chemical/x-cache-csf" "csf")
+                                 ("chemical/x-cactvs-binary" "cbin" "cascii" "ctab")
+                                 ("chemical/x-cdx" "cdx")
+                                 ("chemical/x-cerius" "cer")
+                                 ("chemical/x-chem3d" "c3d")
+                                 ("chemical/x-chemdraw" "chm")
+                                 ("chemical/x-cif" "cif")
+                                 ("chemical/x-cmdf" "cmdf")
+                                 ("chemical/x-cml" "cml")
+                                 ("chemical/x-compass" "cpa")
+                                 ("chemical/x-crossfire" "bsd")
+                                 ("chemical/x-csml" "csml" "csm")
+                                 ("chemical/x-ctx" "ctx")
+                                 ("chemical/x-cxf" "cxf" "cef")
+                                 ("chemical/x-embl-dl-nucleotide" "emb" "embl")
+                                 ("chemical/x-galactic-spc" "spc")
+                                 ("chemical/x-gamess-input" "inp" "gam" "gamin")
+                                 ("chemical/x-gaussian-checkpoint" "fch" "fchk")
+                                 ("chemical/x-gaussian-cube" "cub")
+                                 ("chemical/x-gaussian-input" "gau" "gjc" "gjf")
+                                 ("chemical/x-gaussian-log" "gal")
+                                 ("chemical/x-gcg8-sequence" "gcg")
+                                 ("chemical/x-genbank" "gen")
+                                 ("chemical/x-hin" "hin")
+                                 ("chemical/x-isostar" "istr" "ist")
+                                 ("chemical/x-jcamp-dx" "jdx" "dx")
+                                 ("chemical/x-kinemage" "kin")
+                                 ("chemical/x-macmolecule" "mcm")
+                                 ("chemical/x-macromodel-input" "mmd" "mmod")
+                                 ("chemical/x-mdl-molfile" "mol")
+                                 ("chemical/x-mdl-rdfile" "rd")
+                                 ("chemical/x-mdl-rxnfile" "rxn")
+                                 ("chemical/x-mdl-sdfile" "sd" "sdf")
+                                 ("chemical/x-mdl-tgf" "tgf")
+                                 ("chemical/x-mmcif" "mcif")
+                                 ("chemical/x-mol2" "mol2")
+                                 ("chemical/x-molconn-Z" "b")
+                                 ("chemical/x-mopac-graph" "gpt")
+                                 ("chemical/x-mopac-input" "mop" "mopcrt" "mpc" "dat" "zmt")
+                                 ("chemical/x-mopac-out" "moo")
+                                 ("chemical/x-mopac-vib" "mvb")
+                                 ("chemical/x-ncbi-asn1" "asn")
+                                 ("chemical/x-ncbi-asn1-ascii" "prt" "ent")
+                                 ("chemical/x-ncbi-asn1-binary" "val" "aso")
+                                 ("chemical/x-ncbi-asn1-spec" "asn")
+                                 ("chemical/x-pdb" "pdb" "ent")
+                                 ("chemical/x-rosdal" "ros")
+                                 ("chemical/x-swissprot" "sw")
+                                 ("chemical/x-vamas-iso14976" "vms")
+                                 ("chemical/x-vmd" "vmd")
+                                 ("chemical/x-xtel" "xtel")
+                                 ("chemical/x-xyz" "xyz")
+                                 ("image/gif" "gif")
+                                 ("image/ief" "ief")
+                                 ("image/jpeg" "jpeg" "jpg" "jpe")
+                                 ("image/pcx" "pcx")
+                                 ("image/png" "png")
+                                 ("image/svg+xml" "svg" "svgz")
+                                 ("image/tiff" "tiff" "tif")
+                                 ("image/vnd.djvu" "djvu" "djv")
+                                 ("image/vnd.wap.wbmp" "wbmp")
+                                 ("image/x-cmu-raster" "ras")
+                                 ("image/x-coreldraw" "cdr")
+                                 ("image/x-coreldrawpattern" "pat")
+                                 ("image/x-coreldrawtemplate" "cdt")
+                                 ("image/x-corelphotopaint" "cpt")
+                                 ("image/x-icon" "ico")
+                                 ("image/x-jg" "art")
+                                 ("image/x-jng" "jng")
+                                 ("image/x-ms-bmp" "bmp")
+                                 ("image/x-photoshop" "psd")
+                                 ("image/x-portable-anymap" "pnm")
+                                 ("image/x-portable-bitmap" "pbm")
+                                 ("image/x-portable-graymap" "pgm")
+                                 ("image/x-portable-pixmap" "ppm")
+                                 ("image/x-rgb" "rgb")
+                                 ("image/x-xbitmap" "xbm")
+                                 ("image/x-xpixmap" "xpm")
+                                 ("image/x-xwindowdump" "xwd")
+                                 ("model/iges" "igs" "iges")
+                                 ("model/mesh" "msh" "mesh" "silo")
+                                 ("model/vrml" "wrl" "vrml")
+                                 ("text/calendar" "ics" "icz")
+                                 ("text/comma-separated-values" "csv")
+                                 ("text/css" "css")
+                                 ("text/h323" "323")
+                                 ("text/html" "html" "htm" "shtml")
+                                 ("text/iuls" "uls")
+                                 ("text/mathml" "mml")
+                                 ("text/plain" "asc" "txt" "text" "diff" "pot")
+                                 ("text/richtext" "rtx")
+                                 ("text/rtf" "rtf")
+                                 ("text/scriptlet" "sct" "wsc")
+                                 ("text/texmacs" "tm" "ts")
+                                 ("text/tab-separated-values" "tsv")
+                                 ("text/vnd.sun.j2me.app-descriptor" "jad")
+                                 ("text/vnd.wap.wml" "wml")
+                                 ("text/vnd.wap.wmlscript" "wmls")
+                                 ("text/x-bibtex" "bib")
+                                 ("text/x-boo" "boo")
+                                 ("text/x-c++hdr" "h++" "hpp" "hxx" "hh")
+                                 ("text/x-c++src" "c++" "cpp" "cxx" "cc")
+                                 ("text/x-chdr" "h")
+                                 ("text/x-component" "htc")
+                                 ("text/x-csh" "csh")
+                                 ("text/x-csrc" "c")
+                                 ("text/x-dsrc" "d")
+                                 ("text/x-haskell" "hs")
+                                 ("text/x-java" "java")
+                                 ("text/javascript" "js")
+                                 ("text/x-literate-haskell" "lhs")
+                                 ("text/x-moc" "moc")
+                                 ("text/x-pascal" "pp" "as")
+                                 ("text/x-pcs-gcd" "gcd")
+                                 ("text/x-perl" "pl" "pm")
+                                 ("text/x-python" "py")
+                                 ("text/x-setext" "etx")
+                                 ("text/x-sh" "sh")
+                                 ("text/x-tcl" "tcl" "tk")
+                                 ("text/x-tex" "tex" "ltx" "sty" "cls")
+                                 ("text/x-vcalendar" "vcs")
+                                 ("text/x-vcard" "vcf")
+                                 ("video/dl" "dl")
+                                 ("video/dv" "dif" "dv")
+                                 ("video/fli" "fli")
+                                 ("video/gl" "gl")
+                                 ("video/mpeg" "mpeg" "mpg" "mpe")
+                                 ("video/mp4" "mp4")
+                                 ("video/quicktime" "qt" "mov")
+                                 ("video/vnd.mpegurl" "mxu")
+                                 ("video/x-la-asf" "lsf" "lsx")
+                                 ("video/x-m4v" "m4v")
+                                 ("video/x-mng" "mng")
+                                 ("video/x-ms-asf" "asf" "asx")
+                                 ("video/x-ms-wm" "wm")
+                                 ("video/x-ms-wmv" "wmv")
+                                 ("video/x-ms-wmx" "wmx")
+                                 ("video/x-ms-wvx" "wvx")
+                                 ("video/x-msvideo" "avi")
+                                 ("video/x-sgi-movie" "movie")
+                                 ("x-conference/x-cooltalk" "ice")
+                                 ("x-world/x-vrml" "vrm" "vrml" "wrl"))
+  "An alist where the cars are MIME types and the cdrs are list
+of file suffixes for the corresponding type.")
+
+(defparameter *mime-type-hash*
+  (let ((hash (make-hash-table :test #'equalp)))
+    (loop for (type . suffixes) in *mime-type-list* do
+          (loop for suffix in suffixes do
+                (setf (gethash suffix hash) type)))
+    hash)
+  "A hash table which maps file suffixes to MIME types.")
+
+(defun mime-type (pathspec)
+  "Given a pathname designator PATHSPEC returns the MIME type
+\(as a string) corresponding to the suffix of the file denoted by
+PATHSPEC \(or NIL)."
+  (gethash (pathname-type pathspec) *mime-type-hash*))
\ No newline at end of file
diff --git a/deps/hunchentoot/misc.lisp b/deps/hunchentoot/misc.lisp
new file mode 100644 (file)
index 0000000..bc7a640
--- /dev/null
@@ -0,0 +1,274 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(let ((scanner-hash (make-hash-table :test #'equal)))
+  (defun scanner-for-get-param (param-name)
+    "Returns a CL-PPCRE scanner which matches a GET parameter in a
+URL.  Scanners are memoized in SCANNER-HASH once they are created."
+    (or (gethash param-name scanner-hash)
+        (setf (gethash param-name scanner-hash)
+                (create-scanner
+                 `(:alternation
+                   ;; session=value at end of URL
+                   (:sequence
+                    (:char-class #\? #\&)
+                    ,param-name
+                    #\=
+                    (:greedy-repetition 0 nil (:inverted-char-class #\&))
+                    :end-anchor)
+                   ;; session=value with other parameters following
+                   (:sequence
+                    (:register (:char-class #\? #\&))
+                    ,param-name
+                    #\=
+                    (:greedy-repetition 0 nil (:inverted-char-class #\&))
+                    #\&))))))
+  (defun add-cookie-value-to-url (url &key
+                                      (cookie-name (session-cookie-name *acceptor*))
+                                      (value (when-let (session (session *request*))
+                                               (session-cookie-value session)))
+                                      (replace-ampersands-p t))
+    "Removes all GET parameters named COOKIE-NAME from URL and then
+adds a new GET parameter with the name COOKIE-NAME and the value
+VALUE.  If REPLACE-AMPERSANDS-P is true all literal ampersands in URL
+are replaced with '&amp;'. The resulting URL is returned."
+    (unless url
+      ;; see URL-REWRITE:*URL-REWRITE-FILL-TAGS*
+      (setq url (request-uri *request*)))
+    (setq url (regex-replace-all (scanner-for-get-param cookie-name) url "\\1"))
+    (when value
+      (setq url (format nil "~A~:[?~;&~]~A=~A"
+                        url
+                        (find #\? url)
+                        cookie-name
+                        (url-encode value))))
+    (when replace-ampersands-p
+      (setq url (regex-replace-all "&" url "&amp;")))
+    url))
+
+(defun maybe-rewrite-urls-for-session (html &key
+                                            (cookie-name (session-cookie-name *acceptor*))
+                                            (value (when-let (session (session *request*))
+                                                     (session-cookie-value session))))
+  "Rewrites the HTML page HTML such that the name/value pair
+COOKIE-NAME/COOKIE-VALUE is inserted if the client hasn't sent a
+cookie of the same name but only if *REWRITE-FOR-SESSION-URLS* is
+true.  See the docs for URL-REWRITE:REWRITE-URLS."
+  (cond ((or (not *rewrite-for-session-urls*)
+             (null value)
+             (cookie-in cookie-name))
+          html)
+        (t
+          (with-input-from-string (*standard-input* html)
+            (with-output-to-string (*standard-output*)
+              (url-rewrite:rewrite-urls
+               (lambda (url)
+                 (add-cookie-value-to-url url
+                                          :cookie-name cookie-name
+                                          :value value))))))))
+
+(defun create-prefix-dispatcher (prefix handler)
+  "Creates a request dispatch function which will dispatch to the
+function denoted by HANDLER if the file name of the current request
+starts with the string PREFIX."
+  (lambda (request)
+    (let ((mismatch (mismatch (script-name request) prefix
+                              :test #'char=)))
+      (and (or (null mismatch)
+               (>= mismatch (length prefix)))
+           handler))))
+
+(defun create-regex-dispatcher (regex handler)
+  "Creates a request dispatch function which will dispatch to the
+function denoted by HANDLER if the file name of the current request
+matches the CL-PPCRE regular expression REGEX."
+  (let ((scanner (create-scanner regex)))
+    (lambda (request)
+      (and (scan scanner (script-name request))
+           handler))))
+
+(defun abort-request-handler (&optional result)
+  "This function can be called by a request handler at any time to
+immediately abort handling the request.  This works as if the handler
+had returned RESULT.  See the source code of REDIRECT for an example."
+  (throw 'handler-done result))
+
+(defun maybe-handle-range-header (file)
+  "Helper function for handle-static-file.  Determines whether the
+  requests specifies a Range header.  If so, parses the header and
+  position the already opened file to the location specified.  Returns
+  the number of bytes to transfer from the file.  Invalid specified
+  ranges are reported to the client with a HTTP 416 status code."
+  (let ((bytes-to-send (file-length file)))
+    (cl-ppcre:register-groups-bind
+        (start end)
+        ("^bytes=(\\d+)-(\\d*)$" (header-in* :range) :sharedp t)
+      ;; body won't be executed if regular expression does not match
+      (setf start (parse-integer start))
+      (setf end (if (> (length end) 0)
+                    (parse-integer end)
+                    (1- (file-length file))))
+      (when (or (< start 0)
+                (>= end (file-length file)))
+        (setf (return-code*) +http-requested-range-not-satisfiable+
+              (header-out :content-range) (format nil "bytes 0-~D/~D" (1- (file-length file)) (file-length file)))
+        (throw 'handler-done
+          (format nil "invalid request range (requested ~D-~D, accepted 0-~D)"
+                  start end (1- (file-length file)))))
+      (file-position file start)
+      (setf (return-code*) +http-partial-content+
+            bytes-to-send (1+ (- end start))
+            (header-out :content-range) (format nil "bytes ~D-~D/~D" start end (file-length file))))
+    bytes-to-send))
+
+(defun handle-static-file (pathname &optional content-type)
+  "A function which acts like a Hunchentoot handler for the file
+denoted by PATHNAME.  Sends a content type header corresponding to
+CONTENT-TYPE or \(if that is NIL) tries to determine the content type
+via the file's suffix."
+  (when (or (wild-pathname-p pathname)
+            (not (fad:file-exists-p pathname))
+            (fad:directory-exists-p pathname))
+    ;; file does not exist
+    (setf (return-code*) +http-not-found+)
+    (abort-request-handler))
+  (unless content-type
+    (setf content-type (mime-type pathname)))
+  (let ((time (or (file-write-date pathname)
+                  (get-universal-time)))
+        bytes-to-send)
+    (setf (content-type*) (or (and content-type
+                                   (maybe-add-charset-to-content-type-header content-type (reply-external-format*)))
+                              "application/octet-stream")
+          (header-out :last-modified) (rfc-1123-date time)
+          (header-out :accept-ranges) "bytes")
+    (handle-if-modified-since time)
+    (with-open-file (file pathname
+                          :direction :input
+                          :element-type 'octet)
+      (setf bytes-to-send (maybe-handle-range-header file)
+            (content-length*) bytes-to-send)
+      (let ((out (send-headers))
+            (buf (make-array +buffer-length+ :element-type 'octet)))
+        (loop
+           (when (zerop bytes-to-send)
+             (return))
+           (let* ((chunk-size (min +buffer-length+ bytes-to-send)))
+             (unless (eql chunk-size (read-sequence buf file :end chunk-size))
+               (error "can't read from input file"))
+             (write-sequence buf out :end chunk-size)
+             (decf bytes-to-send chunk-size)))
+        (finish-output out)))))
+
+(defun create-static-file-dispatcher-and-handler (uri path &optional content-type)
+  "Creates and returns a request dispatch function which will dispatch
+to a handler function which emits the file denoted by the pathname
+designator PATH with content type CONTENT-TYPE if the SCRIPT-NAME of
+the request matches the string URI.  If CONTENT-TYPE is NIL, tries to
+determine the content type via the file's suffix."
+  ;; the dispatcher
+  (lambda (request)
+    (when (string= (script-name request) uri)
+      ;; the handler
+      (lambda ()
+        (handle-static-file path content-type)))))
+
+(defun create-folder-dispatcher-and-handler (uri-prefix base-path &optional content-type)
+  "Creates and returns a dispatch function which will dispatch to a
+handler function which emits the file relative to BASE-PATH that is
+denoted by the URI of the request relative to URI-PREFIX.  URI-PREFIX
+must be a string ending with a slash, BASE-PATH must be a pathname
+designator for an existing directory.  If CONTENT-TYPE is not NIL,
+it'll be the content type used for all files in the folder."
+  (unless (and (stringp uri-prefix)
+               (plusp (length uri-prefix))
+               (char= (char uri-prefix (1- (length uri-prefix))) #\/))
+    (parameter-error "~S must be string ending with a slash." uri-prefix))
+  (unless (fad:directory-pathname-p base-path)
+    (parameter-error "~S is supposed to denote a directory." base-path))
+  (flet ((handler ()
+           (let ((request-path (request-pathname *request* uri-prefix)))
+             (when (null request-path)
+               (setf (return-code*) +http-forbidden+)
+               (abort-request-handler))
+             (handle-static-file (merge-pathnames request-path base-path) content-type))))
+    (create-prefix-dispatcher uri-prefix #'handler)))
+
+(defun no-cache ()
+  "Adds appropriate headers to completely prevent caching on most browsers."
+  (setf (header-out :expires)
+          "Mon, 26 Jul 1997 05:00:00 GMT"
+        (header-out :cache-control)
+          "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"
+        (header-out :pragma)
+          "no-cache"
+        (header-out :last-modified)
+          (rfc-1123-date))
+  (values))
+
+(defun redirect (target &key (host (host *request*) host-provided-p)
+                             port
+                             (protocol (if (ssl-p) :https :http))
+                             (add-session-id (not (or host-provided-p
+                                                      (starts-with-scheme-p target)
+                                                      (cookie-in (session-cookie-name *acceptor*)))))
+                             (code +http-moved-temporarily+))
+  "Redirects the browser to TARGET which should be a string.  If
+TARGET is a full URL starting with a scheme, HOST, PORT and PROTOCOL
+are ignored.  Otherwise, TARGET should denote the path part of a URL,
+PROTOCOL must be one of the keywords :HTTP or :HTTPS, and the URL to
+redirect to will be constructed from HOST, PORT, PROTOCOL, and TARGET.
+Adds a session ID if ADD-SESSION-ID is true.  If CODE is a 3xx
+redirection code, it will be sent as status code."
+  (check-type code (integer 300 399))
+  (let ((url (if (starts-with-scheme-p target)
+               target
+               (format nil "~A://~A~@[:~A~]~A"
+                       (ecase protocol
+                         ((:http) "http")
+                         ((:https) "https"))
+                       (if port
+                         (first (ppcre:split ":" (or host "")))
+                         host)
+                       port target))))
+    (when add-session-id
+      (setq url (add-cookie-value-to-url url :replace-ampersands-p nil)))
+    (setf (header-out :location) url
+          (return-code*) code)
+    (abort-request-handler)))
+
+(defun require-authorization (&optional (realm "Hunchentoot"))
+  "Sends back appropriate headers to require basic HTTP authentication
+\(see RFC 2617) for the realm REALM."
+  (setf (header-out :www-authenticate)
+          (format nil "Basic realm=\"~A\"" (quote-string realm))
+        (return-code *reply*)
+          +http-authorization-required+)
+  (abort-request-handler))
diff --git a/deps/hunchentoot/packages.lisp b/deps/hunchentoot/packages.lisp
new file mode 100644 (file)
index 0000000..b88ae37
--- /dev/null
@@ -0,0 +1,295 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(defpackage #:hunchentoot
+  (:nicknames #:tbnl)
+  (:use :cl :cl-ppcre :chunga :flexi-streams :url-rewrite)
+  (:shadow #:defconstant
+           #:url-encode)
+  #+:lispworks
+  (:import-from :lw #:with-unique-names #:when-let)
+  (:export #:*acceptor*
+           #:*catch-errors-p*
+           #+:lispworks
+           #:*cleanup-function*
+           #+:lispworks
+           #:*cleanup-interval*
+           #:*content-types-for-url-rewrite*
+           #:*default-connection-timeout*
+           #:*default-content-type*
+           #:*dispatch-table*
+           #:*file-upload-hook*
+           #:*handle-http-errors-p*
+           #:*header-stream*
+           #:*http-error-handler*
+           #:*hunchentoot-default-external-format*
+          #:*hunchentoot-version*
+           #:*lisp-errors-log-level*
+           #:*lisp-warnings-log-level*
+           #:*log-lisp-backtraces-p*
+           #:*log-lisp-errors-p*
+           #:*log-lisp-warnings-p*
+           #:*methods-for-post-parameters*
+           #:*reply*
+           #:*request*
+           #:*rewrite-for-session-urls*
+           #:*session*
+           #:*session-gc-frequency*
+           #:*session-max-time*
+           #:*session-secret*
+           #:*show-lisp-backtraces-p*
+           #:*show-lisp-errors-p*
+           #:*tmp-directory*
+           #:*use-remote-addr-for-sessions*
+           #:*use-user-agent-for-sessions*
+           #:+http-accepted+
+           #:+http-authorization-required+
+           #:+http-bad-gateway+
+           #:+http-bad-request+
+           #:+http-conflict+
+           #:+http-continue+
+           #:+http-created+
+           #:+http-expectation-failed+
+           #:+http-failed-dependency+
+           #:+http-forbidden+
+           #:+http-gateway-time-out+
+           #:+http-gone+
+           #:+http-internal-server-error+
+           #:+http-length-required+
+           #:+http-method-not-allowed+
+           #:+http-moved-permanently+
+           #:+http-moved-temporarily+
+           #:+http-multi-status+
+           #:+http-multiple-choices+
+           #:+http-network-authentication-required+
+           #:+http-no-content+
+           #:+http-non-authoritative-information+
+           #:+http-not-acceptable+
+           #:+http-not-found+
+           #:+http-not-implemented+
+           #:+http-not-modified+
+           #:+http-ok+
+           #:+http-partial-content+
+           #:+http-payment-required+
+           #:+http-precondition-failed+
+           #:+http-precondition-required+
+           #:+http-proxy-authentication-required+
+           #:+http-request-entity-too-large+
+           #:+http-request-header-fields-too-large+
+           #:+http-request-time-out+
+           #:+http-request-uri-too-large+
+           #:+http-requested-range-not-satisfiable+
+           #:+http-reset-content+
+           #:+http-see-other+
+           #:+http-service-unavailable+
+           #:+http-switching-protocols+
+           #:+http-temporary-redirect+
+           #:+http-too-many-requests+
+           #:+http-unsupported-media-type+
+           #:+http-use-proxy+
+           #:+http-version-not-supported+
+           #:abort-request-handler
+           #:accept-connections
+           #:acceptor
+           #:acceptor-access-log-destination
+           #:acceptor-address
+           #:acceptor-listen-backlog
+           #:acceptor-dispatch-request
+           #:acceptor-error-template-directory
+           #:acceptor-input-chunking-p
+           #:acceptor-log-access
+           #:acceptor-log-message
+           #:acceptor-message-log-destination
+           #:acceptor-name
+           #:acceptor-output-chunking-p
+           #:acceptor-persistent-connections-p
+           #:acceptor-port
+           #:acceptor-read-timeout
+           #:acceptor-remove-session
+           #:acceptor-reply-class
+           #:acceptor-request-class
+           #:acceptor-ssl-p
+           #-:hunchentoot-no-ssl #:acceptor-ssl-certificate-file               
+           #-:hunchentoot-no-ssl #:acceptor-ssl-privatekey-file
+           #-:hunchentoot-no-ssl #:acceptor-ssl-privatekey-password
+           #:acceptor-status-message
+           #:acceptor-write-timeout
+           #:acceptor-document-root
+           #:acceptor-error-template-directory
+           #:authorization
+           #:aux-request-value
+           #:client-as-string
+           #:content-length
+           #:content-length*
+           #:content-type
+           #:content-type*
+           #:cookie-domain
+           #:cookie-expires
+           #:cookie-http-only
+           #:cookie-in
+           #:cookie-max-age
+           #:cookie-name
+           #:cookie-out
+           #:cookie-path
+           #:cookie-secure
+           #:cookie-value
+           #:cookies-in
+           #:cookies-in*
+           #:cookies-out
+           #:cookies-out*
+           #:create-folder-dispatcher-and-handler
+           #:create-prefix-dispatcher
+           #:create-regex-dispatcher
+           #:create-request-handler-thread
+           #:create-static-file-dispatcher-and-handler
+           #:decrement-taskmaster-thread-count
+           #:default-document-directory
+           #:define-easy-handler
+           #:delete-aux-request-value
+           #:delete-session-value
+           #:dispatch-easy-handlers
+           #:easy-acceptor
+           #-:hunchentoot-no-ssl #:easy-ssl-acceptor
+           #:escape-for-html
+           #:execute-acceptor
+           #:get-parameter
+           #:get-parameters
+           #:get-parameters*
+           #:handle-incoming-connection
+           #:handle-if-modified-since
+           #:handle-request
+           #:handle-static-file
+           #:header-in
+           #:header-in*
+           #:header-out
+           #:headers-in
+           #:headers-in*
+           #:headers-out
+           #:headers-out*
+           #:host
+           #:http-token-p
+           #:hunchentoot-condition
+           #:hunchentoot-error
+           #:hunchentoot-warning
+           #:increment-taskmaster-thread-count
+           #:initialize-connection-stream
+           #:log-message*
+           #:maybe-invoke-debugger
+           #:mime-type
+           #:multi-threaded-taskmaster
+           #:next-session-id
+           #:no-cache
+           #:one-thread-per-connection-taskmaster
+           #:parameter
+           #:parameter-error
+           #:post-parameter
+           #:post-parameters
+           #:post-parameters*
+           #:process-connection
+           #:process-request
+           #:query-string
+           #:query-string*
+           #:raw-post-data
+           #:real-remote-addr
+           #:reason-phrase
+           #:recompute-request-parameters
+           #:redirect
+           #:referer
+           #:regenerate-session-cookie-value
+           #:remote-addr
+           #:remote-addr*
+           #:remote-port
+           #:remote-port*
+           #:local-addr
+           #:local-addr*
+           #:local-port
+           #:local-port*
+           #:remove-session
+           #:reply
+           #:reply-external-format
+           #:reply-external-format*
+           #:request
+           #:request-acceptor
+           #:request-method
+           #:request-method*
+           #:request-pathname
+           #:request-uri
+           #:request-uri*
+           #:require-authorization
+           #:reset-connection-stream
+           #:reset-sessions
+           #:reset-session-secret
+           #:return-code
+           #:return-code*
+           #:rfc-1123-date
+           #:script-name
+           #:script-name*
+           #:send-headers
+           #:server-protocol
+           #:server-protocol*
+           #:session
+           #:session-cookie-name
+           #:session-cookie-value
+           #:session-created
+           #:session-db
+           #:session-db-lock
+           #:session-gc
+           #:session-id
+           #:session-max-time
+           #:session-remote-addr
+           #:session-start
+           #:session-too-old-p
+           #:session-user-agent
+           #:session-value
+           #:session-verify
+           #:set-cookie
+           #:set-cookie*
+           #:shutdown
+           #:single-threaded-taskmaster
+           #-:hunchentoot-no-ssl #:ssl-acceptor
+           #:ssl-p
+           #:start
+           #:start-listening
+           #:start-session
+           #:start-thread
+           #:started-p
+           #:stop
+           #:taskmaster
+           #:taskmaster-acceptor
+           #:taskmaster-max-accept-count
+           #:taskmaster-max-thread-count
+           #:taskmaster-thread-count
+           #:too-many-taskmaster-requests
+           #:url-decode
+           #:url-encode
+           #:user-agent
+           #:within-request-p
+           #:detach-socket
+           #:bad-request))
diff --git a/deps/hunchentoot/release-checklist.txt b/deps/hunchentoot/release-checklist.txt
new file mode 100644 (file)
index 0000000..afef4bb
--- /dev/null
@@ -0,0 +1,5 @@
+What do do for a release:
+
+Update version number in hunchentoot.asd and doc/index.xml
+Update CHANGELOG (keep format)
+Create html documentation (cd doc; make)
diff --git a/deps/hunchentoot/reply.lisp b/deps/hunchentoot/reply.lisp
new file mode 100644 (file)
index 0000000..194d5d4
--- /dev/null
@@ -0,0 +1,157 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defclass reply ()
+  ((content-type :reader content-type
+                 :documentation "The outgoing 'Content-Type' http
+header which defaults to the value of *DEFAULT-CONTENT-TYPE*.")
+   (content-length :reader content-length
+                   :initform nil
+                   :documentation "The outgoing 'Content-Length'
+http header which defaults NIL.  If this is NIL, Hunchentoot will
+compute the content length.")
+   (headers-out :initform nil
+                :reader headers-out
+                :documentation "An alist of the outgoing http headers
+not including the 'Set-Cookie', 'Content-Length', and 'Content-Type'
+headers.  Use the functions HEADER-OUT and \(SETF HEADER-OUT) to
+modify this slot.")
+   (return-code :initform +http-ok+
+                :accessor return-code
+                :documentation "The http return code of this
+reply.  The return codes Hunchentoot can handle are defined in
+specials.lisp.")
+   (external-format :initform *hunchentoot-default-external-format*
+                    :accessor reply-external-format
+                    :documentation "The external format of the reply -
+used for character output.")
+   (cookies-out :initform nil
+                :accessor cookies-out
+                :documentation "The outgoing cookies.  This slot's
+value should only be modified by the functions defined in
+cookies.lisp."))
+  (:documentation "Objects of this class hold all the information
+about an outgoing reply.  They are created automatically by
+Hunchentoot and can be accessed and modified by the corresponding
+handler.
+
+You should not mess with the slots of these objects directly, but you
+can subclass REPLY in order to implement your own behaviour.  See the
+REPLY-CLASS slot of the ACCEPTOR class."))
+
+(defmethod initialize-instance :after ((reply reply) &key)
+  (setf (header-out :content-type reply) *default-content-type*))
+
+(defun headers-out* (&optional (reply *reply*))
+  "Returns an alist of the outgoing headers associated with the
+REPLY object REPLY."
+  (headers-out reply))
+
+(defun cookies-out* (&optional (reply *reply*))
+  "Returns an alist of the outgoing cookies associated with the
+REPLY object REPLY."
+  (cookies-out reply))
+
+(defun (setf cookies-out*) (new-value &optional (reply *reply*))
+  "Sets the alist of the outgoing cookies associated with the REPLY
+object REPLY."
+  (setf (cookies-out reply) new-value))
+
+(defun content-type* (&optional (reply *reply*))
+  "The outgoing 'Content-Type' http header of REPLY."
+  (content-type reply))
+
+(defun (setf content-type*) (new-value &optional (reply *reply*))
+  "Sets the outgoing 'Content-Type' http header of REPLY."
+  (setf (header-out :content-type reply) new-value))
+
+(defun content-length* (&optional (reply *reply*))
+  "The outgoing 'Content-Length' http header of REPLY."
+  (content-length reply))
+
+(defun (setf content-length*) (new-value &optional (reply *reply*))
+  "Sets the outgoing 'Content-Length' http header of REPLY."
+  (setf (header-out :content-length reply) new-value))
+
+(defun return-code* (&optional (reply *reply*))
+  "The http return code of REPLY.  The return codes Hunchentoot can
+handle are defined in specials.lisp."
+  (return-code reply))
+
+(defun (setf return-code*) (new-value &optional (reply *reply*))
+  "Sets the http return code of REPLY."
+  (setf (return-code reply) new-value))
+
+(defun reply-external-format* (&optional (reply *reply*))
+  "The external format of REPLY which is used for character output."
+  (reply-external-format reply))
+
+(defun (setf reply-external-format*) (new-value &optional (reply *reply*))
+  "Sets the external format of REPLY."
+  (setf (reply-external-format reply) new-value))
+
+(defun header-out-set-p (name &optional (reply *reply*))
+  "Returns a true value if the outgoing http header named NAME has
+been specified already.  NAME should be a keyword or a string."
+  (assoc* name (headers-out reply)))
+
+(defun header-out (name &optional (reply *reply*))
+  "Returns the current value of the outgoing http header named NAME.
+NAME should be a keyword or a string."
+  (cdr (assoc name (headers-out reply))))
+
+(defun cookie-out (name &optional (reply *reply*))
+  "Returns the current value of the outgoing cookie named
+NAME. Search is case-sensitive."
+  (cdr (assoc name (cookies-out reply) :test #'string=)))
+
+(defgeneric (setf header-out) (new-value name &optional reply)
+  (:documentation "Changes the current value of the outgoing http
+header named NAME \(a keyword or a string).  If a header with this
+name doesn't exist, it is created.")
+  (:method (new-value (name symbol) &optional (reply *reply*))
+   ;; the default method
+   (let ((entry (assoc name (headers-out reply))))
+     (if entry
+       (setf (cdr entry) new-value)
+       (setf (slot-value reply 'headers-out)
+             (acons name new-value (headers-out reply))))
+     new-value))
+  (:method (new-value (name string) &optional (reply *reply*))
+   "If NAME is a string, it is converted to a keyword first."
+   (setf (header-out (as-keyword name :destructivep nil) reply) new-value))
+  (:method :after (new-value (name (eql :content-length)) &optional (reply *reply*))
+   "Special case for the `Content-Length' header."
+   (check-type new-value integer)
+   (setf (slot-value reply 'content-length) new-value))
+  (:method :after (new-value (name (eql :content-type)) &optional (reply *reply*))
+   "Special case for the `Content-Type' header."
+   (check-type new-value (or null string))
+   (setf (slot-value reply 'content-type) new-value)))
diff --git a/deps/hunchentoot/request.lisp b/deps/hunchentoot/request.lisp
new file mode 100644 (file)
index 0000000..75d4aa0
--- /dev/null
@@ -0,0 +1,622 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defclass request ()
+  ((acceptor :initarg :acceptor
+             :documentation "The acceptor which created this request
+object."
+             :reader request-acceptor)
+   (headers-in :initarg :headers-in
+               :documentation "An alist of the incoming headers."
+               :reader headers-in)
+   (method :initarg :method
+           :documentation "The request method as a keyword."
+           :reader request-method)
+   (uri :initarg :uri
+        :documentation "The request URI as a string."
+        :reader request-uri)
+   (server-protocol :initarg :server-protocol
+                    :documentation "The HTTP protocol as a keyword."
+                    :reader server-protocol)
+   (local-addr :initarg :local-addr
+               :documentation "The IP address of the local system
+that the client connected to."
+               :reader local-addr)
+   (local-port :initarg :local-port
+               :documentation "The TCP port number of the local
+system that the client connected to."
+               :reader local-port)
+   (remote-addr :initarg :remote-addr
+                :documentation "The IP address of the client that
+initiated this request."
+                :reader remote-addr)
+   (remote-port :initarg :remote-port
+                :documentation "The TCP port number of the client
+socket from which this request originated."
+                :reader remote-port)
+   (content-stream :initarg :content-stream
+                   :reader content-stream
+                   :documentation "A stream from which the request
+body can be read if there is one.")
+   (cookies-in :initform nil
+               :documentation "An alist of the cookies sent by the client."
+               :reader cookies-in)
+   (get-parameters :initform nil
+                   :documentation "An alist of the GET parameters sent
+by the client."
+                   :reader get-parameters)
+   (post-parameters :initform nil
+                    :documentation "An alist of the POST parameters
+sent by the client."
+                    :reader post-parameters)
+   (script-name :initform nil
+                :documentation "The URI requested by the client without
+the query string."
+                :reader script-name)
+   (query-string :initform nil
+                 :documentation "The query string of this request."
+                 :reader query-string)
+   (session :initform nil
+            :accessor session
+            :documentation "The session object associated with this
+request.")
+   (aux-data :initform nil
+             :accessor aux-data
+             :documentation "Used to keep a user-modifiable alist with
+arbitrary data during the request.")
+   (raw-post-data :initform nil
+                  :documentation "The raw string sent as the body of a
+POST request, populated only if not a multipart/form-data request."))
+  (:documentation "Objects of this class hold all the information
+about an incoming request.  They are created automatically by
+acceptors and can be accessed by the corresponding handler.
+
+You should not mess with the slots of these objects directly, but you
+can subclass REQUEST in order to implement your own behaviour.  See
+the REQUEST-CLASS slot of the ACCEPTOR class."))
+
+(defgeneric process-request (request)
+  (:documentation "This function is called by PROCESS-CONNECTION after
+the incoming headers have been read.  It calls HANDLE-REQUEST to
+select and call a handler and sends the output of this handler to the
+client using START-OUTPUT.  Note that PROCESS-CONNECTION is called
+once per connection and loops in case of a persistent connection while
+PROCESS-REQUEST is called anew for each request.
+
+Essentially, you can view process-request as a thin wrapper around
+HANDLE-REQUEST.
+
+The return value of this function is ignored."))
+
+(defun convert-hack (string external-format)
+  "The rfc2388 package is buggy in that it operates on a character
+stream and thus only accepts encodings which are 8 bit transparent.
+In order to support different encodings for parameter values
+submitted, we post process whatever string values the rfc2388 package
+has returned."
+  (flex:octets-to-string (map '(vector (unsigned-byte 8) *) 'char-code string)
+                         :external-format external-format))
+
+(defun parse-rfc2388-form-data (stream content-type-header external-format)
+  "Creates an alist of POST parameters from the stream STREAM which is
+supposed to be of content type 'multipart/form-data'."
+  (let* ((parsed-content-type-header (rfc2388:parse-header content-type-header :value))
+        (boundary (or (cdr (rfc2388:find-parameter
+                             "BOUNDARY"
+                             (rfc2388:header-parameters parsed-content-type-header)))
+                      (return-from parse-rfc2388-form-data))))
+    (loop for part in (rfc2388:parse-mime stream boundary)
+          for headers = (rfc2388:mime-part-headers part)
+          for content-disposition-header = (rfc2388:find-content-disposition-header headers)
+          for name = (cdr (rfc2388:find-parameter
+                           "NAME"
+                           (rfc2388:header-parameters content-disposition-header)))
+          when name
+          collect (cons (convert-hack name external-format)
+                        (let ((contents (rfc2388:mime-part-contents part)))
+                          (if (pathnamep contents)
+                            (list contents
+                                  (convert-hack (rfc2388:get-file-name headers) external-format)
+                                  (rfc2388:content-type part :as-string t))
+                            (convert-hack contents external-format)))))))
+
+(defun get-post-data (&key (request *request*) want-stream (already-read 0))
+  "Reads the request body from the stream and stores the raw contents
+\(as an array of octets) in the corresponding slot of the REQUEST
+object.  Returns just the stream if WANT-STREAM is true.  If there's a
+Content-Length header, it is assumed, that ALREADY-READ octets have
+already been read."
+  (let* ((headers-in (headers-in request))
+         (content-length (when-let (content-length-header (cdr (assoc :content-length headers-in
+                                                                      :test #'eq)))
+                           (parse-integer content-length-header :junk-allowed t)))
+         (content-stream (content-stream request)))
+    (setf (slot-value request 'raw-post-data)
+          (cond (want-stream
+                 (let ((stream (make-flexi-stream content-stream :external-format +latin-1+)))
+                   (when content-length
+                     (setf (flexi-stream-bound stream) content-length))
+                   stream))
+                ((and content-length (> content-length already-read))
+                 (decf content-length already-read)
+                 (when (input-chunking-p)
+                   ;; see RFC 2616, section 4.4
+                   (log-message* :warning "Got Content-Length header although input chunking is on."))
+                 (let ((content (make-array content-length :element-type 'octet)))
+                   (read-sequence content content-stream)
+                   content))
+                ((input-chunking-p)
+                 (loop with buffer = (make-array +buffer-length+ :element-type 'octet)
+                       with content = (make-array 0 :element-type 'octet :adjustable t)
+                       for index = 0 then (+ index pos)
+                       for pos = (read-sequence buffer content-stream)
+                       do (adjust-array content (+ index pos))
+                          (replace content buffer :start1 index :end2 pos)
+                       while (= pos +buffer-length+)
+                       finally (return content)))))))
+
+(defmethod initialize-instance :after ((request request) &rest init-args)
+  "The only initarg for a REQUEST object is :HEADERS-IN.  All other
+slot values are computed in this :AFTER method."
+  (declare (ignore init-args))
+  (with-slots (headers-in cookies-in get-parameters script-name query-string session)
+      request
+    (handler-case*
+     (let* ((uri (request-uri request))
+            (match-start (position #\? uri))
+            (external-format (or (external-format-from-content-type (cdr (assoc* :content-type headers-in)))
+                                 +utf-8+)))
+       (cond
+        (match-start
+         (setq script-name (url-decode (subseq uri 0 match-start) external-format)
+               query-string (subseq uri (1+ match-start))))
+        (t (setq script-name (url-decode uri external-format))))
+       ;; some clients (e.g. ASDF-INSTALL) send requests like
+       ;; "GET http://server/foo.html HTTP/1.0"...
+       (setq script-name (regex-replace "^https?://[^/]+" script-name ""))
+       ;; compute GET parameters from query string and cookies from
+       ;; the incoming 'Cookie' header
+       (setq get-parameters
+             (let ((*substitution-char* #\?))
+               (form-url-encoded-list-to-alist (split "&" query-string) external-format))
+             cookies-in
+             (cookies-to-alist (split "\\s*[,;]\\s*" (cdr (assoc :cookie headers-in
+                                                                 :test #'eq))))
+             session (session-verify request)
+             *session* session))
+     (error (condition)
+            (log-message* :error "Error when creating REQUEST object: ~A" condition)
+            ;; we assume it's not our fault...
+            (setf (return-code*) +http-bad-request+)))))
+
+(defmethod process-request (request)
+  "Standard implementation for processing a request."
+  (catch 'request-processed ; used by HTTP HEAD handling to end request processing in a HEAD request (see START-OUTPUT)
+    (let (*tmp-files*
+          *headers-sent*
+          (*request* request))
+      (unwind-protect
+           (with-mapped-conditions ()
+             (labels
+                 ((report-error-to-client (error &optional backtrace)
+                    (when *log-lisp-errors-p*
+                      (log-message* *lisp-errors-log-level* "~A~@[~%~A~]" error (when *log-lisp-backtraces-p*
+                                                                                  backtrace)))
+                    (start-output +http-internal-server-error+
+                                  (acceptor-status-message *acceptor*
+                                                           +http-internal-server-error+
+                                                           :error (princ-to-string error)
+                                                           :backtrace (princ-to-string backtrace)))))
+               (multiple-value-bind (contents error backtrace)
+                   ;; skip dispatch if bad request
+                   (when (eql (return-code *reply*) +http-ok+)
+                     (catch 'handler-done
+                       (handle-request *acceptor* *request*)))
+                 (when error
+                   ;; error occurred in request handler
+                   (report-error-to-client error backtrace))
+                 (unless *headers-sent*
+                   (handler-case
+                       (with-debugger
+                         (start-output (return-code *reply*)
+                                       (or contents
+                                           (acceptor-status-message *acceptor*
+                                                                    (return-code *reply*)))))
+                     (error (e)
+                       ;; error occurred while writing to the client.  attempt to report.
+                       (report-error-to-client e)))))))
+        (dolist (path *tmp-files*)
+          (when (and (pathnamep path) (probe-file path))
+            ;; the handler may have chosen to (re)move the uploaded
+            ;; file, so ignore errors that happen during deletion
+            (ignore-errors*
+              (delete-file path))))))))
+
+(defun within-request-p ()
+  "True if we're in the context of a request, otherwise nil."
+  (and (boundp '*request*) *request*))
+
+(defun parse-multipart-form-data (request external-format)
+  "Parse the REQUEST body as multipart/form-data, assuming that its
+content type has already been verified.  Returns the form data as
+alist or NIL if there was no data or the data could not be parsed."
+  (handler-case*
+      (let ((content-stream (make-flexi-stream (content-stream request) :external-format +latin-1+)))
+        (prog1
+            (parse-rfc2388-form-data content-stream (header-in :content-type request) external-format)
+          (let ((stray-data (get-post-data :already-read (flexi-stream-position content-stream))))
+            (when (and stray-data (plusp (length stray-data)))
+              (hunchentoot-warn "~A octets of stray data after form-data sent by client."
+                                (length stray-data))))))
+    (error (condition)
+      (log-message* :error "While parsing multipart/form-data parameters: ~A" condition)
+      nil)))
+
+(defun maybe-read-post-parameters (&key (request *request*) force external-format)
+  "Make surce that any POST parameters in the REQUEST are parsed.  The
+body of the request must be either application/x-www-form-urlencoded
+or multipart/form-data to be considered as containing POST parameters.
+If FORCE is true, parsing is done unconditionally.  Otherwise, parsing
+will only be done if the RAW-POST-DATA slot in the REQUEST is false.
+EXTERNAL-FORMAT specifies the external format of the data in the
+request body.  By default, the encoding is determined from the
+Content-Type header of the request or from
+*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT* if none is found."
+  (when (and (header-in :content-type request)
+             (member (request-method request) *methods-for-post-parameters* :test #'eq)
+             (or force
+                 (not (slot-value request 'raw-post-data)))
+            ;; can't reparse multipart posts, even when FORCEd
+            (not (eq t (slot-value request 'raw-post-data))))
+    (unless (or (header-in :content-length request)
+                (input-chunking-p))
+      (log-message* :warning "Can't read request body because there's ~
+no Content-Length header and input chunking is off.")
+      (return-from maybe-read-post-parameters nil))
+    (handler-case*
+        (multiple-value-bind (type subtype charset)
+              (parse-content-type (header-in :content-type request))
+          (let ((external-format (or external-format
+                                     (when charset
+                                       (handler-case
+                                           (make-external-format charset :eol-style :lf)
+                                         (error ()
+                                           (hunchentoot-warn "Ignoring ~
+unknown character set ~A in request content type."
+                                                 charset))))
+                                     *hunchentoot-default-external-format*)))
+            (setf (slot-value request 'post-parameters)
+                  (cond ((and (string-equal type "application")
+                              (string-equal subtype "x-www-form-urlencoded"))
+                         (form-url-encoded-list-to-alist
+                          (split "&" (raw-post-data :request request :external-format +latin-1+))
+                          external-format))
+                        ((and (string-equal type "multipart")
+                              (string-equal subtype "form-data"))
+                         (prog1 (parse-multipart-form-data request external-format)
+                           (setf (slot-value request 'raw-post-data) t)))))))
+      (error (condition)
+        (log-message* :error "Error when reading POST parameters from body: ~A" condition)
+        ;; this is not the right thing to do because it could happen
+        ;; that we aren't finished reading from the request stream and
+        ;; can't send a reply - to be revisited
+        (setf (return-code*) +http-bad-request+
+              *finish-processing-socket* t)
+        (abort-request-handler)))))
+
+(defun recompute-request-parameters (&key (request *request*)
+                                          (external-format *hunchentoot-default-external-format*))
+  "Recomputes the GET and POST parameters for the REQUEST object
+REQUEST.  This only makes sense if you're switching external formats
+during the request."
+  (maybe-read-post-parameters :request request :force t :external-format external-format)
+  (setf (slot-value request 'get-parameters)
+        (form-url-encoded-list-to-alist (split "&" (query-string request)) external-format))
+  (values))
+                                                
+(defun script-name* (&optional (request *request*))
+  "Returns the file name of the REQUEST object REQUEST. That's the
+requested URI without the query string \(i.e the GET parameters)."
+  (script-name request))
+
+(defun query-string* (&optional (request *request*))
+  "Returns the query string of the REQUEST object REQUEST. That's
+the part behind the question mark \(i.e. the GET parameters)."
+  (query-string request))
+
+(defun get-parameters* (&optional (request *request*))
+  "Returns an alist of the GET parameters associated with the REQUEST
+object REQUEST."
+  (get-parameters request))
+
+(defmethod post-parameters :before ((request request))
+  ;; Force here because if someone calls POST-PARAMETERS they actually
+  ;; want them, regardless of why the RAW-POST-DATA has been filled
+  ;; in. (For instance, if SEND-HEADERS has been called, filling in
+  ;; RAW-POST-DATA, and then subsequent code calls POST-PARAMETERS,
+  ;; without the :FORCE flag POST-PARAMETERS would return NIL.)
+  (maybe-read-post-parameters
+   :request request :force (not (slot-value request 'post-parameters))))
+
+(defun post-parameters* (&optional (request *request*))
+  "Returns an alist of the POST parameters associated with the REQUEST
+object REQUEST."
+  (post-parameters request))
+
+(defun headers-in* (&optional (request *request*))
+  "Returns an alist of the incoming headers associated with the
+REQUEST object REQUEST."
+  (headers-in request))
+
+(defun cookies-in* (&optional (request *request*))
+  "Returns an alist of all cookies associated with the REQUEST object
+REQUEST."
+  (cookies-in request))
+
+(defgeneric header-in (name request)
+  (:documentation "Returns the incoming header with name NAME.  NAME
+can be a keyword \(recommended) or a string.")
+  (:method (name request)
+   (cdr (assoc* name (headers-in request)))))
+
+(defun header-in* (name &optional (request *request*))
+  "Returns the incoming header with name NAME.  NAME can be a keyword
+\(recommended) or a string."
+  (header-in name request))
+
+(defun authorization (&optional (request *request*))
+  "Returns as two values the user and password \(if any) as encoded in
+the 'AUTHORIZATION' header.  Returns NIL if there is no such header."
+  (let* ((authorization (header-in :authorization request))
+         (start (and authorization
+                     (> (length authorization) 5)
+                     (string-equal "Basic" authorization :end2 5)
+                     (scan "\\S" authorization :start 5))))
+    (when start
+      (destructuring-bind (&optional user password)
+          (split ":" (base64:base64-string-to-string (subseq authorization start)) :limit 2)
+        (values user password)))))
+
+(defun remote-addr* (&optional (request *request*))
+  "Returns the address the current request originated from."
+  (remote-addr request))
+
+(defun remote-port* (&optional (request *request*))
+  "Returns the port the current request originated from."
+  (remote-port request))
+
+(defun local-addr* (&optional (request *request*))
+  "Returns the address the current request connected to."
+  (local-addr request))
+
+(defun local-port* (&optional (request *request*))
+  "Returns the port the current request connected to."
+  (local-port request))
+
+(defun real-remote-addr (&optional (request *request*))
+  "Returns the 'X-Forwarded-For' incoming http header as the
+second value in the form of a list of IP addresses and the first
+element of this list as the first value if this header exists.
+Otherwise returns the value of REMOTE-ADDR as the only value."
+  (let ((x-forwarded-for (header-in :x-forwarded-for request)))
+    (cond (x-forwarded-for (let ((addresses (split "\\s*,\\s*" x-forwarded-for)))
+                             (values (first addresses) addresses)))
+          (t (remote-addr request)))))
+
+(defun host (&optional (request *request*))
+  "Returns the 'Host' incoming http header value."
+  (header-in :host request))
+
+(defun request-uri* (&optional (request *request*))
+  "Returns the request URI."
+  (request-uri request))
+
+(defun request-method* (&optional (request *request*))
+  "Returns the request method as a Lisp keyword."
+  (request-method request))
+
+(defun server-protocol* (&optional (request *request*))
+  "Returns the request protocol as a Lisp keyword."
+  (server-protocol request))
+
+(defun user-agent (&optional (request *request*))
+  "Returns the 'User-Agent' http header."
+  (header-in :user-agent request))
+
+(defun cookie-in (name &optional (request *request*))
+  "Returns the cookie with the name NAME \(a string) as sent by the
+browser - or NIL if there is none."
+  (cdr (assoc name (cookies-in request) :test #'string=)))
+
+(defun referer (&optional (request *request*))
+  "Returns the 'Referer' \(sic!) http header."
+  (header-in :referer request))
+
+(defun get-parameter (name &optional (request *request*))
+  "Returns the GET parameter with name NAME \(a string) - or NIL if
+there is none.  Search is case-sensitive."
+  (cdr (assoc name (get-parameters request) :test #'string=)))
+
+(defun post-parameter (name &optional (request *request*))
+  "Returns the POST parameter with name NAME \(a string) - or NIL if
+there is none.  Search is case-sensitive."
+  (cdr (assoc name (post-parameters request) :test #'string=)))
+
+(defun parameter (name &optional (request *request*))
+  "Returns the GET or the POST parameter with name NAME \(a string) -
+or NIL if there is none.  If both a GET and a POST parameter with the
+same name exist the GET parameter is returned.  Search is
+case-sensitive."
+  (or (get-parameter name request)
+      (post-parameter name request)))
+
+(defun handle-if-modified-since (time &optional (request *request*))
+  "Handles the 'If-Modified-Since' header of REQUEST.  The date string
+is compared to the one generated from the supplied universal time
+TIME."
+  (let ((if-modified-since (header-in :if-modified-since request))
+        (time-string (rfc-1123-date time)))
+    ;; simple string comparison is sufficient; see RFC 2616 14.25
+    (when (and if-modified-since
+               (equal if-modified-since time-string))
+      (setf (slot-value *reply* 'content-length) nil
+           (slot-value *reply* 'headers-out) (remove :content-length (headers-out*) :key #'car)
+           (return-code*) +http-not-modified+)
+      (abort-request-handler))
+    (values)))
+
+(defun external-format-from-content-type (content-type)
+  "Creates and returns an external format corresponding to the value
+of the content type header provided in CONTENT-TYPE.  If the content
+type was not set or if the character set specified was invalid, NIL is
+returned."
+  (when content-type
+    (when-let (charset (nth-value 2 (parse-content-type content-type)))
+      (handler-case
+          (make-external-format (as-keyword charset) :eol-style :lf)
+        (error ()
+          (hunchentoot-warn "Invalid character set ~S in request has been ignored."
+                            charset))))))
+
+(defun raw-post-data (&key (request *request*) external-format force-text force-binary want-stream)
+  "Returns the content sent by the client if there was any \(unless
+the content type was \"multipart/form-data\").  By default, the result
+is a string if the type of the `Content-Type' media type is \"text\",
+and a vector of octets otherwise.  In the case of a string, the
+external format to be used to decode the content will be determined
+from the `charset' parameter sent by the client \(or otherwise
+*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT* will be used).
+
+You can also provide an external format explicitly \(through
+EXTERNAL-FORMAT) in which case the result will unconditionally be a
+string.  Likewise, you can provide a true value for FORCE-TEXT which
+will force Hunchentoot to act as if the type of the media type had
+been \"text\".  Or you can provide a true value for FORCE-BINARY which
+means that you want a vector of octets at any rate.
+
+If, however, you provide a true value for WANT-STREAM, the other
+parameters are ignored and you'll get the content \(flexi) stream to
+read from it yourself.  It is then your responsibility to read the
+correct amount of data, because otherwise you won't be able to return
+a response to the client.  If the content type of the request was
+`multipart/form-data' or `application/x-www-form-urlencoded', the
+content has been read by Hunchentoot already and you can't read from
+the stream anymore.
+
+You can call RAW-POST-DATA more than once per request, but you can't
+mix calls which have different values for WANT-STREAM.
+
+Note that this function is slightly misnamed because a client can send
+content even if the request method is not POST."
+  (when (and force-binary force-text)
+    (parameter-error "It doesn't make sense to set both FORCE-BINARY and FORCE-TEXT to a true value."))
+  (unless (or external-format force-binary)
+    (setq external-format (or (external-format-from-content-type (header-in :content-type request))
+                              (when force-text
+                                *hunchentoot-default-external-format*))))
+  (let ((raw-post-data (or (slot-value request 'raw-post-data)
+                           (get-post-data :request request :want-stream want-stream))))
+    (cond ((typep raw-post-data 'stream) raw-post-data)
+          ((member raw-post-data '(t nil)) nil)
+          (external-format (octets-to-string raw-post-data :external-format external-format))
+          (t raw-post-data))))
+
+(defun aux-request-value (symbol &optional (request *request*))
+  "Returns the value associated with SYMBOL from the request object
+REQUEST \(the default is the current request) if it exists.  The
+second return value is true if such a value was found."
+  (when request
+    (let ((found (assoc symbol (aux-data request) :test #'eq)))
+      (values (cdr found) found))))
+
+(defsetf aux-request-value (symbol &optional request)
+    (new-value)
+  "Sets the value associated with SYMBOL from the request object
+REQUEST \(default is *REQUEST*).  If there is already a value
+associated with SYMBOL it will be replaced."
+  (with-rebinding (symbol)
+    (with-unique-names (place %request)
+      `(let* ((,%request (or ,request *request*))
+              (,place (assoc ,symbol (aux-data ,%request) :test #'eq)))
+         (cond
+           (,place
+            (setf (cdr ,place) ,new-value))
+           (t
+            (push (cons ,symbol ,new-value)
+                  (aux-data ,%request))
+            ,new-value))))))
+
+(defun delete-aux-request-value (symbol &optional (request *request*))
+  "Removes the value associated with SYMBOL from the request object
+REQUEST."
+  (when request
+    (setf (aux-data request)
+            (delete symbol (aux-data request)
+                    :key #'car :test #'eq)))
+  (values))
+
+(defun parse-path (path)
+  "Return a relative pathname that has been verified to not contain
+  any directory traversals or explicit device or host fields.  Returns
+  NIL if the path is not acceptable."
+  (when (every #'graphic-char-p path)
+    (let* ((pathname (#+sbcl sb-ext:parse-native-namestring
+                      #+ccl ccl:native-to-pathname
+                      ;; Just disallow anything with :wild components later.
+                      #-(or ccl sbcl) parse-namestring
+                      (remove #\\ (regex-replace "^/*" path ""))))
+           (directory (pathname-directory pathname)))
+      (when (and (or (null (pathname-host pathname))
+                     (equal (pathname-host pathname)
+                            (pathname-host *default-pathname-defaults*)))
+                 (or (null (pathname-device pathname))
+                     (equal (pathname-device pathname)
+                            (pathname-device *default-pathname-defaults*)))
+                 (or (null directory)
+                     (and (eql (first directory) :relative)
+                          ;; only string components, no :UP traversals or :WILD
+                          (every #'stringp (rest directory))))
+                 #-(or sbcl ccl) ;; parse-native-namestring should handle this
+                 (and
+                  (typep (pathname-name pathname) '(or null string)) ; no :WILD
+                  (typep (pathname-type pathname) '(or null string)))
+                 (not (equal (file-namestring pathname) "..")))
+        pathname))))
+
+(defun request-pathname (&optional (request *request*) drop-prefix)
+  "Construct a relative pathname from the request's SCRIPT-NAME.
+If DROP-PREFIX is given, pathname construction starts at the first path
+segment after the prefix.
+"
+  (let ((path (script-name request)))
+    (if drop-prefix
+        (when (starts-with-p path drop-prefix)
+          (parse-path (subseq path (length drop-prefix))))
+        (parse-path path))))
diff --git a/deps/hunchentoot/run-test.lisp b/deps/hunchentoot/run-test.lisp
new file mode 100644 (file)
index 0000000..a6f104c
--- /dev/null
@@ -0,0 +1,87 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2011, Hans Huebner.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(defparameter *test-port* 4241)
+
+(asdf:oos 'asdf:load-op :hunchentoot-test)
+
+(defun run-tests ()
+  (format t "~&;; Starting web server on localhost:~A." *test-port*)
+  (force-output)
+  (let ((server (hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port *test-port*))))
+    (unwind-protect
+         (progn
+           (format t "~&;; Sleeping 2 seconds to give the server some time to start...")
+           (force-output)
+           (sleep 2)
+           (format t "~&;; Now running confidence tests.")
+           (force-output)
+           (hunchentoot-test:test-hunchentoot (format nil "http://localhost:~A" *test-port*)))
+      (format t "~&;; Stopping server.")
+      (force-output)
+      (hunchentoot:stop server)
+      (format t "~&;; Cleaning temporary files.")
+      (hunchentoot-test::clean-tmp-dir))))
+
+#-sbcl
+(run-tests)
+
+;;; KLUDGE (by Nikodemus Siivola)
+;;;
+;;; SBCL grabs a massive lock in WITH-COMPILATION-UNIT, which ASDF
+;;; uses in PERFORM-PLAN ... which makes spawning threads during testing
+;;; problematic to say the least.
+;;;
+;;; So, release the world lock for the duration. Nikodemus says that in this
+;;; specific usage this should be safe --- and promises that people who copy
+;;; this code and use it elsewhere will burn in hell for their sins.
+;;;
+;;; More promisingly, he swears up and down that that massive lock from
+;;; W-C-U will be gone by early 2012 at the latest, so this will not be
+;;; an eternal kludge, we hope.
+(defun %call-without-world-lock-kludge (thunk)
+  #+(and sbcl sb-thread)
+  (let ((s (find-symbol "**WORLD-LOCK**" :sb-c)))
+    (if (and s (boundp s))
+        (let ((lock (symbol-value s)))
+          (unwind-protect
+               (progn
+                 (if (sb-thread:holding-mutex-p lock)
+                     (sb-thread:release-mutex lock)
+                     (setf lock nil))
+                 (funcall thunk))
+            (when lock
+              (sb-thread:grab-mutex lock))))
+        (funcall thunk)))
+  #-(and sbcl sb-thread)
+  (funcall thunk))
+
+#+sbcl
+(%call-without-world-lock-kludge 'run-tests)
\ No newline at end of file
diff --git a/deps/hunchentoot/session.lisp b/deps/hunchentoot/session.lisp
new file mode 100644 (file)
index 0000000..41bb714
--- /dev/null
@@ -0,0 +1,381 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defgeneric session-db-lock (acceptor &key whole-db-p)
+  (:documentation "A function which returns a lock that will be used
+to prevent concurrent access to sessions.  The first argument will be
+the acceptor that handles the current request, the second argument is
+true if the whole \(current) session database is modified.  If it is
+NIL, only one existing session in the database is modified.
+
+This function can return NIL which means that sessions or session
+databases will be modified without a lock held \(for example for
+single-threaded environments).  The default is to always return a
+global lock \(ignoring the ACCEPTOR argument) for Lisps that support
+threads and NIL otherwise."))
+
+(defmethod session-db-lock ((acceptor t) &key (whole-db-p t))
+  (declare (ignore whole-db-p))
+  *global-session-db-lock*)
+
+(defmacro with-session-lock-held ((lock) &body body)
+  "This is like WITH-LOCK-HELD except that it will accept NIL as a
+\"lock\" and just execute BODY in this case."
+  (with-unique-names (thunk)
+    (with-rebinding (lock)
+      `(flet ((,thunk () ,@body))
+         (cond (,lock (with-lock-held (,lock) (,thunk)))
+               (t (,thunk)))))))
+
+(defgeneric session-db (acceptor)
+  (:documentation "Returns the current session database which is an
+alist where each car is a session's ID and the cdr is the
+corresponding SESSION object itself.  The default is to use a global
+list for all acceptors."))
+
+(defmethod session-db ((acceptor t))
+  *session-db*)
+
+(defgeneric (setf session-db) (new-value acceptor)
+  (:documentation "Modifies the current session database.  See SESSION-DB."))
+
+(defmethod (setf session-db) (new-value (acceptor t))
+  (setq *session-db* new-value))
+
+(defgeneric next-session-id (acceptor)
+  (:documentation "Returns the next sequential session ID, an integer,
+which should be unique per session.  The default method uses a simple
+global counter and isn't guarded by a lock.  For a high-performance
+production environment you might consider using a more robust
+implementation."))
+
+(let ((session-id-counter 0))
+  (defmethod next-session-id ((acceptor t))
+    (incf session-id-counter)))
+
+(defclass session ()
+  ((session-id :initform (next-session-id (request-acceptor *request*))
+               :reader session-id
+               :type integer
+               :documentation "The unique ID \(an INTEGER) of the session.")
+   (session-string :reader session-string
+                   :documentation "The session string encodes enough
+data to safely retrieve this session.  It is sent to the browser as a
+cookie value or as a GET parameter.")
+   (user-agent :initform (user-agent *request*)
+               :reader session-user-agent
+               :documentation "The incoming 'User-Agent' header that
+was sent when this session was created.")
+   (remote-addr :initform (real-remote-addr *request*)
+                :reader session-remote-addr
+                :documentation "The remote IP address of the client
+when this session was started as returned by REAL-REMOTE-ADDR.")
+   (session-start :initform (get-universal-time)
+                  :reader session-start
+                  :documentation "The time this session was started.")
+   (last-click :initform (get-universal-time)
+               :reader session-last-click
+               :documentation "The last time this session was used.")
+   (session-data :initarg :session-data
+                 :initform nil
+                 :reader session-data
+                 :documentation "Data associated with this session -
+see SESSION-VALUE.")
+   (max-time :initarg :max-time
+             :initform *session-max-time*
+             :accessor session-max-time
+             :type fixnum
+             :documentation "The time \(in seconds) after which this
+session expires if it's not used."))
+  (:documentation "SESSION objects are automatically maintained by
+Hunchentoot.  They should not be created explicitly with MAKE-INSTANCE
+but implicitly with START-SESSION and they should be treated as opaque
+objects.
+
+You can ignore Hunchentoot's SESSION objects altogether and implement
+your own sessions if you provide corresponding methods for
+SESSION-COOKIE-VALUE and SESSION-VERIFY."))
+
+(defun encode-session-string (id user-agent remote-addr start)
+  "Creates a uniquely encoded session string based on the values ID,
+USER-AGENT, REMOTE-ADDR, and START"
+  (unless (boundp '*session-secret*)
+    (hunchentoot-warn "Session secret is unbound.  Using Lisp's RANDOM function to initialize it.")
+    (reset-session-secret))
+  ;; *SESSION-SECRET* is used twice due to known theoretical
+  ;; vulnerabilities of MD5 encoding
+  (md5-hex (concatenate 'string
+                       *session-secret*
+                       (md5-hex (format nil "~A~A~@[~A~]~@[~A~]~A"
+                                         *session-secret*
+                                         id
+                                         (and *use-user-agent-for-sessions*
+                                              user-agent)
+                                         (and *use-remote-addr-for-sessions*
+                                              remote-addr)
+                                         start)))))
+
+(defun stringify-session (session)
+  "Creates a string representing the SESSION object SESSION. See
+ENCODE-SESSION-STRING."
+  (encode-session-string (session-id session)
+                         (session-user-agent session)
+                         (session-remote-addr session)
+                         (session-start session)))
+
+(defmethod initialize-instance :after ((session session) &rest init-args)
+  "Set SESSION-STRING slot after the session has been initialized."
+  (declare (ignore init-args))
+  (setf (slot-value session 'session-string) (stringify-session session)))
+
+(defun session-gc ()
+  "Removes sessions from the current session database which are too
+old - see SESSION-TOO-OLD-P."
+  (with-session-lock-held ((session-db-lock *acceptor*))
+    (setf (session-db *acceptor*)
+          (loop for id-session-pair in (session-db *acceptor*)
+                for (nil . session) = id-session-pair
+                when (session-too-old-p session)
+                do (acceptor-remove-session *acceptor* session)
+                else
+                collect id-session-pair)))
+  (values))
+
+(defun session-value (symbol &optional (session *session*))
+  "Returns the value associated with SYMBOL from the session object
+SESSION \(the default is the current session) if it exists."
+  (when session
+    (let ((found (assoc symbol (session-data session) :test #'eq)))
+      (values (cdr found) found))))
+
+(defsetf session-value (symbol &optional session)
+    (new-value)
+  "Sets the value associated with SYMBOL from the session object
+SESSION. If there is already a value associated with SYMBOL it will be
+replaced. Will automatically start a session if none was supplied and
+there's no session for the current request."
+  (with-rebinding (symbol)
+    (with-unique-names (place %session)
+      `(let ((,%session (or ,session (start-session))))
+         (with-session-lock-held ((session-db-lock *acceptor* :whole-db-p nil))
+           (let* ((,place (assoc ,symbol (session-data ,%session) :test #'eq)))
+             (cond
+               (,place
+                (setf (cdr ,place) ,new-value))
+               (t
+                (push (cons ,symbol ,new-value)
+                      (slot-value ,%session 'session-data))
+                ,new-value))))))))
+
+(defun delete-session-value (symbol &optional (session *session*))
+  "Removes the value associated with SYMBOL from SESSION if there is
+one."
+  (when session
+    (setf (slot-value session 'session-data)
+            (delete symbol (session-data session)
+                    :key #'car :test #'eq)))
+  (values))
+
+(defgeneric session-cookie-value (session)
+  (:documentation "Returns a string which can be used to safely
+restore the session SESSION if as session has already been
+established.  This is used as the value stored in the session cookie
+or in the corresponding GET parameter and verified by SESSION-VERIFY.
+
+A default method is provided and there's no reason to change it unless
+you want to use your own session objects."))
+
+(defmethod session-cookie-value ((session session))
+  (and session
+       (format nil
+               "~D:~A"
+               (session-id session)
+               (session-string session))))
+
+(defgeneric session-cookie-name (acceptor)
+  (:documentation "Returns the name \(a string) of the cookie \(or the
+GET parameter) which is used to store a session on the client side.
+The default is to use the string \"hunchentoot-session\", but you can
+specialize this function if you want another name."))
+
+(defmethod session-cookie-name ((acceptor t))
+  "hunchentoot-session")
+
+(defgeneric session-created (acceptor new-session)
+  (:documentation "This function is called whenever a new session has
+been created.  There's a default method which might trigger a session
+GC based on the value of *SESSION-GC-FREQUENCY*.
+
+The return value is ignored."))
+
+(let ((global-session-usage-counter 0))
+  (defmethod session-created ((acceptor t) (session t))
+    "Counts session usage globally and triggers session GC if
+necessary."
+    (when (and *session-gc-frequency*
+               (zerop (mod (incf global-session-usage-counter)
+                           *session-gc-frequency*)))
+      (session-gc))))
+
+(defun start-session ()
+  "Returns the current SESSION object. If there is no current session,
+creates one and updates the corresponding data structures. In this
+case the function will also send a session cookie to the browser."
+  (let ((session (session *request*)))
+    (when session
+      (return-from start-session session))
+    (setf session (make-instance 'session)
+          (session *request*) session)
+    (with-session-lock-held ((session-db-lock *acceptor*))
+      (setf (session-db *acceptor*)
+            (acons (session-id session) session (session-db *acceptor*))))
+    (set-cookie (session-cookie-name *acceptor*)
+                :value (session-cookie-value session)
+                :path "/"
+                :http-only t)
+    (session-created *acceptor* session)
+    (setq *session* session)))
+
+(defun remove-session (session)
+  "Completely removes the SESSION object SESSION from Hunchentoot's
+internal session database."
+  (set-cookie (session-cookie-name *acceptor*)
+              :value "deleted"
+              :path "/"
+              :expires 0)
+  (with-session-lock-held ((session-db-lock *acceptor*))
+    (acceptor-remove-session *acceptor* session)
+    (setf (session-db *acceptor*)
+          (delete (session-id session) (session-db *acceptor*)
+                  :key #'car :test #'=)))
+  (values))
+
+(defun session-too-old-p (session)
+  "Returns true if the SESSION object SESSION has not been active in
+the last \(SESSION-MAX-TIME SESSION) seconds."
+  (< (+ (session-last-click session) (session-max-time session))
+     (get-universal-time)))
+
+(defun get-stored-session (id)
+  "Returns the SESSION object corresponding to the number ID if the
+session has not expired. Will remove the session if it has expired but
+will not create a new one."
+  (let ((session
+         (cdr (assoc id (session-db *acceptor*) :test #'=))))
+    (when (and session
+               (session-too-old-p session))
+      (when *reply*
+        (log-message* :info "Session with ID ~A too old" id))
+      (remove-session session)
+      (setq session nil))
+    session))
+
+(defun regenerate-session-cookie-value (session)
+  "Regenerates the cookie value. This should be used
+when a user logs in according to the application to prevent against
+session fixation attacks. The cookie value being dependent on ID,
+USER-AGENT, REMOTE-ADDR, START, and *SESSION-SECRET*, the only value
+we can change is START to regenerate a new value. Since we're
+generating a new cookie, it makes sense to have the session being
+restarted, in time. That said, because of this fact, calling this
+function twice in the same second will regenerate twice the same value."
+  (setf (slot-value session 'session-start) (get-universal-time)
+        (slot-value session 'session-string) (stringify-session session))
+  (set-cookie (session-cookie-name *acceptor*)
+              :value (session-cookie-value session)
+              :path "/"
+              :http-only t))
+
+(defgeneric session-verify (request)
+  (:documentation "Tries to get a session identifier from the cookies
+\(or alternatively from the GET parameters) sent by the client (see
+SESSION-COOKIE-NAME and SESSION-COOKIE-VALUE).  This identifier is
+then checked for validity against the REQUEST object REQUEST.  On
+success the corresponding session object \(if not too old) is returned
+\(and updated).  Otherwise NIL is returned.
+
+A default method is provided and you only need to write your own one
+if you want to maintain your own sessions."))
+
+(defmethod session-verify ((request request))
+  (let ((session-identifier (or (when-let (session-cookie (cookie-in (session-cookie-name *acceptor*) request))
+                                  (url-decode session-cookie))
+                                (get-parameter (session-cookie-name *acceptor*) request))))
+    (when (and (stringp session-identifier)
+               (scan "^\\d+:.+" session-identifier))
+      (destructuring-bind (id-string session-string)
+          (split ":" session-identifier :limit 2)
+        (let* ((id (parse-integer id-string))
+               (session (get-stored-session id))
+               (user-agent (user-agent request))
+               (remote-addr (remote-addr request)))
+          (cond
+            ((and session
+                  (string= session-string
+                           (session-string session))
+                  (string= session-string
+                           (encode-session-string id
+                                                  user-agent
+                                                  (real-remote-addr request)
+                                                  (session-start session))))
+             ;; the session key presented by the client is valid
+             (setf (slot-value session 'last-click) (get-universal-time))
+             session)
+            (session
+             ;; the session ID pointed to an existing session, but the
+             ;; session string did not match the expected session string
+             (log-message* :warning
+                           "Fake session identifier '~A' (User-Agent: '~A', IP: '~A')"
+                           session-identifier user-agent remote-addr)
+             ;; remove the session to make sure that it can't be used
+             ;; again; the original legitimate user will be required to
+             ;; log in again
+             (remove-session session)
+             nil)
+            (t
+             ;; no session was found under the ID given, presumably
+             ;; because it has expired.
+             (log-message* :info
+                           "No session for session identifier '~A' (User-Agent: '~A', IP: '~A')"
+                           session-identifier user-agent remote-addr)
+             nil)))))))
+
+(defun reset-session-secret ()
+  "Sets *SESSION-SECRET* to a new random value. All old sessions will
+cease to be valid."
+  (setq *session-secret* (create-random-string 10 36)))
+
+(defun reset-sessions (&optional (acceptor *acceptor*))
+  "Removes ALL stored sessions of ACCEPTOR."
+  (with-session-lock-held ((session-db-lock acceptor))
+    (loop for (nil . session) in (session-db acceptor)
+          do (acceptor-remove-session acceptor session))
+    (setq *session-db* nil))
+  (values))
diff --git a/deps/hunchentoot/set-timeouts.lisp b/deps/hunchentoot/set-timeouts.lisp
new file mode 100644 (file)
index 0000000..e00e95d
--- /dev/null
@@ -0,0 +1,85 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defun set-timeouts (usocket read-timeout write-timeout)
+  "Sets up timeouts on the given USOCKET object.  READ-TIMEOUT is the
+read timeout period, WRITE-TIMEOUT is the write timeout, specified in
+\(fractional) seconds.  The timeouts can either be implemented using
+the low-level socket options SO_RCVTIMEO and SO_SNDTIMEO or some
+other, implementation specific mechanism.  On platforms that do not
+support separate read and write timeouts, both must be equal or an
+error will be signaled.  READ-TIMEOUT and WRITE-TIMEOUT may be NIL,
+which means that the corresponding socket timeout value will not be
+set."
+  (declare (ignorable usocket read-timeout write-timeout))
+  ;; add other Lisps here if necessary
+  #+(or :sbcl :cmu :abcl)
+  (unless (eql read-timeout write-timeout)
+    (parameter-error "Read and write timeouts for socket must be equal."))
+  #+:clisp
+  (when read-timeout
+    (socket:socket-options (usocket:socket usocket) :SO-RCVTIMEO read-timeout))
+  #+:clisp
+  (when write-timeout
+    (socket:socket-options (usocket:socket usocket) :SO-SNDTIMEO write-timeout))
+  #+:ecl
+  (when read-timeout
+    (setf (sb-bsd-sockets:sockopt-receive-timeout (usocket:socket usocket))
+         read-timeout))
+  #+:ecl
+  (when write-timeout
+    (setf (sb-bsd-sockets:sockopt-send-timeout (usocket:socket usocket))
+         write-timeout))
+  #+:openmcl
+  (when read-timeout
+    (setf (ccl:stream-input-timeout (usocket:socket usocket))
+          read-timeout))
+  #+:openmcl
+  (when write-timeout
+    (setf (ccl:stream-output-timeout (usocket:socket usocket))
+          write-timeout))
+  #+:sbcl
+  (when read-timeout
+    (setf (sb-impl::fd-stream-timeout (usocket:socket-stream usocket))
+          (coerce read-timeout 'single-float)))
+  #+:cmu
+  (setf (lisp::fd-stream-timeout (usocket:socket-stream usocket))
+        (coerce read-timeout 'integer))
+  #+:abcl
+  (when read-timeout
+    (java:jcall (java:jmethod "java.net.Socket" "setSoTimeout"  "int")
+                (usocket:socket usocket)
+                (* 1000 read-timeout)))
+  #+:abcl
+  (when write-timeout
+    (warn "Unimplemented."))
+  #-(or :clisp :allegro :openmcl :sbcl :lispworks :cmu :ecl :abcl)
+  (not-implemented 'set-timeouts))
+
diff --git a/deps/hunchentoot/specials.lisp b/deps/hunchentoot/specials.lisp
new file mode 100644 (file)
index 0000000..a0c98ce
--- /dev/null
@@ -0,0 +1,310 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defmacro defconstant (name value &optional doc)
+  "Make sure VALUE is evaluated only once \(to appease SBCL)."
+  `(cl:defconstant ,name (if (boundp ',name) (symbol-value ',name) ,value)
+     ,@(when doc (list doc))))
+
+(eval-when (:compile-toplevel :execute :load-toplevel)
+  (defmacro defvar-unbound (name &optional (doc-string ""))
+    "Convenience macro to declare unbound special variables with a
+documentation string."
+    `(progn
+      (defvar ,name)
+      (setf (documentation ',name 'variable) ,doc-string)
+      ',name))
+
+  (defvar *http-reason-phrase-map* (make-hash-table)
+    "Used to map numerical return codes to reason phrases.")
+  
+  (defmacro def-http-return-code (name value reason-phrase)
+    "Shortcut to define constants for return codes.  NAME is a
+Lisp symbol, VALUE is the numerical value of the return code, and
+REASON-PHRASE is the phrase \(a string) to be shown in the
+server's status line."
+    `(eval-when (:compile-toplevel :execute :load-toplevel)
+       (defconstant ,name ,value ,(format nil "HTTP return code \(~A) for '~A'."
+                                          value reason-phrase))
+       (setf (gethash ,value *http-reason-phrase-map*) ,reason-phrase))))
+
+(defconstant +crlf+
+  (make-array 2 :element-type '(unsigned-byte 8)
+              :initial-contents (mapcar 'char-code '(#\Return #\Linefeed)))
+  "A 2-element array consisting of the character codes for a CRLF
+sequence.")
+
+(def-http-return-code +http-continue+ 100 "Continue")
+(def-http-return-code +http-switching-protocols+ 101 "Switching Protocols")
+(def-http-return-code +http-ok+ 200 "OK")
+(def-http-return-code +http-created+ 201 "Created")
+(def-http-return-code +http-accepted+ 202 "Accepted")
+(def-http-return-code +http-non-authoritative-information+ 203 "Non-Authoritative Information")
+(def-http-return-code +http-no-content+ 204 "No Content")
+(def-http-return-code +http-reset-content+ 205 "Reset Content")
+(def-http-return-code +http-partial-content+ 206 "Partial Content")
+(def-http-return-code +http-multi-status+ 207 "Multi-Status")
+(def-http-return-code +http-multiple-choices+ 300 "Multiple Choices")
+(def-http-return-code +http-moved-permanently+ 301 "Moved Permanently")
+(def-http-return-code +http-moved-temporarily+ 302 "Moved Temporarily")
+(def-http-return-code +http-see-other+ 303 "See Other")
+(def-http-return-code +http-not-modified+ 304 "Not Modified")
+(def-http-return-code +http-use-proxy+ 305 "Use Proxy")
+(def-http-return-code +http-temporary-redirect+ 307 "Temporary Redirect")
+(def-http-return-code +http-bad-request+ 400 "Bad Request")
+(def-http-return-code +http-authorization-required+ 401 "Authorization Required")
+(def-http-return-code +http-payment-required+ 402  "Payment Required")
+(def-http-return-code +http-forbidden+ 403 "Forbidden")
+(def-http-return-code +http-not-found+ 404 "Not Found")
+(def-http-return-code +http-method-not-allowed+ 405 "Method Not Allowed")
+(def-http-return-code +http-not-acceptable+ 406 "Not Acceptable")
+(def-http-return-code +http-proxy-authentication-required+ 407 "Proxy Authentication Required")
+(def-http-return-code +http-request-time-out+ 408 "Request Time-out")
+(def-http-return-code +http-conflict+ 409 "Conflict")
+(def-http-return-code +http-gone+ 410 "Gone")
+(def-http-return-code +http-length-required+ 411 "Length Required")
+(def-http-return-code +http-precondition-failed+ 412 "Precondition Failed")
+(def-http-return-code +http-request-entity-too-large+ 413 "Request Entity Too Large")
+(def-http-return-code +http-request-uri-too-large+ 414 "Request-URI Too Large")
+(def-http-return-code +http-unsupported-media-type+ 415 "Unsupported Media Type")
+(def-http-return-code +http-requested-range-not-satisfiable+ 416 "Requested range not satisfiable")
+(def-http-return-code +http-expectation-failed+ 417 "Expectation Failed")
+(def-http-return-code +http-failed-dependency+ 424 "Failed Dependency")
+(def-http-return-code +http-precondition-required+ 428 "Precondition Required")
+(def-http-return-code +http-too-many-requests+ 429 "Too Many Requests")
+(def-http-return-code +http-request-header-fields-too-large+ 431 "Request Header Fields Too Large")
+(def-http-return-code +http-internal-server-error+ 500 "Internal Server Error")
+(def-http-return-code +http-not-implemented+ 501 "Not Implemented")
+(def-http-return-code +http-bad-gateway+ 502 "Bad Gateway")
+(def-http-return-code +http-service-unavailable+ 503 "Service Unavailable")
+(def-http-return-code +http-gateway-time-out+ 504 "Gateway Time-out")
+(def-http-return-code +http-version-not-supported+ 505 "Version not supported")
+(def-http-return-code +http-network-authentication-required+ 511 "Network Authentication Required")
+
+(defconstant +day-names+
+  #("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun")
+  "The three-character names of the seven days of the week - needed
+for cookie date format.")
+
+(defconstant +month-names+
+  #("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")
+  "The three-character names of the twelve months - needed for cookie
+date format.")
+
+(defvar *rewrite-for-session-urls* t
+  "Whether HTML pages should possibly be rewritten for cookie-less
+session-management.")
+
+(defvar *content-types-for-url-rewrite*
+  '("text/html" "application/xhtml+xml")
+  "The content types for which url-rewriting is OK. See
+*REWRITE-FOR-SESSION-URLS*.")
+
+(defvar *the-random-state* (make-random-state t)
+  "A fresh random state.")
+
+(defvar-unbound *session-secret*
+  "A random ASCII string that's used to encode the public session
+data.  This variable is initially unbound and will be set \(using
+RESET-SESSION-SECRET) the first time a session is created, if
+necessary.  You can prevent this from happening if you set the value
+yourself before starting acceptors.")
+
+(defvar-unbound *hunchentoot-stream*
+  "The stream representing the socket Hunchentoot is listening on.")
+
+(defvar-unbound *finish-processing-socket*
+  "Will be set to T if PROCESS-CONNECTION is to stop processing more
+  requests on the current socket connection.")
+
+(defvar-unbound *close-hunchentoot-stream*
+  "This variable is set to NIL during the processing of a handler to
+tell the acceptor not to close the connection after it is done.")
+
+(defvar *headers-sent* nil
+  "Used internally to check whether the reply headers have
+already been sent for this request.")
+
+(defvar *file-upload-hook* nil
+  "If this is not NIL, it should be a unary function which will
+be called with a pathname for each file which is uploaded to
+Hunchentoot.  The pathname denotes the temporary file to which
+the uploaded file is written.  The hook is called directly before
+the file is created.")
+
+(defvar *session-db* nil
+  "The default \(global) session database.")
+
+(defvar *session-max-time* #.(* 30 60)
+  "The default time \(in seconds) after which a session times out.")
+
+(defvar *session-gc-frequency* 50
+  "A session GC \(see function SESSION-GC) will happen every
+*SESSION-GC-FREQUENCY* requests \(counting only requests which create
+a new session) if this variable is not NIL.  See SESSION-CREATED.")
+
+(defvar *use-user-agent-for-sessions* t
+  "Whether the 'User-Agent' header should be encoded into the session
+string.  If this value is true, a session will cease to be accessible
+if the client sends a different 'User-Agent' header.")
+
+(defvar *use-remote-addr-for-sessions* nil
+  "Whether the client's remote IP \(as returned by REAL-REMOTE-ADDR)
+should be encoded into the session string.  If this value is true, a
+session will cease to be accessible if the client's remote IP changes.
+
+This might for example be an issue if the client uses a proxy server
+which doesn't send correct 'X_FORWARDED_FOR' headers.")
+
+(defvar *default-content-type* "text/html"
+  "The default content-type header which is returned to the client.
+If this is text content type, the character set used for encoding the
+response will automatically be added to the content type in a
+``charset'' attribute.")
+
+(defvar *methods-for-post-parameters* '(:post)
+  "A list of the request method types \(as keywords) for which
+Hunchentoot will try to compute POST-PARAMETERS.")
+
+(defvar *header-stream* nil
+  "If this variable is not NIL, it should be bound to a stream to
+which incoming and outgoing headers will be written for debugging
+purposes.")
+
+(defvar *show-lisp-errors-p* nil
+  "Whether Lisp errors in request handlers should be shown in HTML output.")
+
+(defvar *show-lisp-backtraces-p* t
+  "Whether Lisp errors shown in HTML output should contain backtrace information.")
+
+(defvar *log-lisp-errors-p* t
+  "Whether Lisp errors in request handlers should be logged.")
+
+(defvar *log-lisp-backtraces-p* t
+  "Whether Lisp backtraces should be logged.  Only has an effect if
+*LOG-LISP-ERRORS-P* is true as well.")
+
+(defvar *log-lisp-warnings-p* t
+  "Whether Lisp warnings in request handlers should be logged.")
+
+(defvar *lisp-errors-log-level* :error
+  "Log level for Lisp errors.  Should be one of :ERROR \(the default),
+:WARNING, or :INFO.")
+
+(defvar *lisp-warnings-log-level* :warning
+  "Log level for Lisp warnings.  Should be one of :ERROR, :WARNING
+\(the default), or :INFO.")
+
+(defvar *message-log-lock* (make-lock "global-message-log-lock")
+  "A global lock to prevent concurrent access to the log file used by
+the ACCEPTOR-LOG-MESSAGE function.")
+
+(defvar *access-log-lock* (make-lock "global-access-log-lock")
+  "A global lock to prevent concurrent access to the log file used by
+the ACCEPTOR-LOG-ACCESS function.")
+
+(defvar *catch-errors-p* t
+  "Whether Hunchentoot should catch and log errors \(or rather invoke
+the debugger).")
+
+(defvar-unbound *acceptor*
+  "The current ACCEPTOR object while in the context of a request.")
+
+(defvar-unbound *request*
+  "The current REQUEST object while in the context of a request.")
+
+(defvar-unbound *reply*
+  "The current REPLY object while in the context of a request.")
+
+(defvar-unbound *session*
+  "The current session while in the context of a request, or NIL.")
+
+(defconstant +implementation-link+
+  #+:cmu "http://www.cons.org/cmucl/"
+  #+:sbcl "http://www.sbcl.org/"
+  #+:allegro "http://www.franz.com/products/allegrocl/"
+  #+:lispworks "http://www.lispworks.com/"
+  #+:openmcl "http://openmcl.clozure.com/"
+  "A link to the website of the underlying Lisp implementation.")
+
+(defvar *tmp-directory*
+  #+(or :win32 :mswindows) "c:\\hunchentoot-temp\\"
+  #-(or :win32 :mswindows) "/tmp/hunchentoot/"
+  "Directory for temporary files created by MAKE-TMP-FILE-NAME.")
+
+(defvar *tmp-files* nil
+  "A list of temporary files created while a request was handled.")
+
+(defconstant +latin-1+
+  (make-external-format :latin1 :eol-style :lf)
+  "A FLEXI-STREAMS external format used for `faithful' input and
+output of binary data.")
+
+(defconstant +utf-8+
+  (make-external-format :utf8 :eol-style :lf)
+  "A FLEXI-STREAMS external format used internally for logging and to
+encode cookie values.")
+
+(defvar *hunchentoot-default-external-format* +utf-8+
+  "The external format used to compute the REQUEST object.")
+
+(defconstant +buffer-length+ 8192
+  "Length of buffers used for internal purposes.")
+
+(defvar *default-connection-timeout* 20
+  "The default connection timeout used when an acceptor is reading
+from and writing to a socket stream.")
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (define-symbol-macro *supports-threads-p*
+   #+:lispworks t
+   #-:lispworks bt:*supports-threads-p*))
+
+(defvar *global-session-db-lock*
+  (load-time-value (and *supports-threads-p* (make-lock "global-session-db-lock")))
+  "A global lock to prevent two threads from modifying *session-db* at
+the same time \(or NIL for Lisps which don't have threads).")
+
+(pushnew :hunchentoot *features*)
+
+;; stuff for Nikodemus Siivola's HYPERDOC
+;; see <http://common-lisp.net/project/hyperdoc/>
+;; and <http://www.cliki.net/hyperdoc>
+
+(defvar *hyperdoc-base-uri* "http://weitz.de/hunchentoot/")
+
+(let ((exported-symbols-alist
+       (loop for symbol being the external-symbols of :hunchentoot
+             collect (cons symbol (concatenate 'string "#" (string-downcase symbol))))))
+  (defun hyperdoc-lookup (symbol type)
+    (declare (ignore type))
+    (cdr (assoc symbol exported-symbols-alist :test #'eq))))
+
+(defparameter hunchentoot:*hunchentoot-version* #.(asdf:component-version (asdf:find-system :hunchentoot)))
diff --git a/deps/hunchentoot/ssl.lisp b/deps/hunchentoot/ssl.lisp
new file mode 100644 (file)
index 0000000..58ecc2e
--- /dev/null
@@ -0,0 +1,119 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defclass ssl-acceptor (acceptor)
+  ((ssl-certificate-file :initarg :ssl-certificate-file
+                         :reader acceptor-ssl-certificate-file
+                         :documentation "A pathname designator for a
+certificate file in PEM format.")
+   (ssl-privatekey-file :initarg :ssl-privatekey-file
+                        :reader acceptor-ssl-privatekey-file
+                        :documentation "A pathname designator for a
+private key file in PEM format, or \(only on LispWorks) NIL if the
+certificate file contains the private key.")
+   (ssl-privatekey-password :initform nil
+                            :initarg :ssl-privatekey-password
+                            :reader acceptor-ssl-privatekey-password
+                            :documentation "The password for the
+private key file or NIL for no password."))
+  (:default-initargs
+   :port 443)
+  (:documentation "Create and START an instance of this class
+\(instead of ACCEPTOR) if you want an https server.  There are two
+required initargs, :SSL-CERTIFICATE-FILE and :SSL-PRIVATEKEY-FILE, for
+pathname designators denoting the certificate file and the key file in
+PEM format.  On LispWorks, you can have both in one file in which case
+the second initarg is optional.  You can also use the
+:SSL-PRIVATEKEY-PASSWORD initarg to provide a password \(as a string)
+for the key file \(or NIL, the default, for no password).
+
+The default port for SSL-ACCEPTOR instances is 443 instead of 80"))
+
+;; general implementation
+
+(defmethod acceptor-ssl-p ((acceptor ssl-acceptor))
+  t)
+
+(defmethod initialize-instance :after ((acceptor ssl-acceptor) &rest initargs)
+  (declare (ignore initargs))
+  ;; LispWorks can read both from the same file, so we can default one
+  #+:lispworks
+  (unless (slot-boundp acceptor 'ssl-privatekey-file)
+    (setf (slot-value acceptor 'ssl-privatekey-file)
+          (acceptor-ssl-certificate-file acceptor)))
+  ;; OpenSSL doesn't know much about Lisp pathnames...
+  (setf (slot-value acceptor 'ssl-privatekey-file)
+        (namestring (truename (acceptor-ssl-privatekey-file acceptor)))
+        (slot-value acceptor 'ssl-certificate-file)
+        (namestring (truename (acceptor-ssl-certificate-file acceptor)))))
+
+;; usocket implementation
+
+#-:lispworks
+(defmethod initialize-connection-stream ((acceptor ssl-acceptor) stream)
+  ;; attach SSL to the stream if necessary
+  (call-next-method acceptor
+                    (cl+ssl:make-ssl-server-stream stream
+                                                   :certificate (acceptor-ssl-certificate-file acceptor)
+                                                   :key (acceptor-ssl-privatekey-file acceptor)
+                                                   :password (acceptor-ssl-privatekey-password acceptor))))
+
+;; LispWorks implementation
+
+#+:lispworks
+(defun make-ssl-server-stream (socket-stream &key certificate-file privatekey-file privatekey-password)
+  "Given the acceptor socket stream SOCKET-STREAM attaches SSL to the
+stream using the certificate file CERTIFICATE-FILE and the private key
+file PRIVATEKEY-FILE.  Both of these values must be namestrings
+denoting the location of the files and will be fed directly to
+OpenSSL.  If PRIVATEKEY-PASSWORD is not NIL then it should be the
+password for the private key file \(if necessary).  Returns the
+stream."
+  (flet ((ctx-configure-callback (ctx)
+           (when privatekey-password
+             (comm:set-ssl-ctx-password-callback ctx :password privatekey-password))
+           (comm:ssl-ctx-use-certificate-file ctx
+                                              certificate-file
+                                              comm:ssl_filetype_pem)
+           (comm:ssl-ctx-use-privatekey-file ctx
+                                             privatekey-file
+                                             comm:ssl_filetype_pem)))
+    (comm:attach-ssl socket-stream
+                     :ctx-configure-callback #'ctx-configure-callback)
+    socket-stream))
+
+#+:lispworks
+(defmethod initialize-connection-stream ((acceptor ssl-acceptor) stream)
+  ;; attach SSL to the stream if necessary
+  (call-next-method acceptor
+                    (make-ssl-server-stream stream
+                                            :certificate-file (acceptor-ssl-certificate-file acceptor)
+                                            :privatekey-file (acceptor-ssl-privatekey-file acceptor)
+                                            :privatekey-password (acceptor-ssl-privatekey-password acceptor))))
diff --git a/deps/hunchentoot/taskmaster.lisp b/deps/hunchentoot/taskmaster.lisp
new file mode 100644 (file)
index 0000000..deffd10
--- /dev/null
@@ -0,0 +1,479 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+(defclass taskmaster ()
+  ((acceptor :accessor taskmaster-acceptor
+             :documentation "A backpointer to the acceptor instance
+this taskmaster works for."))
+  (:documentation "An instance of this class is responsible for
+distributing the work of handling requests for its acceptor.  This is
+an \"abstract\" class in the sense that usually only instances of
+subclasses of TASKMASTER will be used."))
+
+(defgeneric execute-acceptor (taskmaster)
+  (:documentation "This is a callback called by the acceptor once it
+has performed all initial processing to start listening for incoming
+connections \(see START-LISTENING).  It usually calls the
+ACCEPT-CONNECTIONS method of the acceptor, but depending on the
+taskmaster instance the method might be called from a new thread."))
+
+(defgeneric handle-incoming-connection (taskmaster socket)
+  (:documentation "This function is called by the acceptor to start
+processing of requests on a new incoming connection.  SOCKET is the
+usocket instance that represents the new connection \(or a socket
+handle on LispWorks).  The taskmaster starts processing requests on
+the incoming connection by calling the PROCESS-CONNECTION method of
+the acceptor instance.  The SOCKET argument is passed to
+PROCESS-CONNECTION as an argument."))
+
+(defgeneric shutdown (taskmaster)
+  (:documentation "Shuts down the taskmaster, i.e. frees all resources
+that were set up by it.  For example, a multi-threaded taskmaster
+might terminate all threads that are currently associated with it.
+This function is called by the acceptor's STOP method."))
+
+(defgeneric create-request-handler-thread (taskmaster socket)
+  (:documentation 
+   "Create a new thread in which to process the request.
+    This thread will call PROCESS-CONNECTION to process the request."))
+
+(defgeneric too-many-taskmaster-requests (taskmaster socket)
+  (:documentation 
+   "Signal a \"too many requests\" error, just prior to closing the connection."))
+
+(defgeneric taskmaster-max-thread-count (taskmaster)
+  (:documentation
+   "The maximum number of request threads this taskmaster will simultaneously
+    run before refusing or queueing new connections requests.  If the value
+    is null, then there is no limit.")
+  (:method ((taskmaster taskmaster))
+    "Default method -- no limit on the number of threads."
+    nil))
+
+(defgeneric taskmaster-max-accept-count (taskmaster)
+  (:documentation
+   "The maximum number of connections this taskmaster will accept before refusing
+    new connections.  If supplied, this must be greater than MAX-THREAD-COUNT.
+    The number of queued requests is the difference between MAX-ACCEPT-COUNT
+    and MAX-THREAD-COUNT.")
+  (:method ((taskmaster taskmaster))
+    "Default method -- no limit on the number of connections."
+    nil))
+
+(defgeneric taskmaster-thread-count (taskmaster)
+  (:documentation
+   "Returns the current number of taskmaster requests.")
+  (:method ((taskmaster taskmaster))
+    "Default method -- claim there is one connection thread."
+    1))
+
+(defgeneric increment-taskmaster-thread-count (taskmaster)
+  (:documentation
+   "Atomically increment the number of taskmaster requests.")
+  (:method  ((taskmaster taskmaster))
+    "Default method -- do nothing."
+    nil))
+
+(defgeneric decrement-taskmaster-thread-count (taskmaster)
+  (:documentation
+   "Atomically decrement the number of taskmaster requests")
+  (:method ((taskmaster taskmaster))
+    "Default method -- do nothing."
+    nil))
+
+(defgeneric start-thread (taskmaster thunk &key name)
+  (:documentation
+   "Start a name thread in which to call the THUNK, in the context of the given TASKMASTER.
+Keyword arguments provide TASKMASTER-dependent options.
+Return a thread object.
+
+Hunchentoot taskmaster methods will call it with the taskmaster as the context,
+allowing hunchentoot extensions to define specialized methods that may e.g.
+wrap the thunk within a proper set of bindings and condition handlers.")
+  (:method ((taskmaster t) thunk &key name)
+    #-lispworks
+    (bt:make-thread thunk :name name)
+    #+lispworks
+    (mp:process-run-function name nil thunk)))
+
+
+(defclass single-threaded-taskmaster (taskmaster)
+  ()
+  (:documentation "A taskmaster that runs synchronously in the thread
+where the START function was invoked \(or in the case of LispWorks in
+the thread started by COMM:START-UP-SERVER).  This is the simplest
+possible taskmaster implementation in that its methods do nothing but
+calling their acceptor \"sister\" methods - EXECUTE-ACCEPTOR calls
+ACCEPT-CONNECTIONS, HANDLE-INCOMING-CONNECTION calls
+PROCESS-CONNECTION."))
+
+(defmethod execute-acceptor ((taskmaster single-threaded-taskmaster))
+  ;; in a single-threaded environment we just call ACCEPT-CONNECTIONS
+  (accept-connections (taskmaster-acceptor taskmaster)))
+
+(defmethod handle-incoming-connection ((taskmaster single-threaded-taskmaster) socket)
+  ;; in a single-threaded environment we just call PROCESS-CONNECTION
+  (process-connection (taskmaster-acceptor taskmaster) socket))
+
+(defvar *default-max-thread-count* 100)
+(defvar *default-max-accept-count* (+ *default-max-thread-count* 20))
+
+
+(defclass multi-threaded-taskmaster (taskmaster)
+  ((acceptor-process
+    :accessor acceptor-process
+    :documentation
+    "A process that accepts incoming connections and hands them off to new processes
+     for request handling."))
+  (:documentation "An abstract class for taskmasters that use multiple threads.
+For a concrete class to instantiate, use one-thread-per-connection-taskmaster."))
+
+(defmethod execute-acceptor ((taskmaster multi-threaded-taskmaster))
+  (setf (acceptor-process taskmaster)
+        (start-thread
+         taskmaster
+         (lambda () (accept-connections (taskmaster-acceptor taskmaster)))
+         :name (format nil "hunchentoot-listener-~A:~A"
+                       (or (acceptor-address (taskmaster-acceptor taskmaster)) "*")
+                       (acceptor-port (taskmaster-acceptor taskmaster))))))
+
+
+;; You might think it would be nice to provide a taskmaster that takes
+;; threads out of a thread pool.  There are two things to consider:
+;;  - On a 2010-ish Linux box, thread creation takes less than 250 microseconds.
+;;  - Bordeaux Threads doesn't provide a way to "reset" and restart a thread,
+;;    and it's not clear how many Lisp implementations can do this.
+;; If you're still interested, use the quux-hunchentoot extension to hunchentoot.
+
+(defclass one-thread-per-connection-taskmaster (multi-threaded-taskmaster)
+  (;; Support for bounding the number of threads we'll create
+   (max-thread-count
+    :type (or integer null)
+    :initarg :max-thread-count
+    :initform nil
+    :accessor taskmaster-max-thread-count
+    :documentation 
+    "The maximum number of request threads this taskmaster will simultaneously
+     run before refusing or queueing new connections requests.  If the value
+     is null, then there is no limit.")
+   (thread-count
+    :type integer
+    :initform 0
+    :accessor taskmaster-thread-count
+    :documentation
+    "The number of taskmaster processing threads currently running.")
+   (thread-count-lock
+    :initform (make-lock "taskmaster-thread-count")
+    :reader taskmaster-thread-count-lock
+    :documentation
+    "In the absence of 'atomic-incf', we need this to atomically
+     increment and decrement the request count.")
+   (max-accept-count
+    :type (or integer null)
+    :initarg :max-accept-count
+    :initform nil
+    :accessor taskmaster-max-accept-count
+    :documentation
+    "The maximum number of connections this taskmaster will accept before refusing
+     new connections.  If supplied, this must be greater than MAX-THREAD-COUNT.
+     The number of queued requests is the difference between MAX-ACCEPT-COUNT
+     and MAX-THREAD-COUNT.")
+   (accept-count
+    :type integer
+    :initform 0
+    :accessor taskmaster-accept-count
+    :documentation
+    "The number of connection currently accepted by the taskmaster. These
+    connections are not ensured to be processed, thay may be waiting for an
+    empty processing slot or rejected because the load is too heavy.")
+   (accept-count-lock
+    :initform (make-lock "taskmaster-accept-count")
+    :reader taskmaster-accept-count-lock
+    :documentation
+    "In the absence of 'atomic-incf', we need this to atomically
+     increment and decrement the accept count.")
+   (wait-queue
+    :initform (make-condition-variable)
+    :reader taskmaster-wait-queue
+    :documentation
+    "A queue that we use to wait for a free connection.")
+   (wait-lock
+    :initform (make-lock "taskmaster-thread-lock")
+    :reader taskmaster-wait-lock
+    :documentation
+    "The lock for the connection wait queue.")
+   (worker-thread-name-format
+    :type (or string null)
+    :initarg :worker-thread-name-format
+    :initform "hunchentoot-worker-~A"
+    :accessor taskmaster-worker-thread-name-format))
+  (:default-initargs
+   :max-thread-count *default-max-thread-count*
+   :max-accept-count *default-max-accept-count*)
+  (:documentation "A taskmaster that starts one thread for listening
+to incoming requests and one new thread for each incoming connection.
+
+If MAX-THREAD-COUNT is null, a new thread will always be created for
+each request.
+
+If MAX-THREAD-COUNT is supplied, the number of request threads is
+limited to that.  Furthermore, if MAX-ACCEPT-COUNT is not supplied, an
+HTTP 503 will be sent if the thread limit is exceeded.  Otherwise, if
+MAX-ACCEPT-COUNT is supplied, it must be greater than MAX-THREAD-COUNT;
+in this case, requests are accepted up to MAX-ACCEPT-COUNT, and only
+then is HTTP 503 sent.
+
+It is important to note that MAX-ACCEPT-COUNT and the HTTP 503 behavior
+described above is racing with the acceptor listen backlog. If we are receiving
+requests faster than threads can be spawned and 503 sent, the requests will be
+silently rejected by the kernel.
+
+In a load-balanced environment with multiple Hunchentoot servers, it's
+reasonable to provide MAX-THREAD-COUNT but leave MAX-ACCEPT-COUNT null.
+This will immediately result in HTTP 503 when one server is out of
+resources, so the load balancer can try to find another server.
+
+In an environment with a single Hunchentoot server, it's reasonable
+to provide both MAX-THREAD-COUNT and a somewhat larger value for
+MAX-ACCEPT-COUNT.  This will cause a server that's almost out of
+resources to wait a bit; if the server is completely out of resources,
+then the reply will be HTTP 503.
+
+This is the default taskmaster implementation for multi-threaded Lisp
+implementations."))
+
+(defmethod initialize-instance :after ((taskmaster one-thread-per-connection-taskmaster) &rest init-args)
+  "Ensure the if MAX-ACCEPT-COUNT is supplied, that it is greater than MAX-THREAD-COUNT."
+  (declare (ignore init-args))
+  (when (taskmaster-max-accept-count taskmaster)
+    (unless (taskmaster-max-thread-count taskmaster)
+      (parameter-error "MAX-THREAD-COUNT must be supplied if MAX-ACCEPT-COUNT is supplied"))
+    (unless (> (taskmaster-max-accept-count taskmaster) (taskmaster-max-thread-count taskmaster))
+      (parameter-error "MAX-ACCEPT-COUNT must be greater than MAX-THREAD-COUNT"))))
+
+(defmethod increment-taskmaster-accept-count ((taskmaster one-thread-per-connection-taskmaster))
+  (when (taskmaster-max-accept-count taskmaster)
+    (with-lock-held ((taskmaster-accept-count-lock taskmaster))
+      (incf (taskmaster-accept-count taskmaster)))))
+
+(defmethod decrement-taskmaster-accept-count ((taskmaster one-thread-per-connection-taskmaster))
+  (when (taskmaster-max-accept-count taskmaster)
+    (with-lock-held ((taskmaster-accept-count-lock taskmaster))
+      (decf (taskmaster-accept-count taskmaster)))))
+
+(defmethod increment-taskmaster-thread-count ((taskmaster one-thread-per-connection-taskmaster))
+  (when (taskmaster-max-thread-count taskmaster)
+    (with-lock-held ((taskmaster-thread-count-lock taskmaster))
+      (incf (taskmaster-thread-count taskmaster)))))
+
+(defmethod decrement-taskmaster-thread-count ((taskmaster one-thread-per-connection-taskmaster))
+  (when (taskmaster-max-thread-count taskmaster)
+    (prog1
+        (with-lock-held ((taskmaster-thread-count-lock taskmaster))
+          (decf (taskmaster-thread-count taskmaster))
+          (decrement-taskmaster-accept-count taskmaster))
+      (when (and (taskmaster-max-accept-count taskmaster)
+                 (< (taskmaster-thread-count taskmaster) (taskmaster-max-accept-count taskmaster)))
+        (note-free-connection taskmaster)))))
+
+(defmethod note-free-connection ((taskmaster one-thread-per-connection-taskmaster))
+  "Note that a connection has been freed up"
+  (with-lock-held ((taskmaster-wait-lock taskmaster))
+    (condition-variable-signal (taskmaster-wait-queue taskmaster))))
+
+(defmethod wait-for-free-connection ((taskmaster one-thread-per-connection-taskmaster))
+  "Wait for a connection to be freed up"
+  (with-lock-held ((taskmaster-wait-lock taskmaster))
+    (loop until (< (taskmaster-thread-count taskmaster) (taskmaster-max-thread-count taskmaster))
+          do (condition-variable-wait (taskmaster-wait-queue taskmaster) (taskmaster-wait-lock taskmaster)))))
+
+(defmethod too-many-taskmaster-requests ((taskmaster one-thread-per-connection-taskmaster) socket)
+  (declare (ignore socket))
+  (acceptor-log-message (taskmaster-acceptor taskmaster)
+                        :warning "Can't handle a new request, too many request threads already"))
+
+(defmethod create-request-handler-thread ((taskmaster one-thread-per-connection-taskmaster) socket)
+  "Create a thread for handling a single request"
+  ;; we are handling all conditions here as we want to make sure that
+  ;; the acceptor process never crashes while trying to create a
+  ;; worker thread; one such problem exists in
+  ;; GET-PEER-ADDRESS-AND-PORT which can signal socket conditions on
+  ;; some platforms in certain situations.
+  (handler-case*
+   (start-thread
+    taskmaster
+    (lambda () (handle-incoming-connection% taskmaster socket))
+    :name (format nil (taskmaster-worker-thread-name-format taskmaster) (client-as-string socket)))
+   (error (cond)
+          ;; need to bind *ACCEPTOR* so that LOG-MESSAGE* can do its work.
+          (let ((*acceptor* (taskmaster-acceptor taskmaster)))
+            (ignore-errors
+              (close (make-socket-stream socket *acceptor*) :abort t))
+            (log-message* *lisp-errors-log-level*
+                         "Error while creating worker thread for new incoming connection: ~A" cond)))))
+
+;;; usocket implementation
+
+#-:lispworks
+(defmethod shutdown ((taskmaster taskmaster))
+  taskmaster)
+
+#-:lispworks
+(defmethod shutdown ((taskmaster one-thread-per-connection-taskmaster))
+  ;; just wait until the acceptor process has finished, then return
+  (bt:join-thread (acceptor-process taskmaster))
+  taskmaster)
+
+#-:lispworks
+(defmethod handle-incoming-connection ((taskmaster one-thread-per-connection-taskmaster) socket)
+  (create-request-handler-thread taskmaster socket))
+
+#-lispworks
+(defmethod handle-incoming-connection% ((taskmaster one-thread-per-connection-taskmaster) socket)
+  ;; Here's the idea, with the stipulations given in ONE-THREAD-PER-CONNECTION-TASKMASTER
+  ;;  - If MAX-THREAD-COUNT is null, just start a taskmaster
+  ;;  - If the connection count will exceed MAX-ACCEPT-COUNT or if MAX-ACCEPT-COUNT
+  ;;    is null and the connection count will exceed MAX-THREAD-COUNT,
+  ;;    return an HTTP 503 error to the client
+  ;;  - Otherwise if we're between MAX-THREAD-COUNT and MAX-ACCEPT-COUNT,
+  ;;    wait until the connection count drops, then handle the request
+  ;;  - Otherwise, increment THREAD-COUNT and start a taskmaster
+  (increment-taskmaster-accept-count taskmaster)
+  (flet ((process-connection% (acceptor socket)
+           (increment-taskmaster-thread-count taskmaster)
+           (unwind-protect
+                (process-connection acceptor socket)
+             (decrement-taskmaster-thread-count taskmaster))))
+    (cond ((null (taskmaster-max-thread-count taskmaster))
+           ;; No limit on number of requests, just start a taskmaster
+           (process-connection (taskmaster-acceptor taskmaster) socket))
+          ((if (taskmaster-max-accept-count taskmaster)
+               (>= (taskmaster-accept-count taskmaster) (taskmaster-max-accept-count taskmaster))
+               (>= (taskmaster-thread-count taskmaster) (taskmaster-max-thread-count taskmaster)))
+           ;; Send HTTP 503 to indicate that we can't handle the request right now
+           (too-many-taskmaster-requests taskmaster socket)
+           (send-service-unavailable-reply taskmaster socket))
+          ((and (taskmaster-max-accept-count taskmaster)
+                (>= (taskmaster-thread-count taskmaster) (taskmaster-max-thread-count taskmaster)))
+           ;; Wait for a request to finish, then carry on
+           (wait-for-free-connection taskmaster)
+           (process-connection% (taskmaster-acceptor taskmaster) socket))
+          (t
+           ;; We're within both limits, just start a taskmaster
+           (process-connection% (taskmaster-acceptor taskmaster) socket)))))
+
+(defun send-service-unavailable-reply (taskmaster socket)
+  "A helper function to send out a quick error reply, before any state
+is set up via PROCESS-REQUEST."
+  (let* ((acceptor (taskmaster-acceptor taskmaster))
+         (*acceptor* acceptor)
+         (*hunchentoot-stream* (make-socket-stream socket acceptor)))
+    (unwind-protect
+         (with-conditions-caught-and-logged ()
+           (with-mapped-conditions ()
+             (let* ((*hunchentoot-stream* (initialize-connection-stream acceptor *hunchentoot-stream*))
+                    (*reply* (make-instance (acceptor-reply-class acceptor)))
+                    (*request* (acceptor-make-request acceptor socket)))
+               (with-character-stream-semantics
+                 (send-response acceptor
+                                (flex:make-flexi-stream *hunchentoot-stream* :external-format :iso-8859-1)
+                                +http-service-unavailable+
+                                :content (acceptor-status-message acceptor +http-service-unavailable+))))))
+      (decrement-taskmaster-accept-count taskmaster)
+      (when *hunchentoot-stream*
+        (ignore-errors*
+          (finish-output *hunchentoot-stream*))
+        (ignore-errors*
+          (close *hunchentoot-stream* :abort t))))))
+
+(defun client-as-string (socket)
+  "A helper function which returns the client's address and port as a
+   string and tries to act robustly in the presence of network problems."
+  #-:lispworks
+  (let ((address (usocket:get-peer-address socket))
+        (port (usocket:get-peer-port socket)))
+    (when (and address port)
+      (format nil "~A:~A"
+              (usocket:vector-quad-to-dotted-quad address)
+              port)))
+  #+:lispworks
+  (multiple-value-bind (address port)
+      (comm:get-socket-peer-address socket)
+    (when (and address port)
+      (format nil "~A:~A"
+              (comm:ip-address-string address)
+              port))))
+
+;; LispWorks implementation
+
+#+:lispworks
+(defmethod shutdown ((taskmaster taskmaster))
+  (when-let (process (acceptor-process (taskmaster-acceptor taskmaster)))
+    ;; kill the main acceptor process, see LW documentation for
+    ;; COMM:START-UP-SERVER
+    (mp:process-kill process))
+  taskmaster)
+
+#+:lispworks
+(defmethod handle-incoming-connection ((taskmaster one-thread-per-connection-taskmaster) socket)
+  (incf *worker-counter*)
+  ;; check if we need to perform a global GC
+  (when (and *cleanup-interval*
+             (zerop (mod *worker-counter* *cleanup-interval*)))
+    (when *cleanup-function*
+      (funcall *cleanup-function*)))
+  (create-request-handler-thread taskmaster socket))
+
+#+:lispworks
+(defmethod handle-incoming-connection% ((taskmaster one-thread-per-connection-taskmaster) socket)
+  (increment-taskmaster-accept-count taskmaster)
+  (flet ((process-connection% (acceptor socket)
+           (increment-taskmaster-thread-count taskmaster)
+           (unwind-protect
+                (process-connection acceptor socket)
+             (decrement-taskmaster-thread-count taskmaster))))
+    (cond ((null (taskmaster-max-thread-count taskmaster))
+           ;; No limit on number of requests, just start a taskmaster
+           (process-connection (taskmaster-acceptor taskmaster) socket))
+          ((if (taskmaster-max-accept-count taskmaster)
+               (>= (taskmaster-accept-count taskmaster) (taskmaster-max-accept-count taskmaster))
+               (>= (taskmaster-thread-count taskmaster) (taskmaster-max-thread-count taskmaster)))
+           ;; Send HTTP 503 to indicate that we can't handle the request right now
+           (too-many-taskmaster-requests taskmaster socket)
+           (send-service-unavailable-reply taskmaster socket))
+          ((and (taskmaster-max-accept-count taskmaster)
+                (>= (taskmaster-thread-count taskmaster) (taskmaster-max-thread-count taskmaster)))
+           ;; Lispworks doesn't have condition variables, so punt
+           (too-many-taskmaster-requests taskmaster socket)
+           (send-service-unavailable-reply taskmaster socket))
+          (t
+           ;; We're within both limits, just start a taskmaster
+           (process-connection% (taskmaster-acceptor taskmaster) socket)))))
+
diff --git a/deps/hunchentoot/test/UTF-8-demo.html b/deps/hunchentoot/test/UTF-8-demo.html
new file mode 100644 (file)
index 0000000..b8157db
--- /dev/null
@@ -0,0 +1,213 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head><title>UTF-8 test file</title></head>
+    <body>
+    <p>Original by Markus Kuhn, adapted for HTML by Martin D&uuml;rst.</p>
+<pre>
+UTF-8 encoded sample plain-text file
+‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+Markus Kuhn [ˈmaʳkʊs kuːn] &lt;mkuhn@acm.org> — 1999-08-20
+
+
+The ASCII compatible UTF-8 encoding of ISO 10646 and Unicode
+plain-text files is defined in RFC 2279 and in ISO 10646-1 Annex R.
+
+
+Using Unicode/UTF-8, you can write in emails and source code things such as
+
+Mathematics and Sciences:
+
+  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i), ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),
+
+  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⊥ &lt; a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (A ⇔ B),
+
+  2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm
+
+Linguistics and dictionaries:
+
+  ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
+  Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]
+
+APL:
+
+  ((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
+
+Nicer typography in plain text files:
+
+  ╔══════════════════════════════════════════╗
+  ║                                          ║
+  ║   • ‘single’ and “double” quotes         ║
+  ║                                          ║
+  ║   • Curly apostrophes: “We’ve been here” ║
+  ║                                          ║
+  ║   • Latin-1 apostrophe and accents: '´`  ║
+  ║                                          ║
+  ║   • ‚deutsche‘ „Anführungszeichen“       ║
+  ║                                          ║
+  ║   • †, ‡, ‰, •, 3–4, —, −5/+5, ™, …      ║
+  ║                                          ║
+  ║   • ASCII safety test: 1lI|, 0OD, 8B     ║
+  ║                      ╭─────────╮         ║
+  ║   • the euro symbol: │ 14.95 € │         ║
+  ║                      ╰─────────╯         ║
+  ╚══════════════════════════════════════════╝
+
+Greek (in Polytonic):
+
+  The Greek anthem:
+
+  Σὲ γνωρίζω ἀπὸ τὴν κόψη
+  τοῦ σπαθιοῦ τὴν τρομερή,
+  σὲ γνωρίζω ἀπὸ τὴν ὄψη
+  ποὺ μὲ βία μετράει τὴ γῆ.
+
+  ᾿Απ᾿ τὰ κόκκαλα βγαλμένη
+  τῶν ῾Ελλήνων τὰ ἱερά
+  καὶ σὰν πρῶτα ἀνδρειωμένη
+  χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!
+
+  From a speech of Demosthenes in the 4th century BC:
+
+  Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
+  ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
+  λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
+  τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ 
+  εἰς τοῦτο προήκοντα,  ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
+  πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
+  οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
+  οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
+  ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
+  τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
+  γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
+  προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
+  σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
+  τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
+  τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
+  τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.
+
+  Δημοσθένους, Γ´ ᾿Ολυνθιακὸς
+
+Georgian:
+
+  From a Unicode conference invitation:
+
+  გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
+  კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
+  ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
+  ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
+  ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
+  ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
+  ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
+
+Russian:
+
+  From a Unicode conference invitation:
+
+  Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
+  Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
+  Конференция соберет широкий круг экспертов по  вопросам глобального
+  Интернета и Unicode, локализации и интернационализации, воплощению и
+  применению Unicode в различных операционных системах и программных
+  приложениях, шрифтах, верстке и многоязычных компьютерных системах.
+
+Thai (UCS Level 2):
+
+  Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
+  classic 'San Gua'):
+
+  [----------------------------|------------------------]
+    ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่
+  สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา
+    ทรงนับถือขันทีเป็นที่พึ่ง           บ้านเมืองจึงวิปริตเป็นนักหนา
+  โฮจิ๋นเรียกทัพทั่วหัวเมืองมา         หมายจะฆ่ามดชั่วตัวสำคัญ
+    เหมือนขับไสไล่เสือจากเคหา      รับหมาป่าเข้ามาเลยอาสัญ
+  ฝ่ายอ้องอุ้นยุแยกให้แตกกัน          ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
+    พลันลิฉุยกุยกีกลับก่อเหตุ          ช่างอาเพศจริงหนาฟ้าร้องไห้
+  ต้องรบราฆ่าฟันจนบรรลัย           ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ
+
+  (The above is a two-column text. If combining characters are handled
+  correctly, the lines of the second column should be aligned with the
+  | character above.)
+
+Ethiopian:
+
+  Proverbs in the Amharic language:
+
+  ሰማይ አይታረስ ንጉሥ አይከሰስ።
+  ብላ ካለኝ እንደአባቴ በቆመጠኝ።
+  ጌጥ ያለቤቱ ቁምጥና ነው።
+  ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
+  የአፍ ወለምታ በቅቤ አይታሽም።
+  አይጥ በበላ ዳዋ ተመታ።
+  ሲተረጉሙ ይደረግሙ።
+  ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
+  ድር ቢያብር አንበሳ ያስር።
+  ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
+  እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
+  የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
+  ሥራ ከመፍታት ልጄን ላፋታት።
+  ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
+  የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
+  ተንጋሎ ቢተፉ ተመልሶ ባፉ።
+  ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
+  እግርህን በፍራሽህ ልክ ዘርጋ።
+
+Runes:
+
+  ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ
+
+  (Old English, which transcribed into Latin reads 'He cwaeth that he
+  bude thaem lande northweardum with tha Westsae.' and means 'He said
+  that he lived in the northern land near the Western Sea.')
+
+Braille:
+
+  ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌
+
+  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
+  ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
+  ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
+  ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
+  ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ 
+  ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲
+
+  ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
+
+  ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
+  ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
+  ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
+  ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ 
+  ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ 
+  ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
+  ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
+  ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
+  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
+
+  (The first couple of paragraphs of "A Christmas Carol" by Dickens)
+
+Compact font selection example text:
+
+  ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
+  abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
+  –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
+  ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა
+
+Greetings in various languages:
+
+  Hello world, Καλημέρα κόσμε, コンニチハ
+
+Box drawing alignment tests:                                          █
+                                                                      ▉
+  ╔══╦══╗  ┌──┬──┐  ╭──┬──╮  ╭──┬──╮  ┏━━┳━━┓  ┎┒┏┑   ╷  ╻ ┏┯┓ ┌┰┐    ▊ ╱╲╱╲╳╳╳
+  ║┌─╨─┐║  │╔═╧═╗│  │╒═╪═╕│  │╓─╁─╖│  ┃┌─╂─┐┃  ┗╃╄┙  ╶┼╴╺╋╸┠┼┨ ┝╋┥    ▋ ╲╱╲╱╳╳╳
+  ║│╲ ╱│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╿ │┃  ┍╅╆┓   ╵  ╹ ┗┷┛ └┸┘    ▌ ╱╲╱╲╳╳╳
+  ╠╡ ╳ ╞╣  ├╢   ╟┤  ├┼─┼─┼┤  ├╫─╂─╫┤  ┣┿╾┼╼┿┫  ┕┛┖┚     ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
+  ║│╱ ╲│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╽ │┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▎
+  ║└─╥─┘║  │╚═╤═╝│  │╘═╪═╛│  │╙─╀─╜│  ┃└─╂─┘┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▏
+  ╚══╩══╝  └──┴──┘  ╰──┴──╯  ╰──┴──╯  ┗━━┻━━┛           └╌╌┘ ╎ ┗╍╍┛ ┋  ▁▂▃▄▅▆▇█
+
+</pre>
+</body>
+</html>
diff --git a/deps/hunchentoot/test/favicon.ico b/deps/hunchentoot/test/favicon.ico
new file mode 100755 (executable)
index 0000000..30c7eaa
Binary files /dev/null and b/deps/hunchentoot/test/favicon.ico differ
diff --git a/deps/hunchentoot/test/fz.jpg b/deps/hunchentoot/test/fz.jpg
new file mode 100644 (file)
index 0000000..6ebe67b
Binary files /dev/null and b/deps/hunchentoot/test/fz.jpg differ
diff --git a/deps/hunchentoot/test/packages.lisp b/deps/hunchentoot/test/packages.lisp
new file mode 100755 (executable)
index 0000000..3f0f509
--- /dev/null
@@ -0,0 +1,37 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(defpackage #:hunchentoot-test
+  (:nicknames #:tbnl-test)
+  (:use :cl :cl-who :hunchentoot)
+  (:export #:test-hunchentoot))
+  
+(defpackage #:hunchentoot-test-user
+  (:use :cl :hunchentoot))
\ No newline at end of file
diff --git a/deps/hunchentoot/test/script-engine.lisp b/deps/hunchentoot/test/script-engine.lisp
new file mode 100644 (file)
index 0000000..4db1e00
--- /dev/null
@@ -0,0 +1,180 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot-test)
+
+(defun file-contents (pathname &key (element-type '(unsigned-byte 8)))
+  (with-open-file (s pathname :element-type element-type)
+    (let ((result (make-array (file-length s) :element-type element-type)))
+      (read-sequence result s)
+      result)))  
+
+(defclass script-context ()
+  ((base-url :initarg :base-url
+             :reader script-context-base-url
+             :documentation "Base URL to be used for all HTTP requests in this script context")))
+
+(defmethod initialize-instance :before ((script-context script-context) &key context-class-name)
+  ;; just ignore the context-class-name so that we can use &args in the WITH-SCRIPT-CONTEXT macro below.
+  (declare (ignore context-class-name)))
+
+(defvar *script-context* nil
+  "Current script context")
+
+(defmacro with-script-context ((&rest args &key (context-class-name 'script-context) &allow-other-keys)
+                               &body body)
+  `(let ((*script-context* (make-instance ',context-class-name ,@args))
+         (*default-pathname-defaults* *this-file*)
+         failed)
+     (handler-bind
+        ((assertion-failed (lambda (condition)
+                             (push condition failed)
+                             (format t "Assertion failed:~%~A~%" condition))))
+       (prog1
+           (progn ,@body
+                  (values))
+         (if failed
+             (format t ";; ~A assertion~:P FAILED~%" (length failed))
+             (format t ";; all tests PASSED~%"))))))
+
+(defclass http-reply ()
+  ((body :initarg :body)
+   (status-code :initarg :status-code)
+   (headers :initarg :headers)
+   (uri :initarg :uri)
+   (stream :initarg :stream)
+   (close :initarg :close)
+   (reason-phrase :initarg :reason-phrase)))
+
+(defvar *last-reply* nil
+  "Contains the last HTTP reply received")
+
+(define-condition assertion-failed (simple-condition)
+  ((assertion :initarg :assertion
+              :accessor condition-assertion
+              :initform nil)
+   (reply-slot-name :initarg :reply-slot-name
+                    :reader condition-reply-slot-name)
+   (reply-value :initarg :reply-value
+                :reader condition-reply-value)
+   (operator :initarg :operator
+             :reader condition-operator)
+   (args :initarg :args
+         :reader condition-args)
+   (reply :initarg :reply
+          :reader condition-reply))
+  (:report print-assertion))
+
+(defun print-assertion (condition stream)
+  (format stream " (~A "
+          (condition-operator condition))
+  (loop
+     for rest on (cons (condition-reply-value condition)
+                       (condition-args condition))
+     for value = (car rest)
+     for more-p = (cdr rest)
+     do (if (and (arrayp value) (not (stringp value)))
+            (format stream "<array>")
+            (format stream "~S" value))
+     when more-p
+       do (princ #\Space stream))
+  (format stream ")~%"))
+
+(defun function-designator-p (thing)
+  "Return true value if THING is a function or a symbol that has a function definition."
+  (or (functionp thing)
+      (and (symbolp thing)
+           (fboundp thing))))
+
+(defmacro with-operator-defaulting ((default-operator) &body body)
+  "If OPERATOR is not a function designator, prepend it to ARGS and
+bind OPERATOR to DEFAULT-OPERATOR.  OPERATOR and ARGS are captured
+from the expansion environment."
+  `(if (function-designator-p operator)
+       (progn ,@body)
+       (let ((operator ',default-operator)
+             (args (cons operator args)))
+         ,@body)))
+
+(defun http-assert (reply-slot-name operator &rest args)
+  (let ((reply-value (slot-value *last-reply* reply-slot-name)))
+    (with-operator-defaulting (equal)
+      (unless (apply operator reply-value args)
+        (signal 'assertion-failed
+                :reply-slot-name reply-slot-name
+                :reply-value reply-value
+                :operator operator
+                :args args
+                :reply *last-reply*)))))
+
+(define-condition header-assertion-failed (assertion-failed)
+  ((header-name :initarg :header-name :reader condition-header-name)))
+
+(defun http-assert-header (header-name operator &rest args)
+  (let ((header-value (cdr (assoc header-name (slot-value *last-reply* 'headers) :test #'string-equal))))
+    (with-operator-defaulting (matches)
+      (unless (apply operator header-value args)
+        (signal 'header-assertion-failed
+                :reply-slot-name 'headers
+                :header-name header-name
+                :reply-value header-value
+                :operator operator
+                :args args
+                :reply *last-reply*)))))
+
+(defun http-assert-body (regex)
+  (http-assert 'body 'matches regex))
+
+(defun matches (string regex)
+  (cl-ppcre:scan regex string))
+
+(defun integer-equal (string integer)
+  (eql (parse-integer string) integer))
+
+(defun http-request (url
+                     &rest args
+                     &key (protocol :http/1.1)
+                          (method :get)
+                          content
+                          content-type
+                          content-length
+                          range
+                          cookie-jar
+                          basic-authorization
+                          parameters
+                          external-format-out
+                          additional-headers)
+  (declare (ignore protocol method content content-type content-length cookie-jar basic-authorization
+                   range parameters external-format-out additional-headers))
+  (setf *last-reply* (make-instance 'http-reply))
+  (with-slots (body status-code headers uri stream close) *last-reply*
+    (setf (values body status-code headers uri stream close)
+          (apply 'drakma:http-request
+                 (format nil "~A~A" (script-context-base-url *script-context*) url)
+                 args)))
+  (values))
diff --git a/deps/hunchentoot/test/script.lisp b/deps/hunchentoot/test/script.lisp
new file mode 100644 (file)
index 0000000..6c87679
--- /dev/null
@@ -0,0 +1,194 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot-test)
+
+(defun file-length-string (pathname)
+  (with-open-file (f pathname)
+    (princ-to-string (file-length f))))
+
+(defun say (fmt &rest args)
+  (format t "; ")
+  (apply #'format t fmt args)
+  (terpri))
+
+(defun test-hunchentoot (base-url &key (make-cookie-jar
+                                        (lambda ()
+                                          (make-instance 'drakma:cookie-jar))))
+  "Runs the built-in confidence test.  BASE-URL is the base URL to use
+for testing, it should not have a trailing slash.  The keyword
+arguments accepted are for future extension and should not currently
+be used.
+
+The script expects the Hunchentoot example test server to be running
+at the given BASE-URL and retrieves various pages from that server,
+expecting certain responses."
+  (with-script-context (:base-url (format nil "~A/hunchentoot/test/" base-url))
+
+    (say "Request home page")
+    (http-request "")
+    (http-assert 'status-code 200)
+    (http-assert-header :content-type "^text/html")
+
+    (say "Test cookies")
+    (let ((cookie-jar (funcall make-cookie-jar)))
+      (http-request "cookie.html" :cookie-jar cookie-jar)
+      (http-request "cookie.html" :cookie-jar cookie-jar)
+      (http-assert-body "(?msi)COOKIE-IN &quot;pumpkin&quot;.*&quot;barking&quot;"))
+
+    (say "Test session variables")
+    (let ((cookie-jar (funcall make-cookie-jar)))
+      (http-request "session.html" :cookie-jar cookie-jar
+                    :method :post :parameters '(("new-foo-value" . "ABC") ("new-bar-value" . "DEF")))
+      (http-request "session.html" :cookie-jar cookie-jar)
+      ;; These assertions assume that SESSION-VALUE returns the found alist value as second value
+      (http-assert-body "(?i)\(HUNCHENTOOT-TEST::FOO . &quot;ABC&quot;\)")
+      (http-assert-body "(?i)\(HUNCHENTOOT-TEST::BAR . &quot;DEF&quot;\)"))
+
+    (say "Test malformed session cookie validation")
+    (dolist (session-id '("" "invalid-session-id" ":invalid-session-id" "invalid:session-id"))
+      (http-request "session.html"
+                    :additional-headers (acons "Cookie" (format nil "hunchentoot-session=~A" session-id) nil))
+      (http-assert 'status-code 200)
+      ;; session is empty
+      (http-assert-body "(?i)\(HUNCHENTOOT-TEST::FOO\)"))
+
+    (say "Test GET parameters with foreign characters (Latin-1)")
+    (http-request "parameter_latin1_get.html"
+                  :external-format-out :iso-8859-1
+                  :parameters (list (cons "foo" (format nil "H~Chner" #.(code-char 252))))
+                  :additional-headers '(("Content-Type" . "text/plain; charset=iso-8859-1")))
+    (http-assert-header :content-type "(?i)text/html; charset=ISO-8859-1")
+    (http-assert-body "(72 252 104 110 101 114)")
+    (http-assert-body "(?i)&quot;H&#xFC;hner&quot;")
+
+    (say "Test POST parameters with foreign characters (Latin-1)")
+    (http-request "parameter_latin1_post.html"
+                  :external-format-out :iso-8859-1
+                  :method :post :parameters (list (cons "foo" (format nil "H~Chner" #.(code-char 252)))))
+    (http-assert-header :content-type "(?i)text/html; charset=ISO-8859-1")
+    (http-assert-body "(72 252 104 110 101 114)")
+    (http-assert-body "(?i)&quot;H&#xFC;hner&quot;")
+
+    (say "Test GET parameters with foreign characters (UTF-8)")
+    (http-request "parameter_utf8_get.html"
+                  :external-format-out :utf-8
+                  :parameters (list (cons "foo" (format nil "H~Chner" #.(code-char 252)))))
+    (http-assert-header :content-type "(?i)text/html; charset=UTF-8")
+    (http-assert-body "(72 252 104 110 101 114)")
+    (http-assert-body "(?i)&quot;H&#xFC;hner&quot;")
+
+    (say "Test POST parameters with foreign characters (UTF-8)")
+    (http-request "parameter_utf8_post.html"
+                  :method :post
+                  :external-format-out :utf-8
+                  :parameters (list (cons "foo" (format nil "H~Chner" #.(code-char 252)))))
+    (http-assert-header :content-type "(?i)text/html; charset=UTF-8")
+    (http-assert-body "(72 252 104 110 101 114)")
+    (http-assert-body "(?i)&quot;H&#xFC;hner&quot;")
+
+    (say "Test redirection")
+    (http-request "redir.html")
+    (http-assert 'uri (lambda (uri)
+                        (matches (princ-to-string uri) "info.html\\?redirected=1")))
+
+    (say "Test authorization")
+    (http-request "authorization.html")
+    (http-assert 'status-code 401)
+    (http-request "authorization.html"
+                  :basic-authorization '("nanook" "igloo"))
+    (http-assert 'status-code 200)
+
+    (say "Request the Zappa image")
+    (http-request "image.jpg")
+    (http-assert-header :content-length (file-length-string #P"fz.jpg"))
+    (http-assert-header :content-type "image/jpeg")
+    (http-assert 'body (complement #'mismatch) (file-contents #P"fz.jpg"))
+
+    (say "Request the Zappa image from RAM")
+    (http-request "image-ram.jpg")
+    (http-assert-header :content-length (file-length-string #P"fz.jpg"))
+    (http-assert-header :content-type "image/jpeg")
+    (http-assert 'body (complement #'mismatch) (file-contents #P"fz.jpg"))
+
+    (say "Upload a file")
+    (http-request "upload.html"
+                  :method :post :parameters '(("clean" . "doit")))
+    (http-request "upload.html"
+                  :method :post :parameters '(("file1" #P"fz.jpg")))
+    (http-request "upload.html")
+    (http-assert-body (format nil "fz.jpg.*>~A&nbsp;Bytes" (file-length-string #P"fz.jpg")))
+
+    (say "Range tests")
+    (say " Upload file")
+    (let* ((range-test-file-size (* 256 1024))  ; large enough to have hunchentoot use multiple buffers when reading back data, should be aligned to 1024
+           (range-test-buffer (make-array range-test-file-size :element-type '(unsigned-byte 8)))
+           (uploaded-file-url "files/?path=user-stream")) ; The uploaded file will appear under the name "user-stream" in hunchentoot
+
+      (dotimes (i range-test-file-size)
+         (setf (aref range-test-buffer i) (random 256)))
+
+      (flex:with-input-from-sequence (upload-stream range-test-buffer)
+        (http-request "upload.html"
+                      :method :post :parameters `(("file1" ,upload-stream))))
+
+      (say " Request the uploaded file, verify contents")
+      (http-request uploaded-file-url)
+      (http-assert-header :content-length (princ-to-string range-test-file-size))
+      (http-assert 'body (complement #'mismatch) range-test-buffer)
+
+      (say " Verify responses to partial requests")
+
+      (say " Request just one byte")
+      (http-request uploaded-file-url :range '(0 0))
+      (http-assert 'status-code 206)
+      (http-assert 'body 'equalp (subseq range-test-buffer 0 1))
+      (http-assert-header :content-range (format nil "bytes 0-0/~D" range-test-file-size))
+
+      (say " End out of range")
+      (http-request uploaded-file-url :range (list 0 range-test-file-size))
+      (http-assert 'status-code 416)
+      (http-assert-header :content-range (format nil "bytes 0-~D/~A" (1- range-test-file-size) range-test-file-size))
+
+      (say " Request whole file as partial")
+      (http-request uploaded-file-url :range (list 0 (1- range-test-file-size)))
+      (http-assert 'status-code 206)
+      (http-assert 'body 'equalp range-test-buffer)
+      (http-assert-header :content-range (format nil "bytes 0-~D/~D" (1- range-test-file-size) range-test-file-size))
+
+      (say " Request something in the middle")
+      (let ((start-offset (/ range-test-file-size 4))
+            (length (/ range-test-file-size 2)))
+        (http-request uploaded-file-url :range (list start-offset (1- length)))
+        (http-assert 'status-code 206)
+        (http-assert 'body 'equalp (subseq range-test-buffer start-offset length))
+        (http-assert-header :content-range (format nil "bytes ~D-~D/~D" start-offset (1- length) range-test-file-size))))
+
+
+    (values)))
+
diff --git a/deps/hunchentoot/test/test-certificate.crt b/deps/hunchentoot/test/test-certificate.crt
new file mode 100644 (file)
index 0000000..95d8320
--- /dev/null
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB3TCCAUYCCQCDg/PAAhv7kjANBgkqhkiG9w0BAQQFADAzMQswCQYDVQQGEwJE
+RTEQMA4GA1UECBMHR2VybWFueTESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTA5MDIx
+NjEyMTgzMFoXDTEwMDIxNjEyMTgzMFowMzELMAkGA1UEBhMCREUxEDAOBgNVBAgT
+B0dlcm1hbnkxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAxpUaQISfEDw3c7VSFsW+oKQViarjMmZNJL7ZWaZlsbVgROPohCRj
+qmbS1yjQ0DZQWZK4PMyyXqT90OUIXSohGAB9O3M/etMpMYaRlws66o2mNx/R8bTZ
+qGDQcXXRg1Ghsq2JnQsyhl4nTQXMn8KM/jLd6iT9XJd+O6AuWfOlticCAwEAATAN
+BgkqhkiG9w0BAQQFAAOBgQCUW7a5BvL8Qoy5Mvd9cxUt8jnDm5KRiEgcmBIIlrVi
+bLXmEQaRPQDoxGsrzi/LaUuMitT/kaGwhbdhfwZsjXI2QxuqpPYRhLnPBvn6q77u
+e0/yXaPp6UnMnQNw2O8xLcUDeLbRrw9IBPeDUYYP0OaTkJvORwFJ4e6rdVyha4o7
+1A==
+-----END CERTIFICATE-----
diff --git a/deps/hunchentoot/test/test-handlers.lisp b/deps/hunchentoot/test/test-handlers.lisp
new file mode 100644 (file)
index 0000000..56bb68b
--- /dev/null
@@ -0,0 +1,557 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot-test)
+
+(defvar *this-file* (load-time-value
+                     (or #.*compile-file-pathname* *load-pathname*)))
+
+(defmacro with-html (&body body)
+  `(with-html-output-to-string (*standard-output* nil :prologue t)
+     ,@body))
+
+(defun hunchentoot-link ()
+  (with-html-output (*standard-output*)
+    (:a :href "http://weitz.de/hunchentoot/" "Hunchentoot")))
+
+(defun menu-link ()
+  (with-html-output (*standard-output*)
+    (:p (:hr
+         (:a :href "/hunchentoot/test" "Back to menu")))))
+
+(defmacro with-lisp-output ((var) &body body)
+  `(let ((*package* (find-package :hunchentoot-test-user)))
+     (with-output-to-string (,var #+:lispworks nil
+                                  #+:lispworks :element-type
+                                  #+:lispworks 'lw:simple-char)
+       ,@body)))
+
+(defmacro info-table (&rest forms)
+  (let ((=value= (gensym))
+        (=first= (gensym)))
+    `(with-html-output (*standard-output*)
+       (:p (:table :border 1 :cellpadding 2 :cellspacing 0
+            (:tr (:td :colspan 2
+                  "Some Information "
+                  (hunchentoot-link)
+                  " provides about this request:"))
+            ,@(loop for form in forms
+                    collect `(:tr (:td :valign "top"
+                                   (:pre :style "padding: 0px"
+                                    (esc (with-lisp-output (s) (pprint ',form s)))))
+                              (:td :valign "top"
+                               (:pre :style "padding: 0px"
+                                (esc (with-lisp-output (s)
+                                       (loop for ,=value= in (multiple-value-list ,form)
+                                             for ,=first= = t then nil
+                                             unless ,=first=
+                                             do (princ ", " s)
+                                             do (pprint ,=value= s))))))))))
+       (menu-link))))
+
+(defun authorization-page ()
+  (multiple-value-bind (user password)
+      (authorization)
+    (cond ((and (equal user "nanook")
+                (equal password "igloo"))
+           (with-html
+             (:html
+              (:head (:title "Hunchentoot page with Basic Authentication"))
+              (:body
+               (:h2 (hunchentoot-link)
+                " page with Basic Authentication")
+               (info-table (header-in* :authorization)
+                           (authorization))))))
+          (t
+           (require-authorization)))))
+
+(defparameter *test-image*
+  (load-time-value
+   (with-open-file (in (make-pathname :name "fz" :type "jpg" :version nil
+                                      :defaults *this-file*)
+                       :element-type 'flex:octet)
+     (let ((image-data (make-array (file-length in)
+                                   :element-type 'flex:octet)))
+       (read-sequence image-data in)
+       image-data))))
+
+(defun image-ram-page ()
+  (setf (content-type*) "image/jpeg")
+  *test-image*)
+
+(let ((count 0))
+  (defun info ()
+    (with-html
+      (:html
+       (:head (:title "Hunchentoot Information"))
+       (:body
+        (:h2 (hunchentoot-link) " Information Page")
+        (:p "This page has been called "
+         (:b
+          (fmt "~[~;once~;twice~:;~:*~R times~]" (incf count)))
+         " since its handler was compiled.")
+        (info-table (host)
+                    (acceptor-address *acceptor*)
+                    (acceptor-port *acceptor*)
+                    (remote-addr*)
+                    (remote-port*)
+                    (real-remote-addr)
+                    (request-method*)
+                    (script-name*)
+                    (query-string*)
+                    (get-parameters*)
+                    (headers-in*)
+                    (cookies-in*)
+                    (user-agent)
+                    (referer)
+                    (request-uri*)
+                    (server-protocol*)))))))
+
+(defun oops ()
+  (with-html
+    (log-message* :error "Oops \(error log level).")
+    (log-message* :warning "Oops \(warning log level).")
+    (log-message* :info "Oops \(info log level).")
+    (error "Errors were triggered on purpose.  Check your error log.")
+    (:html
+     (:body "You should never see this sentence..."))))
+
+(defun redir ()
+  (redirect "/hunchentoot/test/info.html?redirected=1"))
+
+(defun forbidden ()
+  (setf (return-code*) +http-forbidden+)
+  nil)
+
+(defun cookie-test ()
+  (set-cookie "pumpkin" :value "barking")
+  (no-cache)
+  (with-html
+    (:html
+     (:head (:title "Hunchentoot cookie test"))
+     (:body
+      (:h2 (hunchentoot-link)
+       " cookie test")
+      (:p "You might have to reload this page to see the cookie value.")
+      (info-table (cookie-in "pumpkin")
+                  (mapcar 'car (cookies-in*)))))))
+
+(defun session-test ()
+  (let ((new-foo-value (post-parameter "new-foo-value")))
+    (when new-foo-value
+      (setf (session-value 'foo) new-foo-value)))
+  (let ((new-bar-value (post-parameter "new-bar-value")))
+    (when new-bar-value
+      (setf (session-value 'bar) new-bar-value)))
+  (no-cache)
+  (with-html
+    (:html
+     (:head (:title "Hunchentoot session test"))
+     (:body
+      (:h2 (hunchentoot-link)
+       " session test")
+      (:p "Use the forms below to set new values for "
+       (:code "FOO")
+       " or "
+       (:code "BAR")
+       ". You can later return to this page to check if
+they're still set. Also, try to use another browser at the same
+time or try with cookies disabled.")
+      (:p (:form :method :post
+           "New value for "
+           (:code "FOO")
+           ": "
+           (:input :type :text
+            :name "new-foo-value"
+            :value (or (session-value 'foo) ""))))
+      (:p (:form :method :post
+           "New value for "
+           (:code "BAR")
+           ": "
+           (:input :type :text
+            :name "new-bar-value"
+            :value (or (session-value 'bar) ""))))
+      (info-table (session-cookie-name *acceptor*) 
+                  (cookie-in (session-cookie-name *acceptor*))
+                  (mapcar 'car (cookies-in*))
+                  (session-value 'foo)
+                  (session-value 'bar))))))
+
+(defun parameter-test (&key (method :get) (charset :iso-8859-1))
+  (no-cache)
+  (recompute-request-parameters :external-format
+                                (flex:make-external-format charset :eol-style :lf))
+  (setf (content-type*)
+        (format nil "text/html; charset=~A" charset))
+  (with-html
+    (:html
+     (:head (:title (fmt "Hunchentoot ~A parameter test" method)))
+     (:body
+      (:h2 (hunchentoot-link)
+       (fmt " ~A parameter test with charset ~A" method charset))
+      (:p "Enter some non-ASCII characters in the input field below
+and see what's happening.")
+      (:p (:form
+           :method method
+           "Enter a value: "
+           (:input :type :text
+            :name "foo")))
+      (case method
+        (:get (info-table (query-string*)
+                          (map 'list 'char-code (get-parameter "foo"))
+                          (get-parameter "foo")))
+        (:post (info-table (raw-post-data)
+                           (map 'list 'char-code (post-parameter "foo"))
+                           (post-parameter "foo"))))))))
+
+(defun parameter-test-latin1-get ()
+  (parameter-test :method :get :charset :iso-8859-1))
+
+(defun parameter-test-latin1-post ()
+  (parameter-test :method :post :charset :iso-8859-1))
+
+(defun parameter-test-utf8-get ()
+  (parameter-test :method :get :charset :utf-8))
+
+(defun parameter-test-utf8-post ()
+  (parameter-test :method :post :charset :utf-8))
+
+;; this should not be the same directory as *TMP-DIRECTORY* and it
+;; should be initially empty (or non-existent)
+(defvar *tmp-test-directory*
+    #+(or :win32 :mswindows) #p"c:\\hunchentoot-temp\\test\\"
+    #-(or :win32 :mswindows) #p"/tmp/hunchentoot/test/")
+
+(defvar *tmp-test-files* nil)
+
+(let ((counter 0))
+  (defun handle-file (post-parameter)
+    (when (and post-parameter
+               (listp post-parameter))
+      (destructuring-bind (path file-name content-type)
+          post-parameter
+        (let ((new-path (make-pathname :name (format nil "hunchentoot-test-~A"
+                                                     (incf counter))
+                                       :type nil
+                                       :defaults *tmp-test-directory*)))
+          ;; strip directory info sent by Windows browsers
+          (when (search "Windows" (user-agent) :test 'char-equal)
+            (setq file-name (cl-ppcre:regex-replace ".*\\\\" file-name "")))
+          (rename-file path (ensure-directories-exist new-path))
+          (push (list new-path file-name content-type) *tmp-test-files*))))))
+
+(defun clean-tmp-dir ()
+  (loop for (path . nil) in *tmp-test-files*
+        when (probe-file path)
+        do (ignore-errors (delete-file path)))
+  (setq *tmp-test-files* nil))
+
+(defun upload-test ()
+  (let (post-parameter-p)
+    (when (post-parameter "file1")
+      (handle-file (post-parameter "file1"))
+      (setq post-parameter-p t))
+    (when (post-parameter "file2")
+      (handle-file (post-parameter "file2"))
+      (setq post-parameter-p t))
+    (when (post-parameter "clean")
+      (clean-tmp-dir)
+      (setq post-parameter-p t)))
+  (no-cache)
+  (with-html
+    (:html
+     (:head (:title "Hunchentoot file upload test"))
+     (:body
+      (:h2 (hunchentoot-link)
+       " file upload test")
+      (:form :method :post :enctype "multipart/form-data"
+       (:p "First file: "
+        (:input :type :file
+         :name "file1"))
+       (:p "Second file: "
+        (:input :type :file
+         :name "file2"))
+       (:p (:input :type :submit)))
+      (when *tmp-test-files*
+        (htm
+         (:p
+          (:table :border 1 :cellpadding 2 :cellspacing 0
+           (:tr (:td :colspan 3 (:b "Uploaded files")))
+           (loop for (path file-name nil) in *tmp-test-files*
+                 for counter from 1
+                 do (htm
+                     (:tr (:td :align "right" (str counter))
+                      (:td (:a :href (format nil "files/~A?path=~A"
+                                             (url-encode file-name)
+                                             (url-encode (namestring path)))
+                            (esc file-name)))
+                      (:td :align "right"
+                       (str (ignore-errors
+                              (with-open-file (in path)
+                                (file-length in))))
+                       "&nbsp;Bytes"))))))
+         (:form :method :post
+          (:p (:input :type :submit :name "clean" :value "Delete uploaded files")))))
+      (menu-link)))))
+
+(defun send-file ()
+  (let* ((path (get-parameter "path"))
+         (file-info (and path
+                         (find path *tmp-test-files*
+                               :key 'first :test (lambda (a b) (equal a (namestring b)))))))
+    (unless file-info
+      (setf (return-code*) +http-not-found+)
+      (return-from send-file))
+    (handle-static-file (first file-info) (third file-info))))
+
+(defparameter *headline*
+  (load-time-value              
+   (format nil "Hunchentoot test menu (see file <code>~A</code>)"
+           (truename (merge-pathnames (make-pathname :type "lisp") *this-file*)))))
+
+(defvar *utf-8* (flex:make-external-format :utf-8 :eol-style :lf))
+
+(defvar *utf-8-file* (merge-pathnames "UTF-8-demo.html" *this-file*)
+  "Demo file stolen from <http://www.w3.org/2001/06/utf-8-test/>.")
+
+(defun stream-direct ()
+  (setf (content-type*) "text/html; charset=utf-8")
+  (let ((stream (send-headers))
+        (buffer (make-array 1024 :element-type 'flex:octet)))
+    (with-open-file (in *utf-8-file* :element-type 'flex:octet)
+      (loop for pos = (read-sequence buffer in)
+            until (zerop pos) 
+            do (write-sequence buffer stream :end pos)))))
+
+(defun stream-direct-utf-8 ()
+  (setf (content-type*) "text/html; charset=utf-8")
+  (let ((stream (flex:make-flexi-stream (send-headers) :external-format *utf-8*)))
+    (with-open-file (in (merge-pathnames "UTF-8-demo.html" *this-file*)
+                        :element-type 'flex:octet)
+      (setq in (flex:make-flexi-stream in :external-format *utf-8*))
+      (loop for line = (read-line in nil nil)
+            while line
+            do (write-line line stream)))))
+
+(defun stream-direct-utf-8-string ()
+  (setf (content-type*) "text/html; charset=utf-8"
+        (reply-external-format*) *utf-8*)
+  (with-open-file (in (merge-pathnames "UTF-8-demo.html" *this-file*)
+                      :element-type 'flex:octet)
+    (let ((string (make-array (file-length in)
+                              :element-type #-:lispworks 'character #+:lispworks 'lw:simple-char
+                              :fill-pointer t)))
+      (setf in (flex:make-flexi-stream in :external-format *utf-8*)
+            (fill-pointer string) (read-sequence string in))
+      string)))
+
+(define-easy-handler (easy-demo :uri "/hunchentoot/test/easy-demo.html"
+                                :default-request-type :post)
+    (first-name last-name
+                (age :parameter-type 'integer)
+                (implementation :parameter-type 'keyword)
+                (meal :parameter-type '(hash-table boolean))
+                (team :parameter-type 'list))
+  (with-html
+    (:html
+     (:head (:title "Hunchentoot \"easy\" handler example"))
+     (:body
+      (:h2 (hunchentoot-link)
+       " \"Easy\" handler example")
+      (:p (:form :method :post
+           (:table :border 1 :cellpadding 2 :cellspacing 0
+            (:tr
+             (:td "First Name:")
+             (:td (:input :type :text
+                   :name "first-name"
+                   :value (or first-name "Donald"))))
+            (:tr
+             (:td "Last name:")
+             (:td (:input :type :text
+                   :name "last-name"
+                   :value (or last-name "Duck"))))
+            (:tr
+             (:td "Age:")
+             (:td (:input :type :text
+                   :name "age"
+                   :value (or age 42))))
+            (:tr
+             (:td "Implementation:")
+             (:td (:select :name "implementation"
+                   (loop for (value option) in '((:lispworks "LispWorks")
+                                                 (:allegro "AllegroCL")
+                                                 (:cmu "CMUCL")
+                                                 (:sbcl "SBCL")
+                                                 (:openmcl "OpenMCL"))
+                         do (htm
+                             (:option :value value
+                              :selected (eq value implementation)
+                              (str option)))))))
+            (:tr
+             (:td :valign :top "Meal:")
+             (:td (loop for choice in '("Burnt weeny sandwich"
+                                        "Canard du jour"
+                                        "Easy meat"
+                                        "Muffin"
+                                        "Twenty small cigars"
+                                        "Yellow snow")
+                        do (htm
+                            (:input :type "checkbox"
+                             :name (format nil "meal{~A}" choice)
+                             :checked (gethash choice meal)
+                             (esc choice))
+                            (:br)))))
+            (:tr
+             (:td :valign :top "Team:")
+             (:td (loop for player in '("Beckenbauer"
+                                        "Cruyff"
+                                        "Maradona"
+                                        ;; without accent (for SBCL)
+                                        "Pele"
+                                        "Zidane")
+                        do (htm
+                            (:input :type "checkbox"
+                             :name "team"
+                             :value player
+                             :checked (member player team :test 'string=)
+                             (esc player))
+                            (:br)))))
+            (:tr
+             (:td :colspan 2
+              (:input :type "submit"))))))
+      (info-table first-name
+                  last-name
+                  age
+                  implementation
+                  (loop :for choice :being :the :hash-keys :of meal :collect choice)
+                  (gethash "Yellow snow" meal)
+                  team)))))
+                
+
+(defun menu ()
+  (with-html
+    (:html
+     (:head
+      (:link :rel "shortcut icon"
+       :href "/hunchentoot/test/favicon.ico" :type "image/x-icon")
+      (:title "Hunchentoot test menu"))
+     (:body
+      (:h2 (str *headline*))
+      (:table :border 0 :cellspacing 4 :cellpadding 4
+       (:tr (:td (:a :href "/hunchentoot/test/info.html?foo=bar"
+                  "Info provided by Hunchentoot")))
+       (:tr (:td (:a :href "/hunchentoot/test/cookie.html"
+                  "Cookie test")))
+       (:tr (:td (:a :href "/hunchentoot/test/session.html"
+                  "Session test")))
+       (:tr (:td (:a :href "/hunchentoot/test/parameter_latin1_get.html"
+                  "GET parameter handling with LATIN-1 charset")))
+       (:tr (:td (:a :href "/hunchentoot/test/parameter_latin1_post.html"
+                  "POST parameter handling with LATIN-1 charset")))
+       (:tr (:td (:a :href "/hunchentoot/test/parameter_utf8_get.html"
+                  "GET parameter handling with UTF-8 charset")))
+       (:tr (:td (:a :href "/hunchentoot/test/parameter_utf8_post.html"
+                  "POST parameter handling with UTF-8 charset")))
+       (:tr (:td (:a :href "/hunchentoot/test/redir.html"
+                  "Redirect \(302) to info page above")))
+       (:tr (:td (:a :href "/hunchentoot/test/authorization.html"
+                  "Authorization")
+             " (user 'nanook', password 'igloo')"))
+       (:tr (:td (:a :href "/hunchentoot/code/test-handlers.lisp"
+                  "The source code of this test")))
+       (:tr (:td (:a :href "/hunchentoot/test/image.jpg"
+                  "Binary data, delivered from file")
+             " \(a picture)"))
+       (:tr (:td (:a :href "/hunchentoot/test/image-ram.jpg"
+                  "Binary data, delivered from RAM")
+             " \(same picture)"))
+       (:tr (:td (:a :href "/hunchentoot/test/easy-demo.html"
+                  "\"Easy\" handler example")))
+       (:tr (:td (:a :href "/hunchentoot/test/utf8-binary.txt"
+                  "UTF-8 demo")
+             " \(writing octets directly to the stream)"))
+       (:tr (:td (:a :href "/hunchentoot/test/utf8-character.txt"
+                  "UTF-8 demo")
+             " \(writing UTF-8 characters directly to the stream)"))
+       (:tr (:td (:a :href "/hunchentoot/test/utf8-string.txt"
+                  "UTF-8 demo")
+             " \(returning a string)"))
+       (:tr (:td (:a :href "/hunchentoot/test/upload.html"
+                  "File uploads")))
+       (:tr (:td (:a :href "/hunchentoot/test/forbidden.html"
+                  "Forbidden \(403) page")))
+       (:tr (:td (:a :href "/hunchentoot/test/oops.html"
+                  "Error handling")
+             " \(output depends on "
+             (:a :href "http://weitz.de/hunchentoot/#*show-lisp-errors-p*"
+              (:code "*SHOW-LISP-ERRORS-P*"))
+             (fmt " \(currently ~S))" *show-lisp-errors-p*)))
+       (:tr (:td (:a :href "/hunchentoot/foo"
+                  "URI handled by")
+             " "
+             (:a :href "http://weitz.de/hunchentoot/#*default-handler*"
+              (:code "*DEFAULT-HANDLER*")))))))))
+
+(setq *dispatch-table*
+      (nconc
+       (list 'dispatch-easy-handlers
+             (create-static-file-dispatcher-and-handler
+              "/hunchentoot/test/image.jpg"
+              (make-pathname :name "fz" :type "jpg" :version nil
+                             :defaults *this-file*)
+              "image/jpeg")
+             (create-static-file-dispatcher-and-handler
+              "/hunchentoot/test/favicon.ico"
+              (make-pathname :name "favicon" :type "ico" :version nil
+                             :defaults *this-file*))
+             (create-folder-dispatcher-and-handler
+              "/hunchentoot/code/"
+              (make-pathname :name nil :type nil :version nil
+                             :defaults *this-file*)
+              "text/plain"))
+       (mapcar (lambda (args)
+                 (apply 'create-prefix-dispatcher args))
+               '(("/hunchentoot/test/form-test.html" form-test)
+                 ("/hunchentoot/test/forbidden.html" forbidden)
+                 ("/hunchentoot/test/info.html" info)
+                 ("/hunchentoot/test/authorization.html" authorization-page)
+                 ("/hunchentoot/test/image-ram.jpg" image-ram-page)
+                 ("/hunchentoot/test/cookie.html" cookie-test)
+                 ("/hunchentoot/test/session.html" session-test)
+                 ("/hunchentoot/test/parameter_latin1_get.html" parameter-test-latin1-get)
+                 ("/hunchentoot/test/parameter_latin1_post.html" parameter-test-latin1-post)
+                 ("/hunchentoot/test/parameter_utf8_get.html" parameter-test-utf8-get)
+                 ("/hunchentoot/test/parameter_utf8_post.html" parameter-test-utf8-post)
+                 ("/hunchentoot/test/upload.html" upload-test)
+                 ("/hunchentoot/test/redir.html" redir)
+                 ("/hunchentoot/test/oops.html" oops)
+                 ("/hunchentoot/test/utf8-binary.txt" stream-direct)
+                 ("/hunchentoot/test/utf8-character.txt" stream-direct-utf-8)
+                 ("/hunchentoot/test/utf8-string.txt" stream-direct-utf-8-string)
+                 ("/hunchentoot/test/files/" send-file)
+                 ("/hunchentoot/test" menu)))))
diff --git a/deps/hunchentoot/test/test-key-no-password.key b/deps/hunchentoot/test/test-key-no-password.key
new file mode 100644 (file)
index 0000000..a4291c5
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDGlRpAhJ8QPDdztVIWxb6gpBWJquMyZk0kvtlZpmWxtWBE4+iE
+JGOqZtLXKNDQNlBZkrg8zLJepP3Q5QhdKiEYAH07cz960ykxhpGXCzrqjaY3H9Hx
+tNmoYNBxddGDUaGyrYmdCzKGXidNBcyfwoz+Mt3qJP1cl347oC5Z86W2JwIDAQAB
+AoGAJoJhneNaCUb0Je8ipSHhzrsjJhhKiMqH6TlNYvI+xFB9A78CpyV7Yl8gQfM7
+UzVFLamjKr8zU+FBC1Ju5co2sl4u3fPgXwuo5X36IVa03WdClXp0PQ7RsOXqi0Rx
+d1maRkxPok7AnSMCAWNeLCgxVmCKzIWLKcvB8idK7evjGUkCQQDyoewf7ey1eNy7
+hv87E9E/gUQ/9A9rEhkKcRbwvEicB+OcxpZl6Br0Z6EJH39AlJe1ii81lSqfPd+h
+6WE2uU+lAkEA0YXmYnCJdlcYAORLX3ewibVCikOJUIMt7smGVOK23ubmHh49+KUW
+HT3xDPDRVmkmiYzqXZOY0pGUG37b4GAE2wJAXRPa1kDanp835kSaYtpuWjNHsFT7
+GTL/Ii9SApXoMNsh6QGRrpREyt96Olq34VlffYf+JksL57y/rogt/+VE9QJAV+vV
+YmeQ92zSsMUb7+K83PyIAJcYjwWNB8/fI83DKURBOlA8dxNndTvh5ClF3vne5weP
+7VabYXkfam5QfBYu0wJANPeIsAd8yUdZViiMOH6tE8DUlMy/p1N9Rz0eMSc4uUch
+EB59djdHmSknY0JgVZJFybWFWKtbxSvcnrJq/hAcMQ==
+-----END RSA PRIVATE KEY-----
diff --git a/deps/hunchentoot/url-rewrite/packages.lisp b/deps/hunchentoot/url-rewrite/packages.lisp
new file mode 100644 (file)
index 0000000..eff5edf
--- /dev/null
@@ -0,0 +1,38 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :cl-user)
+
+(defpackage #:url-rewrite
+  (:use :cl)
+  (:export #:*url-rewrite-tags*
+           #:*url-rewrite-fill-tags*
+           #:starts-with-scheme-p
+           #:add-get-param-to-url
+           #:rewrite-urls
+           #:url-encode))
\ No newline at end of file
diff --git a/deps/hunchentoot/url-rewrite/primitives.lisp b/deps/hunchentoot/url-rewrite/primitives.lisp
new file mode 100644 (file)
index 0000000..96dd185
--- /dev/null
@@ -0,0 +1,154 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :url-rewrite)
+
+(declaim (inline peek-char*))
+(defun peek-char* ()
+  "PEEK-CHAR with input stream bound to *STANDARD-INPUT* and returning
+NIL on EOF."
+  (peek-char nil nil nil))
+
+(declaim (inline whitespacep))
+(defun whitespacep (c)
+  "Checks whether C is a whitespace character."
+  (find c '(#\Space #\Tab #\Newline #\Linefeed #\Return #\Page)))
+
+(declaim (inline letterp))
+(defun letterp (c)
+  "Checks whether C is a character between A and Z
+\(case-insensitive)."
+  (and (characterp c)
+       (or (char<= #\a c #\z)
+           (char<= #\A c #\Z))))
+
+(declaim (inline name-char-p))
+(defun name-char-p (c)
+  "Checks whether C is a name constituent character in the sense of
+HTML."
+  (and (characterp c)
+       (or (letterp c)
+           (digit-char-p c)
+           (char= c #\-)
+           (char= c #\.))))
+
+(defun comment-start-p ()
+  "Checks whether *STANDARD-OUTPUT* currently 'looks at' the string
+\"--\".  Will move the position within the stream by one unless the
+first characters it sees is not a hyphen."
+  (unless (eql (peek-char*) #\-)
+    ;; if the first character isn't #\- we can return immediately
+    (return-from comment-start-p nil))
+  ;; otherwise read the #\- so we can check the next character
+  (read-char)
+  (eql (peek-char*) #\-))
+
+(defun read-while (predicate &key (skip t) (write-through t))
+  "Reads characters from *STANDARD-INPUT* while PREDICATE returns a
+true value for each character.  Returns the string which was read
+unless SKIP is true.  Writes all characters read to *STANDARD-OUTPUT*
+if WRITE-THROUGH is true.  On EOF the string read so far is returned."
+  (let ((collector (or skip
+                       (make-array 0
+                                   :element-type 'character
+                                   :fill-pointer t
+                                   :adjustable t))))
+    (handler-case
+      (loop while (funcall predicate (peek-char)) do
+            (let ((char (read-char)))
+              (when write-through
+                (write-char char))
+              (unless skip
+                (vector-push-extend char collector)))
+            finally (return (and (not skip) collector)))
+      (end-of-file ()
+        (and (not skip) collector)))))
+
+(defun read-until (string &key (skip t) (write-through t))
+  "Reads characters from *STANDARD-INPUT* up to and including STRING.
+Returns the string which was read \(excluding STRING) unless SKIP is
+true.  Writes all characters read to *STANDARD-OUTPUT* if
+WRITE-THROUGH is true.  On EOF the string read so far is returned."
+  (let* ((length (length string))
+         (offsets
+           ;; we first check whether some substring which starts
+           ;; STRING can be found again later in STRING - this is
+           ;; necessary because we only peek one character ahead
+           (cond ((gethash string *find-string-hash*))
+                 (t (setf (gethash string *find-string-hash*)
+                            ;; the resulting array of offsets is
+                            ;; cached in *FIND-STRING-HASH* so we can
+                            ;; use it again in case READ-UNTIL is
+                            ;; called with the same STRING argument
+                            (loop with offsets = (make-array length
+                                                             :initial-element nil)
+                                  for i from 1 below length
+                                  ;; check if STRING starting from 0
+                                  ;; has something in common with
+                                  ;; STRING starting from I
+                                  for mismatch = (mismatch string string
+                                                           :start1 i :test #'char=)
+                                  when (> mismatch i)
+                                  ;; if this is the case remember the
+                                  ;; length of the match plus the
+                                  ;; character which must follow in
+                                  ;; OFFSETS
+                                  do (push (cons (char string (- mismatch i))
+                                                 (1+ (- mismatch i)))
+                                           (svref offsets i))
+                                  finally (return offsets))))))
+         (collector (or skip
+                        (make-array 0
+                                    :element-type 'character
+                                    :fill-pointer t
+                                    :adjustable t))))
+    (handler-case
+      (loop for i = 0 then (cond (match (1+ i))
+                                 ;; if there is an offset (see above)
+                                 ;; we don't have to start from the
+                                 ;; beginning of STRING
+                                 ((cdr (assoc c (svref offsets i))))
+                                 (t 0))
+            for c = (peek-char)
+            for match = (char= c (char string i))
+            while (or (not match) (< (1+ i) length)) do
+            (cond (skip (read-char))
+                  (t (vector-push-extend (read-char) collector)))
+            when write-through do
+            (write-char c)
+            finally (if write-through
+                      (write-char (read-char))
+                      (read-char))
+            (unless skip
+              ;; decrement the fill pointer because collector now also
+              ;; contains STRING itself
+              (decf (fill-pointer collector) (1- length)))
+            (return (and (not skip) collector)))
+      (end-of-file ()
+        (and (not skip) collector)))))
+
diff --git a/deps/hunchentoot/url-rewrite/specials.lisp b/deps/hunchentoot/url-rewrite/specials.lisp
new file mode 100644 (file)
index 0000000..ee987c8
--- /dev/null
@@ -0,0 +1,66 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz.  All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :url-rewrite)
+
+(defvar *url-rewrite-tags*
+  '(("a" . "href")
+    ("area" . "href")
+    ("frame" . "src")
+    ("img" . "src")
+    ("input" . "src")
+    ("form" . "action")
+    ("iframe" . "src"))
+  "The tag/attribute combinations where URL-rewriting should happen.")
+
+(defvar *url-rewrite-fill-tags*
+  '(("form" . "action"))
+  "The tag/attribute combinations where URL-rewriting should
+optionally add an attribute.")
+
+(defvar *find-string-hash*
+  (make-hash-table :test #'equal)
+  "Hash tables used internally by READ-UNTIL to cache offset arrays.")
+
+;; stuff for Nikodemus Siivola's HYPERDOC
+;; see <http://common-lisp.net/project/hyperdoc/>
+;; and <http://www.cliki.net/hyperdoc>
+
+(defvar *hyperdoc-base-uri* "http://weitz.de/url-rewrite/")
+
+(let ((exported-symbols-alist
+       (loop for symbol being the external-symbols of :url-rewrite
+             collect (cons symbol
+                           (concatenate 'string
+                                        "#"
+                                        (string-downcase symbol))))))
+  (defun hyperdoc-lookup (symbol type)
+    (declare (ignore type))
+    (cdr (assoc symbol
+                exported-symbols-alist
+                :test #'eq))))
diff --git a/deps/hunchentoot/url-rewrite/url-rewrite.lisp b/deps/hunchentoot/url-rewrite/url-rewrite.lisp
new file mode 100644 (file)
index 0000000..7670467
--- /dev/null
@@ -0,0 +1,293 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :url-rewrite)
+
+(defun starts-with-scheme-p (string)
+  "Checks whether the string STRING represents a URL which starts with
+a scheme, i.e. something like 'https://' or 'mailto:'."
+  (loop with scheme-char-seen-p = nil
+        for c across string
+        when (or (char-not-greaterp #\a c #\z)
+                 (digit-char-p c)
+                 (member c '(#\+ #\- #\.) :test #'char=))
+        do (setq scheme-char-seen-p t)
+        else return (and scheme-char-seen-p
+                         (char= c #\:))))
+
+(defun url-encode (string)
+  "URL-encode a string."
+  (with-output-to-string (s)
+    (loop for c across string
+          do (cond ((or (char<= #\0 c #\9)
+                        (char<= #\a c #\z)
+                        (char<= #\A c #\Z)
+                        (find c "$-_.!*'()," :test #'char=))
+                     (write-char c s))
+                   ((char= c #\Space)
+                     (write-char #\+ s))
+                   (t (format s "%~2,'0x" (char-code c)))))))
+
+(defun add-get-param-to-url (url name value)
+  "URL is assumed to be a http URL. The pair of NAME and VALUE will be
+added as a GET parameter to this URL. Assumes that there's no other
+parameter of the same name. Only checks if #\? is part of the string
+to decide how to attach the new parameter to the end of the string."
+  ;; possible bug: doesn't check for #\? which is written as, say,
+  ;; "&x3f;" - also, is there any other way a question mark could be a
+  ;; legitimate part of a URL?
+  (concatenate 'string
+               url
+               (if (find #\? url :test #'char=)
+                 "&amp;"
+                 "?")
+               name
+               "="
+               (url-encode value)))
+
+(defun rewrite-urls (rewrite-fn &optional (test-fn (complement #'starts-with-scheme-p)))
+  "Reads an \(X)HTML document from *STANDARD-INPUT* and writes it back
+to *STANDARD-OUTPUT*. Any attribute value which is in one of the
+positions denoted by *URL-REWRITE-TAGS* is rewritten by REWRITE-FN if
+it passes the test denoted by the optional function TEST-FN which
+defaults to the complement of STARTS-WITH-SCHEME-P.
+
+This function aims to yield correct results for correct \(X)HTML input
+and it also tries hard to never signal an error although it may warn
+if it encounters syntax errors. It will NOT detect any possible error
+nor is there any warranty that it will work correctly with faulty
+input."
+  (loop
+    ;; read (and write back) until we see a #\< which is a candidate
+    ;; for a tag or a markup declaration
+    (read-until "<")
+    ;; get next char without reading it
+    (let ((peek-char (peek-char*)))
+      (cond ((null peek-char)
+              ;; stop if EOF
+              (return-from rewrite-urls))
+            ((char= peek-char #\!)
+              ;; we've seen "<!" so this might be a markup declaration
+              ;; - first write #\! back
+              (write-char (read-char))
+              ;; peek at next char
+              (let ((peek-char (peek-char*)))
+                (cond ((null peek-char)
+                        ;; stop if EOF
+                        (return-from rewrite-urls))
+                      ((eql peek-char #\>)
+                        ;; "<!>" is nothing special, just write the
+                        ;; char and go back to the start of the loop
+                        (write-char (read-char)))
+                      ((letterp peek-char)
+                        ;; a letter, so this should be something like
+                        ;; <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2
+                        ;; Final//EN"> - we just check for names and
+                        ;; delimited strings separated by whitespace
+                        ;; until we see the next #\>
+                        (read-name)
+                        (skip-whitespace)
+                        (block parameter-loop
+                          (loop
+                            (let ((peek-char (peek-char*)))
+                              (cond ((null peek-char)
+                                      ;; stop if EOF
+                                      (warn "EOF in markup declaration")
+                                      (return-from rewrite-urls))
+                                    ((char= peek-char #\>)
+                                      ;; a #\> ends the markup
+                                      ;; declaration - write it back
+                                      ;; and exit the loop
+                                      (write-char (read-char))
+                                      (return-from parameter-loop))
+                                    ((or (letterp peek-char)
+                                         (find peek-char '(#\' #\") :test #'char=))
+                                      ;; a delimiter or a letter, so
+                                      ;; we expect a delimited string
+                                      (read-delimited-string)
+                                      (skip-whitespace))
+                                    ((comment-start-p)
+                                      ;; a comment - skip it and write it back
+                                      (skip-comment))
+                                    (t
+                                      ;; something else - this is an error
+                                      ;; so we warn and exit the loop
+                                      (warn "Unexpected character ~S in markup declaration"
+                                            peek-char)
+                                      (return-from parameter-loop)))))))
+                      ((comment-start-p)
+                        ;; we've seen "<!--" so this starts a comment declaration
+                        ;; - we'll read comments which are possibly separated
+                        ;; by whitespace
+                        (block comment-loop
+                          (loop
+                            (skip-comment)
+                            (skip-whitespace)
+                            (let ((peek-char (peek-char*)))
+                              (cond ((null peek-char)
+                                      ;; stop if EOF
+                                      (warn "EOF in comment declaration")
+                                      (return-from rewrite-urls))
+                                    ((char= peek-char #\>)
+                                      ;; a #\> ends the comment
+                                      ;; declaration - write it back
+                                      ;; and exit the loop
+                                      (write-char (read-char))
+                                      (return-from comment-loop))
+                                    ;; a comment - do nothing
+                                    ((comment-start-p))
+                                    (t
+                                      ;; something else - this is an error
+                                      ;; so we warn and exit the loop
+                                      (warn "Unexpected character ~S in comment declaration"
+                                            peek-char)
+                                      (return-from comment-loop)))))))
+                      (t
+                        ;; neither markup declaration nor comment declaration,
+                        ;; so this was just "<!"
+                        (write-char (read-char))))))
+            ((char= peek-char #\/)
+              (write-char (read-char))
+              (let ((peek-char (peek-char*)))
+                (cond ((null peek-char)
+                        ;; stop if EOF
+                        (warn "EOF in end-tag")
+                        (return-from rewrite-urls))
+                      ((letterp peek-char)
+                        ;; a letter, so this is supposed to start a name -
+                        ;; read it and skip whitespace following it
+                        (let ((name (read-name :skip nil)))
+                          (skip-whitespace)
+                          (let ((peek-char (peek-char*)))
+                            (cond ((null peek-char)
+                                    ;; stop if EOF
+                                    (warn "EOF after </~A" name)
+                                    (return-from rewrite-urls))
+                                  ((char/= (peek-char*) #\>)
+                                    ;; we expect to see #\> here - if not
+                                    ;; we warn but do nothing else
+                                    (warn "Expected #\> after </~A" name))
+                                  (t
+                                    ;; end of end tag, just consume the #\>
+                                    (write-char (read-char)))))))
+                      (t
+                        ;; not a letter, so this is an error -
+                        ;; we warn and ignore this
+                        (warn "Unexpected character ~S after </"
+                              peek-char)))))
+            ((letterp peek-char)
+              ;; a letter so we expect a start tag, possibly followed by
+              ;; attributes - first read name, check if it's mentioned
+              ;; in *URL-REWRITE-TAGS*, and find the name of the
+              ;; corresponding attribute
+              (let* ((name (read-name :skip nil))
+                     (rewrite-attribute (and name
+                                             (cdr (assoc name *url-rewrite-tags*
+                                                         :test #'string-equal))))
+                    attribute-found-p)
+                (flet ((maybe-write-attribute (&optional value
+                                                         (rewrite-attribute
+                                                          (and (not attribute-found-p)
+                                                               (cdr (assoc name
+                                                                           *url-rewrite-fill-tags*
+                                                                           :test #'string-equal)))))
+                         ;; write the name of the attribute
+                         ;; REWRITE-ATTRIBUTE and its (rewritten)
+                         ;; value VALUE to *STANDARD-OUTPUT* if DO-IT
+                         ;; is true - the default value for DO-IT
+                         ;; means to only write the attribute if it
+                         ;; has to be added
+                         (when rewrite-attribute
+                           (unless attribute-found-p
+                             (write-char #\Space))
+                           (write-string rewrite-attribute)
+                           (write-char #\=)
+                           (let ((delimiter (if (find #\' value :test #'char=)
+                                              #\" #\')))
+                             (write-char delimiter)
+                             (write-string (funcall rewrite-fn value))
+                             (write-char delimiter)))))
+                  (skip-whitespace)
+                  (block attribute-loop
+                    (loop
+                      (let ((peek-char (peek-char*)))
+                        (cond ((null peek-char)
+                                ;; stop if EOF
+                                (warn "EOF before ~A tag was closed" name)
+                                (return-from rewrite-urls))
+                              ((eql peek-char #\>)
+                                ;; end of tag - exit attribute loop
+                                (maybe-write-attribute)
+                                (write-char (read-char))
+                                (return-from attribute-loop))
+                              ((eql peek-char #\/)
+                                ;; we've seen #\/, so this might be the XHTML way
+                                ;; to end a stand-alone tag
+                                (write-char (read-char))
+                                (cond ((eql (peek-char*) #\>)
+                                        ;; yes, it is - exit this loop
+                                        (maybe-write-attribute)
+                                        (write-char (read-char)))
+                                      (t
+                                        ;; no, it's not - so this is an error
+                                        (warn "Unexpected #\/ in ~A tag" name)))
+                                ;; exit attribute loop in any case
+                                (return-from attribute-loop))
+                              ((letterp peek-char)
+                                ;; a letter - this should start an attribute
+                                (multiple-value-bind (name value string)
+                                    ;; no need to cons up return values if we're
+                                    ;; not going to rewrite anyway
+                                    (read-attribute :skip (null rewrite-attribute)
+                                                    :write-through (null rewrite-attribute))
+                                  (cond ((and rewrite-attribute
+                                              (string-equal name rewrite-attribute))
+                                          ;; remember that we've seen the
+                                          ;; attribute in question
+                                          (setq attribute-found-p t)
+                                          ;; if this an attribute which should be
+                                          ;; rewritten do it and write the whole
+                                          ;; stuff to *STANDARD-OUT* explicitly
+                                          (cond ((funcall test-fn value)
+                                                  (maybe-write-attribute value name))
+                                                (t
+                                                  ;; otherwise write it back
+                                                  (write-string string))))
+                                        (rewrite-attribute
+                                          ;; we didn't rewrite this attribute but we
+                                          ;; have to write it back to *STANDARD-OUTPUT*
+                                          ;; because READ-ATTRIBUTE didn't do it
+                                          (write-string string))))
+                                (skip-whitespace))
+                              (t
+                                ;; an error - exit the attribute loop
+                                (warn "Unexpected character ~A after <~A" peek-char name)
+                                (return-from attribute-loop)))))))))
+            (t
+              ;; anything else means this is just #\<, no markup
+              (write-char (read-char)))))))
diff --git a/deps/hunchentoot/url-rewrite/util.lisp b/deps/hunchentoot/url-rewrite/util.lisp
new file mode 100644 (file)
index 0000000..417d387
--- /dev/null
@@ -0,0 +1,126 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-USER; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :url-rewrite)
+
+(declaim (inline skip-whitespace))
+(defun skip-whitespace (&key (skip t) (write-through t))
+  "Read characters from *STANDARD-INPUT* as long as they are
+whitespace. Returns the string which was read unless SKIP is true. On
+EOF the string read so far is returned. Writes all characters read to
+*STANDARD-OUTPUT* if WRITE-THROUGH is true."
+  (read-while #'whitespacep
+              :skip skip
+              :write-through write-through))
+
+(defun read-delimited-string (&key (skip t) (write-through t))
+  "Reads and returns as its first value a string from
+*STANDARD-INPUT*. The string is either delimited by ' or \" in which
+case the delimiters aren't part of the string but the second return
+value is the delimiter character or it is assumed to extend to the
+next character which is not a name constituent \(see NAME-CHAR-P). On
+EOF the string read so far is returned. If SKIP is true NIL is
+returned. Writes all characters read to *STANDARD-OUTPUT* if
+WRITE-THROUGH is true."
+  ;; note that this function has no means to signal to the caller
+  ;; that it encountered EOF before the closing delimiter was read,
+  ;; i.e. "'foo' bar='baz'" and "'foo" yield the same result, namely
+  ;; the values "foo" and #\'
+  (handler-case
+    (let* ((peek-char (peek-char))
+           (delimiter (find peek-char '(#\' #\"))))
+      (when delimiter
+        (read-char)
+        (when write-through
+          (write-char delimiter)))
+      (multiple-value-prog1
+        (values
+         (read-while (if delimiter
+                       (lambda (c) (char/= c delimiter))
+                       (lambda (c) (name-char-p c)))
+                     :skip skip
+                     :write-through write-through)
+         delimiter)
+        (when delimiter
+          (read-char)
+          (when write-through
+            (write-char delimiter)))))
+    (end-of-file ()
+      ;; this can only happen if the very first PEEK-CHAR fails,
+      ;; otherwise EOF is handled by READ-WHILE
+      nil)))
+
+(declaim (inline read-name))
+(defun read-name (&key (skip t) (write-through t))
+  "Read characters from *STANDARD-INPUT* as long as they are name
+constituents. Returns the string which was read unless SKIP is
+true. On EOF the string read so far is returned. Writes all characters
+read to *STANDARD-OUTPUT* if WRITE-THROUGH is true."
+  (read-while #'name-char-p :skip skip :write-through write-through))
+
+(defun read-attribute (&key (skip t) (write-through t))
+  "Read characters from *STANDARD-INPUT* assuming that they constitue
+a SGML-style attribute/value pair. Returns three values - the name of
+the attribute, its value, and the whole string which was read. On EOF
+the string(s) read so far is/are returned. If SKIP is true NIL is
+returned. Writes all characters read to *STANDARD-OUTPUT* if
+WRITE-THROUGH is true."
+  (let* ((name (read-name :skip skip
+                          :write-through write-through))
+         (whitespace1 (skip-whitespace :skip skip
+                                       :write-through write-through)))
+    (cond ((eql (peek-char*) #\=)
+            (read-char)
+            (when write-through
+              (write-char #\=))
+            (let ((whitespace2 (skip-whitespace :skip skip :write-through write-through)))
+              (multiple-value-bind (value delimiter)
+                  (read-delimited-string :skip skip :write-through write-through)
+                (let ((delimiter-string (if delimiter (string delimiter) "")))
+                  (if skip
+                    nil
+                    (values name value
+                            (concatenate 'string
+                                         name whitespace1 "=" whitespace2
+                                         delimiter-string value delimiter-string)))))))
+          (t (if skip
+               nil
+               (values name nil
+                       (concatenate 'string name whitespace1)))))))
+
+(defun skip-comment ()
+  "Skip SGML comment from *STANDARD-INPUT*, i.e. a string enclosed in
+'--' on both sides. Returns no values. Writes all characters read to
+*STANDARD-OUTPUT*. This function assumes \(without checking) that the
+current position of *STANDARD-INPUT* is at the beginning of a comment,
+after the first hyphen - see COMMENT-START-P."
+  (read-char)
+  (write-string "--")
+  (read-until "--")
+  (values))
+
diff --git a/deps/hunchentoot/util.lisp b/deps/hunchentoot/util.lisp
new file mode 100644 (file)
index 0000000..5d53814
--- /dev/null
@@ -0,0 +1,379 @@
+;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: HUNCHENTOOT; Base: 10 -*-
+
+;;; Copyright (c) 2004-2010, Dr. Edmund Weitz. All rights reserved.
+
+;;; Redistribution and use in source and binary forms, with or without
+;;; modification, are permitted provided that the following conditions
+;;; are met:
+
+;;;   * Redistributions of source code must retain the above copyright
+;;;     notice, this list of conditions and the following disclaimer.
+
+;;;   * Redistributions in binary form must reproduce the above
+;;;     copyright notice, this list of conditions and the following
+;;;     disclaimer in the documentation and/or other materials
+;;;     provided with the distribution.
+
+;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
+;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(in-package :hunchentoot)
+
+
+(defun starts-with-p (seq subseq &key (test 'eql))
+  "Tests whether the sequence SEQ starts with the sequence
+SUBSEQ.  Individual elements are compared with TEST."
+  (let* ((length (length subseq))
+         (mismatch (mismatch subseq seq
+                             :test test)))
+    (or (null mismatch)
+        (<= length mismatch))))
+
+(defun starts-with-one-of-p (seq subseq-list &key (test 'eql))
+  "Tests whether the sequence SEQ starts with one of the
+sequences in SUBSEQ-LIST.  Individual elements are compared with
+TEST."
+  (some (lambda (subseq)
+          (starts-with-p seq subseq :test test))
+        subseq-list))
+
+(defun create-random-string (&optional (n 10) (base 16))
+  "Returns a random number \(as a string) with base BASE and N
+digits."
+  (with-output-to-string (s)
+    (dotimes (i n)
+      (format s "~VR" base
+              (random base *the-random-state*)))))
+
+(defun reason-phrase (return-code)
+  "Returns a reason phrase for the HTTP return code RETURN-CODE \(which
+should be an integer) or NIL for return codes Hunchentoot doesn't know."
+  (gethash return-code *http-reason-phrase-map* 
+           "No reason phrase known"))
+
+(defgeneric assoc* (thing alist)
+  (:documentation "Similar to CL:ASSOC, but 'does the right thing' if
+THING is a string or a symbol.")
+  (:method ((thing symbol) alist)
+   (assoc thing alist :test #'eq))
+  (:method ((thing string) alist)
+   (assoc thing alist :test #'string-equal))
+  (:method (thing alist)
+   (assoc thing alist :test #'eql)))
+
+(defun md5-hex (string)
+  "Calculates the md5 sum of the string STRING and returns it as a hex string."
+  (with-output-to-string (s)
+    (loop for code across (md5:md5sum-string string)
+         do (format s "~2,'0x" code))))
+
+(defun escape-for-html (string)
+  "Escapes the characters #\\<, #\\>, #\\', #\\\", and #\\& for HTML
+output."
+  (with-output-to-string (out)
+    (with-input-from-string (in string)
+      (loop for char = (read-char in nil nil)
+            while char
+            do (case char
+                 ((#\<) (write-string "&lt;" out))
+                 ((#\>) (write-string "&gt;" out))
+                 ((#\") (write-string "&quot;" out))
+                 ((#\') (write-string "&#039;" out))
+                 ((#\&) (write-string "&amp;" out))
+                 (otherwise (write-char char out)))))))
+
+(defun http-token-p (token)
+  "This function tests whether OBJECT is a non-empty string which is a
+TOKEN according to RFC 2068 \(i.e. whether it may be used for, say,
+cookie names)."
+  (and (stringp token)
+       (plusp (length token))
+       (every (lambda (char)
+                (and ;; CHAR is US-ASCII but not control character or ESC
+                     (< 31 (char-code char) 127)
+                     ;; CHAR is not 'tspecial'
+                     (not (find char "()<>@,;:\\\"/[]?={} " :test #'char=))))
+              token)))
+
+
+(defun rfc-1123-date (&optional (time (get-universal-time)))
+  "Generates a time string according to RFC 1123. Default is current time.
+This can be used to send a 'Last-Modified' header - see
+HANDLE-IF-MODIFIED-SINCE."
+  (multiple-value-bind
+        (second minute hour date month year day-of-week)
+      (decode-universal-time time 0)
+    (format nil "~A, ~2,'0d ~A ~4d ~2,'0d:~2,'0d:~2,'0d GMT"
+            (svref +day-names+ day-of-week)
+            date
+            (svref +month-names+ (1- month))
+            year
+            hour
+            minute
+            second)))
+
+(defun iso-time (&optional (time (get-universal-time)))
+  "Returns the universal time TIME as a string in full ISO format."
+  (multiple-value-bind (second minute hour date month year)
+      (decode-universal-time time)
+    (format nil "~4,'0d-~2,'0d-~2,'0d ~2,'0d:~2,'0d:~2,'0d"
+            year month date hour minute second)))
+
+(let ((counter 0))
+  (declare (ignorable counter))
+  (defun make-tmp-file-name (&optional (prefix "hunchentoot"))
+    "Generates a unique name for a temporary file.  This function is
+called from the RFC2388 library when a file is uploaded."
+    (let ((tmp-file-name
+           #+:allegro
+           (pathname (system:make-temp-file-name prefix *tmp-directory*))
+           #-:allegro
+           (loop for pathname = (make-pathname :name (format nil "~A-~A"
+                                                             prefix (incf counter))
+                                               :type nil
+                                               :defaults *tmp-directory*)
+                 unless (probe-file pathname)
+                 return pathname)))
+      (push tmp-file-name *tmp-files*)
+      ;; maybe call hook for file uploads
+      (when *file-upload-hook*
+        (funcall *file-upload-hook* tmp-file-name))
+      tmp-file-name)))
+
+(defun quote-string (string)
+  "Quotes string according to RFC 2616's definition of `quoted-string'."
+  (with-output-to-string (out)
+    (with-input-from-string (in string)
+      (loop for char = (read-char in nil nil)
+            while char
+            unless (or (char< char #\Space)
+                       (char= char #\Rubout))
+              do (case char
+                   ((#\\) (write-string "\\\\" out))
+                   ((#\") (write-string "\\\"" out))
+                   (otherwise (write-char char out)))))))
+
+(defmacro upgrade-vector (vector new-type &key converter)
+  "Returns a vector with the same length and the same elements as
+VECTOR \(a variable holding a vector) but having element type
+NEW-TYPE.  If CONVERTER is not NIL, it should designate a function
+which will be applied to each element of VECTOR before the result is
+stored in the new vector.  The resulting vector will have a fill
+pointer set to its end.
+
+The macro also uses SETQ to store the new vector in VECTOR."
+  `(setq ,vector
+         (loop with length = (length ,vector)
+               with new-vector = (make-array length
+                                             :element-type ,new-type
+                                             :fill-pointer length)
+               for i below length
+               do (setf (aref new-vector i) ,(if converter
+                                               `(funcall ,converter (aref ,vector i))
+                                               `(aref ,vector i)))
+               finally (return new-vector))))
+
+(defun ensure-parse-integer (string &key (start 0) end (radix 10))
+  (let ((end (or end (length string))))
+    (if (or (>= start (length string))
+            (> end (length string)))
+        (error 'bad-request)
+        (multiple-value-bind (integer stopped)
+            (parse-integer string :start start :end end :radix radix :junk-allowed t)
+          (if (/= stopped end)
+              (error 'bad-request)
+              integer)))))
+
+(defun url-decode (string &optional (external-format *hunchentoot-default-external-format*))
+  "Decodes a URL-encoded string which is assumed to be encoded using the
+external format EXTERNAL-FORMAT, i.e. this is the inverse of
+URL-ENCODE. It is assumed that you'll rarely need this function, if
+ever. But just in case - here it is. The default for EXTERNAL-FORMAT is
+the value of *HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*."
+  (when (zerop (length string))
+    (return-from url-decode ""))
+  (let ((vector (make-array (length string) :element-type 'octet :fill-pointer 0))
+        (i 0)
+        unicodep)
+    (loop
+      (unless (< i (length string))
+        (return))
+      (let ((char (aref string i)))
+       (labels ((decode-hex (length)
+                  (ensure-parse-integer string :start i :end (incf i length)
+                                               :radix 16))
+                (push-integer (integer)
+                  (vector-push integer vector))
+                (peek ()
+                  (if (array-in-bounds-p string i)
+                      (aref string i)
+                      (error 'bad-request)))
+                (advance ()
+                  (setq char (peek))
+                  (incf i)))
+         (cond
+          ((char= #\% char)
+           (advance)
+           (cond
+            ((char= #\u (peek))
+             (unless unicodep
+               (setq unicodep t)
+               (upgrade-vector vector '(integer 0 65535)))
+             (advance)
+             (push-integer (decode-hex 4)))
+            (t
+             (push-integer (decode-hex 2)))))
+          (t
+           (push-integer (char-code (case char
+                                      ((#\+) #\Space)
+                                      (otherwise char))))
+           (advance))))))
+    (cond (unicodep
+           (upgrade-vector vector 'character :converter #'code-char))
+          (t (octets-to-string vector :external-format external-format)))))
+
+(defun form-url-encoded-list-to-alist (form-url-encoded-list
+                                       &optional (external-format *hunchentoot-default-external-format*))
+  "Converts a list FORM-URL-ENCODED-LIST of name/value pairs into an
+alist.  Both names and values are url-decoded while doing this."
+  (mapcar #'(lambda (entry)
+              (destructuring-bind (name &optional value)
+                  (split "=" entry :limit 2)
+                (cons (string-trim " " (url-decode name external-format))
+                      (url-decode (or value "") external-format))))
+          form-url-encoded-list))
+
+(defun cookies-to-alist (cookies)
+  "Converts a list of cookies of the form \"key=value\" to an alist.  No
+  character set processing is done."
+  (mapcar #'(lambda (entry)
+              (destructuring-bind (name &optional value)
+                  (split "=" entry :limit 2)
+                (cons (string-trim " " name) (or value ""))))
+          cookies))
+
+(defun url-encode (string &optional (external-format *hunchentoot-default-external-format*))
+  "URL-encodes a string using the external format EXTERNAL-FORMAT. The
+default for EXTERNAL-FORMAT is the value of
+*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*."
+  (with-output-to-string (s)
+    (loop for c across string
+          for index from 0
+          do (cond ((or (char<= #\0 c #\9)
+                        (char<= #\a c #\z)
+                        (char<= #\A c #\Z)
+                        ;; note that there's no comma in there - because of cookies
+                        (find c "$-_.!*'()" :test #'char=))
+                     (write-char c s))
+                   (t (loop for octet across (string-to-octets string
+                                                               :start index
+                                                               :end (1+ index)
+                                                               :external-format external-format)
+                            do (format s "%~2,'0x" octet)))))))
+
+(defun parse-content-type (content-type-header)
+  "Reads and parses a `Content-Type' header and returns it as three
+values - the type, the subtype, and the requests' character set as
+specified in the 'charset' parameter in the header, if there is one
+and if the content type is \"text\".  CONTENT-TYPE-HEADER is supposed
+to be the corresponding header value as a string."
+  (with-input-from-sequence (stream (map 'list 'char-code content-type-header))
+    (with-character-stream-semantics
+     (let* ((*current-error-message* (format nil "Corrupted Content-Type header ~S:" content-type-header))
+            (type (read-token stream))
+            (subtype (if (eql #\/ (read-char* stream nil))
+                       (read-token stream)
+                       (return-from parse-content-type
+                         ;; try to return something meaningful
+                         (values "application" "octet-stream" nil))))
+            (parameters (read-name-value-pairs stream))
+            (charset (cdr (assoc "charset" parameters :test #'string=)))
+            (charset
+             (when (string-equal type "text")
+               charset)))
+       (values type subtype charset)))))
+
+(defun keep-alive-p (request)
+  "Returns a true value unless the incoming request's headers or the
+server's PERSISTENT-CONNECTIONS-P setting obviate a keep-alive reply.
+The second return value denotes whether the client has explicitly
+asked for a persistent connection."
+  (let ((connection-values
+         ;; the header might consist of different values separated by commas
+         (when-let (connection-header (header-in :connection request))
+           (split "\\s*,\\s*" connection-header))))
+    (flet ((connection-value-p (value)
+             "Checks whether the string VALUE is one of the
+values of the `Connection' header."
+             (member value connection-values :test #'string-equal)))
+      (let ((keep-alive-requested-p (connection-value-p "keep-alive")))
+        (values (and (acceptor-persistent-connections-p *acceptor*)
+                     (or (and (eq (server-protocol request) :http/1.1)
+                              (not (connection-value-p "close")))
+                         (and (eq (server-protocol request) :http/1.0)
+                              keep-alive-requested-p)))
+                keep-alive-requested-p)))))
+
+(defun address-string ()
+  "Returns a string with information about Hunchentoot suitable for
+inclusion in HTML output."
+  (flet ((escape-for-html (arg)
+           (if arg
+               (escape-for-html arg)
+               arg)))
+    (format nil "<address><a href='http://weitz.de/hunchentoot/'>Hunchentoot ~A</a> <a href='~A'>(~A ~A)</a>~@[ at ~A~:[ (port ~D)~;~]~]</address>"
+            *hunchentoot-version*
+            +implementation-link+
+            (escape-for-html (lisp-implementation-type))
+            (escape-for-html (lisp-implementation-version))
+            (escape-for-html (or (host *request*) (acceptor-address *acceptor*)))
+            (scan ":\\d+$" (or (host *request*) ""))
+            (acceptor-port *acceptor*))))
+
+(defun input-chunking-p ()
+  "Whether input chunking is currently switched on for
+*HUNCHENTOOT-STREAM* - note that this will return NIL if the stream
+not a chunked stream."
+  (chunked-stream-input-chunking-p *hunchentoot-stream*))
+
+(defun ssl-p (&optional (acceptor *acceptor*))
+  "Whether the current connection to the client is secure. See
+ACCEPTOR-SSL-P."
+  (acceptor-ssl-p acceptor))
+
+(defmacro with-mapped-conditions (() &body body)
+  "Run BODY with usocket condition mapping in effect, i.e. platform specific network errors will be
+  signalled as usocket conditions.  For Lispworks, no mapping is performed."
+  #+:lispworks
+  `(progn ,@body)
+  #-:lispworks
+  `(usocket:with-mapped-conditions ()
+    ,@body))
+
+(defmacro with-conditions-caught-and-logged (() &body body)
+  "Run BODY with conditions caught and logged by the *ACCEPTOR*. Errors are
+stopped right away so no other part of the software is impacted by them."
+  `(block nil
+     (handler-bind
+         ((error
+           ;; abort if there's an error which isn't caught inside
+           (lambda (cond)
+             (log-message* *lisp-errors-log-level*
+                           "Error while processing connection: ~A" cond)
+             (return)))
+          (warning
+           ;; log all warnings which aren't caught inside
+           (lambda (cond)
+             (when *log-lisp-warnings-p*
+               (log-message* *lisp-warnings-log-level*
+                             "Warning while processing connection: ~A" cond)))))
+       ,@body)))
diff --git a/deps/hunchentoot/www/errors/404.html b/deps/hunchentoot/www/errors/404.html
new file mode 100644 (file)
index 0000000..7de947e
--- /dev/null
@@ -0,0 +1,9 @@
+<html>
+  <head>
+    <title>Not found</title>
+  </head>
+  <body>
+    Resource ${script-name} not found.
+    <img src="/img/made-with-lisp-logo.jpg" width="300" height="100"/>
+  </body>
+</html>
diff --git a/deps/hunchentoot/www/errors/500.html b/deps/hunchentoot/www/errors/500.html
new file mode 100644 (file)
index 0000000..f91b1bf
--- /dev/null
@@ -0,0 +1,18 @@
+<html>
+  <head>
+    <title>Internal Server Error</title>
+  </head>
+  <body>
+    <h1>Internal Server Error</h1>
+    An error occurred while processing your ${script-name} request.
+    <hr/>
+    <h1>Error Message</h1>
+<pre>${error}</pre>
+    <h1>Backtrace</h1>
+<pre>${backtrace}</pre>
+    <hr/>
+<a href="http://weitz.de/hunchentoot">Hunchentoot</a> ${hunchentoot-version} running on ${lisp-implementation-type} ${lisp-implementation-version}
+    <hr/>
+    <img src="/img/made-with-lisp-logo.jpg" width="300" height="100"/>
+  </body>
+</html>
diff --git a/deps/hunchentoot/www/favicon.ico b/deps/hunchentoot/www/favicon.ico
new file mode 100644 (file)
index 0000000..c956daf
Binary files /dev/null and b/deps/hunchentoot/www/favicon.ico differ
diff --git a/deps/hunchentoot/www/hunchentoot-doc.html b/deps/hunchentoot/www/hunchentoot-doc.html
new file mode 100644 (file)
index 0000000..227f92c
--- /dev/null
@@ -0,0 +1,4237 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Strict//EN">
+<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta><title>Hunchentoot - The Common Lisp web server formerly known as TBNL</title><meta name="description" content="
+    A full-featured web server written in Common Lisp offering things
+    like HTTP/1.1 chunking, persistent connections, and SSL.  Includes
+    a framework for building dynamic websites interactively.
+  "></meta><style type="text/css">
+  body { background-color: #ffffff }
+  pre { padding:5px; background-color:#e0e0e0 }
+  pre.none { padding:5px; background-color:#ffffff }
+  h3, h4, h5 { text-decoration: underline; }
+  .entry-type { padding-left: 1em; font-size: 60%; font-style: italic }
+  a { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:visited { text-decoration: none; padding: 1px 2px 1px 2px; }
+  a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; } 
+  a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; }
+  a.none { text-decoration: none; padding: 0; }
+  a.none:visited { text-decoration: none; padding: 0; } 
+  a.none:hover { text-decoration: none; border: none; padding: 0; } 
+  a.none:focus { text-decoration: none; border: none; padding: 0; } 
+  a.noborder { text-decoration: none; padding: 0; } 
+  a.noborder:visited { text-decoration: none; padding: 0; } 
+  a.noborder:hover { text-decoration: none; border: none; padding: 0; } 
+  a.noborder:focus { text-decoration: none; border: none; padding: 0; }  
+        </style></head><body>
+  
+  
+
+  <h2>
+    <a href="http://www.htg1.de/hunchentoot/hunchentoot.html" title="Click here for the Hunchentoot logo" class="noborder">
+      <img align="top" width="93" height="45" border="0" src="hunchentoot.gif"></img>
+    </a>
+    Hunchentoot - The Common Lisp web server formerly known as TBNL
+  </h2>
+
+  <blockquote>
+    <h3 xmlns=""><a class="none" name="abstract">Abstract</a></h3>
+      <p>
+        Hunchentoot is a web server written in Common Lisp and at the
+        same time a toolkit for building dynamic websites.  As a
+        stand-alone web server, Hunchentoot is capable of HTTP/1.1
+        chunking (both directions), persistent connections
+        (keep-alive), and SSL.
+      </p>
+      <p>
+        Hunchentoot provides facilities like automatic session
+        handling (with and without cookies), logging, customizable
+        error handling, and easy access to GET and POST parameters
+        sent by the client. It does <em>not</em> include functionality
+        to programmatically generate HTML output. For this task you
+        can use any library you like, e.g. (shameless self-plug)
+        <a href="http://weitz.de/cl-who/">CL-WHO</a> or
+        <a href="http://weitz.de/html-template/">HTML-TEMPLATE</a>.
+      </p>
+      <p>
+        Hunchentoot talks with its front-end or with the client over
+        TCP/IP sockets and optionally uses multiprocessing to handle
+        several requests at the same time.  Therefore, it cannot be
+        implemented completely in <a href="http://www.lispworks.com/documentation/HyperSpec/Front/index.htm">portable
+        Common Lisp</a>.  It currently works with <a href="http://www.lispworks.com/">LispWorks</a> and all Lisps
+        which are supported by the compatibility layers <a href="http://common-lisp.net/project/usocket/">usocket</a> and
+        <a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux
+        Threads</a>.
+      </p>
+      <p>
+        Hunchentoot comes with a
+        <a href="http://www.opensource.org/licenses/bsd-license.php">BSD-style
+        license</a> so you can basically do with it whatever you want.
+      </p>
+      <p>
+        Hunchentoot is (or was) for example used by
+        <a href="http://quickhoney.com/">QuickHoney</a>,
+        <a href="http://www.city-farming.de/">City Farming</a>,
+        <a href="http://heikestephan.de/">Heike Stephan</a>.
+      </p>
+      <p>
+        <font color="red">Download shortcut:</font>
+        <a href="http://weitz.de/files/hunchentoot.tar.gz">http://weitz.de/files/hunchentoot.tar.gz</a>.
+      </p>
+    
+  </blockquote>
+
+  <h3 xmlns=""><a class="none" name="contents">Contents</a></h3>
+  <ol xmlns="">
+<li><a href="#abstract">Abstract</a></li>
+<li><a href="#contents">Contents</a></li>
+<li>
+<a href="#install">Download and installation</a><ol>
+<li><a href="#port80">Running Hunchentoot on port 80</a></li>
+<li><a href="#proxy">Hunchentoot behind a proxy</a></li>
+</ol>
+</li>
+<li><a href="#support">Support</a></li>
+<li><a href="#teen-age">Your own webserver (the easy teen-age New York version)</a></li>
+<li><a href="#extras">Third party documentation and add-ons</a></li>
+<li>
+<a href="#reference">Function and variable reference</a><ol>
+<li><a href="#acceptors">Acceptors</a></li>
+<li><a href="#acceptor-behaviour">Customizing acceptor behaviour</a></li>
+<li><a href="#subclassing-acceptors">An example of how to subclass ACCEPTOR</a></li>
+<li><a href="#taskmasters">Taskmasters</a></li>
+<li><a href="#request-dispatch">Request dispatch and handling</a></li>
+<li><a href="#easy-handlers">Using the easy-handler framework</a></li>
+<li><a href="#requests">Request objects</a></li>
+<li><a href="#replies">Reply objects</a></li>
+<li><a href="#sessions">Sessions</a></li>
+<li><a href="#session-behaviour">Customizing session behaviour</a></li>
+<li><a href="#cookies">Cookies</a></li>
+<li><a href="#logging">Logging</a></li>
+<li><a href="#conditions">Conditions and error handling</a></li>
+<li><a href="#misc">Miscellaneous</a></li>
+</ol>
+</li>
+<li><a href="#testing">Testing</a></li>
+<li><a href="#debugging">Debugging</a></li>
+<li><a href="#history">History</a></li>
+<li><a href="#index">Symbol index</a></li>
+<li><a href="#ack">Acknowledgements</a></li>
+</ol>
+
+  <h3 xmlns=""><a class="none" name="install">Download and installation</a></h3>
+    Hunchentoot depends on a couple of other Lisp libraries which you'll need
+    to install first:
+    <ul>
+      <li>Pierre R. Mai's <a href="http://www.cliki.net/md5">MD5</a>,</li>
+      <li>Kevin Rosenberg's <a href="http://www.cliki.net/cl-base64">CL-BASE64</a>,</li>
+      <li>Janis Dzerins' <a href="http://common-lisp.net/project/rfc2388/">RFC2388</a>,</li>
+      <li>Peter Seibel's <a href="http://weitz.de/cl-fad/">CL-FAD</a>,</li>
+      <li>Gary King's <a href="http://common-lisp.net/project/trivial-backtrace/">trivial-backtrace</a>,</li>
+      <li>Erik Huelsmann's <a href="http://common-lisp.net/project/usocket">usocket</a> (unless you're using LispWorks),</li>
+      <li>Greg Pfeil's <a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux Threads</a> (unless you're using LispWorks),
+      </li>
+      <li>
+        David Lichteblau's <a href="http://common-lisp.net/project/cl-plus-ssl/">CL+SSL</a>
+        (unless you're using LispWorks),
+      </li>
+      <li>
+        and my own <a href="http://weitz.de/flexi-streams/">FLEXI-STREAMS</a> (0.12.0 or higher),
+        <a href="http://weitz.de/chunga/">Chunga</a> (1.0.0 or
+        higher), and <a href="http://weitz.de/cl-ppcre/">
+        CL-PPCRE</a> (plus
+        <a href="http://weitz.de/cl-who/">CL-WHO</a> for the <a href="#teen-age">example code</a>
+        and <a href="http://weitz.de/drakma/">Drakma</a> for the <a href="#testing">tests</a>).
+      </li>
+    </ul>
+
+    Make sure to use the <em>newest</em> versions of all of these
+    libraries (which might themselves depend on other libraries) - try
+    the repository versions if you're in doubt.  Note: You can compile
+    Hunchentoot without SSL support - and thus without the need to
+    have CL+SSL - if you add <code>:HUNCHENTOOT-NO-SSL</code> to
+    <a href="http://www.lispworks.com/documentation/HyperSpec/Body/v_featur.htm">
+      <code>*FEATURES*</code></a> <em>before</em> you compile it.
+    <p>
+      Hunchentoot will only work with Lisps where
+      the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#character_code">character
+      codes</a> of
+      all <a href="http://en.wikipedia.org/wiki/ISO/IEC_8859-1">Latin-1</a>
+      characters coincide with their
+      Unicode <a href="http://en.wikipedia.org/wiki/Code_point">code
+      points</a> (which is the case for all current implementations I
+      know).
+    </p>
+    <p>
+      Hunchentoot itself together with this documentation can be
+      downloaded from
+      <a href="https://github.com/edicl/hunchentoot/archive/v1.2.38.tar.gz">https://github.com/edicl/hunchentoot/archive/v1.2.38.tar.gz</a>.
+      The current version is 1.2.38.
+    </p>
+    <p>
+      The preferred method to compile and load Hunchentoot is via <a href="http://www.cliki.net/asdf">ASDF</a>.  If you want to avoid
+      downloading and installing all the dependencies manually, give
+      Zach Beane's excellent <a href="http://www.quicklisp.org/">Quicklisp</a> system a try.
+    </p>
+    <p>
+      Hunchentoot and its dependencies can also be installed with <a href="http://common-lisp.net/project/clbuild/">clbuild</a>.
+      There's also a port for <a href="http://www.gentoo.org/proj/en/lisp/common-lisp/index.xml">Gentoo
+      Linux</a> thanks to Matthew Kennedy.
+    </p>
+    <p>
+      The current development version of Hunchentoot can be found
+      at <a href="https://github.com/edicl/hunchentoot">https://github.com/edicl/hunchentoot</a>.
+      If you want to send patches, please fork the github repository and send pull requests.
+    </p>
+
+    <h4 xmlns=""><a name="port80">Running Hunchentoot on port 80</a></h4>
+
+      Hunchentoot does not come with code to help with running it on a
+      privileged port (i.e. port 80 or 443) on Unix-like operating
+      systems.  Modern Unix-like systems have specific, non-portable
+      ways to allow non-root users to listen to privileged ports, so
+      including such functionality in Hunchentoot was considered
+      unnecessary.  Please refer to online resources for help.  At the
+      time of this writing, the YAWS documentation has a <a href="http://yaws.hyber.org/privbind.yaws">comprehensive
+      writeup</a> on the topic.
+    
+
+    <h4 xmlns=""><a name="proxy">Hunchentoot behind a proxy</a></h4>
+
+      If you're feeling unsecure about exposing Hunchentoot to the wild,
+      wild Internet or if your Lisp web application is part of a larger
+      website, you can hide it behind a
+      <a href="http://en.wikipedia.org/wiki/Proxy_server">proxy server</a>.
+      One approach that I have used several times is to employ Apache's
+      <a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html">mod_proxy</a>
+      module with a configuration that looks like this:
+
+<pre><a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypass" class="noborder">ProxyPass</a> /hunchentoot http://127.0.0.1:3000/hunchentoot
+<a href="http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypassreverse" class="noborder">ProxyPassReverse</a> /hunchentoot http://127.0.0.1:3000/hunchentoot</pre>
+
+      This will tunnel all requests where the URI path begins with
+      <code>"/hunchentoot"</code> to a (Hunchentoot) server listening on
+      port 3000 on the same machine.
+
+      <p>
+        Of course, there are
+        <a href="http://www.red-bean.com/pipermail/lispweb/2006-October/001342.html">several
+          other</a> (more lightweight) web proxies that you could use
+        instead of Apache.
+      </p>
+    
+  
+
+  <h3 xmlns=""><a class="none" name="support">Support</a></h3>
+    <p>
+      The development version of Hunchentoot can be found <a href="https://github.com/edicl/hunchentoot" target="_new">on
+      github</a>.  Please use the github issue tracking system to
+      submit bug reports.  Patches are welcome, please use <a href="https://github.com/edicl/hunchentoot/pulls">GitHub pull
+      requests</a>.  If you want to make a change, please <a href="http://weitz.de/patches.html" target="_new">read this
+      first</a>.
+    </p>
+  
+
+  <h3 xmlns=""><a class="none" name="teen-age">Your own webserver (the easy teen-age New York version)</a></h3>
+    Starting your own web server is pretty easy.  Do something like this:
+<pre>(hunchentoot:<a class="noborder" href="#teen-age">start</a> (make-instance 'hunchentoot:<a class="noborder" href="#acceptor">easy-acceptor</a> :port 4242))</pre>
+    That's it.  Now you should be able to enter the address
+    "<a href="http://127.0.0.1:4242/"><code>http://127.0.0.1:4242/</code></a>" in
+    your browser and see something, albeit nothing very interesting
+    for now.
+
+    <p>
+      By default, Hunchentoot serves the files from the
+      <code><i>www/</i></code> directory in its source tree.  In the
+      distribution, that directory contains a HTML version of the
+      documentation as well as the error templates.  The location of
+      the document root directory can be specified when creating a new
+      <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> instance by the way of the
+      <code xmlns=""><a href="#acceptor-document-root">ACCEPTOR-DOCUMENT-ROOT</a></code>.  Likewise, the
+      location of the error template directory can be specified by the
+      <code xmlns=""><a href="#acceptor-error-template-directory">ACCEPTOR-ERROR-TEMPLATE-DIRECTORY</a></code>.  Both
+      <code xmlns=""><a href="#acceptor-document-root">ACCEPTOR-DOCUMENT-ROOT</a></code> and
+      <code xmlns=""><a href="#acceptor-error-template-directory">ACCEPTOR-ERROR-TEMPLATE-DIRECTORY</a></code> can be
+      specified using a logical pathname, which will be translated
+      once when the <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> is instantiated.
+    </p>
+
+    <p>
+      The <code xmlns=""><a href="#easy-acceptor">EASY-ACCEPTOR</a></code> class implements a
+      framework for developing web applications.  Handlers are defined
+      using the <code xmlns=""><a href="#define-easy-handler">DEFINE-EASY-HANDLER</a></code> macro.
+      Request dispatching is performed according to the list of
+      dispatch functions in <code xmlns=""><a href="#*dispatch-table*">*DISPATCH-TABLE*</a></code>.
+      Each of the functions on that list is called to determine
+      whether it wants to handle the request, provided as single
+      argument.  If a dispatcher function wants to handle the request,
+      it returns another function to actually create the desired page.
+    </p>
+
+    <p>
+      <code xmlns=""><a href="#define-easy-handler">DEFINE-EASY-HANDLER</a></code> is accompanied by a set
+      of dispatcher creation functions that can be used to create
+      dispatchers for standard tasks.  These are documented in the <a class="noborder" href="#easy-handlers">subchapter on easy
+      handlers</a>
+    </p>
+
+    <p>
+      Now be a bit more adventurous, try this
+<pre>(hunchentoot:<a class="noborder" href="#define-easy-handler">define-easy-handler</a> (say-yo :uri "/yo") (name)
+  (setf (hunchentoot:<a class="noborder" href="#content-type*">content-type*</a>) "text/plain")
+  (format nil "Hey~@[ ~A~]!" name))</pre>
+      and see what happens at "<a href="http://127.0.0.1:4242/yo"><code>http://127.0.0.1:4242/yo</code></a>" or
+      "<a href="http://127.0.0.1:4242/yo?name=Dude"><code>http://127.0.0.1:4242/yo?name=Dude</code></a>" .
+    </p>
+
+    <p>
+    Hunchentoot comes with a little example website which you can use
+    to see if it works and which should also demonstrate a couple of
+    the things you can do with Hunchentoot.  To start the example
+    website, enter the following code into your listener:
+
+<pre>(<a class="noborder" href="http://common-lisp.net/~mmommer/asdf-howto.shtml#sec11">asdf:oos</a> 'asdf:load-op :hunchentoot-test)</pre>
+
+    Now go to "<a href="http://127.0.0.1:4242/hunchentoot/test"><code>http://127.0.0.1:4242/hunchentoot/test</code></a>" and play a bit.
+    </p>
+  
+
+  <h3 xmlns=""><a class="none" name="extras">Third party documentation and add-ons</a></h3>
+    <p>
+      Adam Petersen has written a book called <a href="http://www.adampetersen.se/articles/lispweb.htm">"Lisp for
+      the Web"</a> which explains how Hunchentoot and some other
+      libraries can be used to build web sites.
+    </p>
+    <p>
+      Here is some software which extends Hunchentoot or is based on it:
+    </p>
+    <ul>
+      <li>
+        <a href="http://weblocks-framework.info/">Weblocks</a> by
+        Slava Akhmechet is a "continuations-based web framework" which
+        is based on Hunchentoot.
+      </li>
+      <li>
+        <a href="https://github.com/slyrus/hunchentoot-cgi">hunchentoot-cgi</a>
+        (by Cyrus Harmon) provides
+        <a href="http://en.wikipedia.org/wiki/Common_Gateway_Interface">CGI</a>
+        handlers for Hunchentoot.
+      </li>
+      <li>
+        <a href="http://weitz.de/cl-webdav/">CL-WEBDAV</a> is a <a href="http://webdav.org/">WebDAV</a>
+        server based on Hunchentoot.
+      </li>
+      <li>
+        <a href="http://restas.lisper.ru/">RESTAS</a> is a web
+        framework based on Hunchentoot.
+      </li>
+    </ul>
+  
+
+  <h3 xmlns=""><a class="none" name="reference">Function and variable reference</a></h3>
+
+    <h4 xmlns=""><a name="acceptors">Acceptors</a></h4>
+
+      If you want Hunchentoot to actually do something, you have to create and
+      <a href="#teen-age">start</a> an <a href="#acceptor">acceptor</a>.
+      You can also run several acceptors in one image, each one
+      listening on a different different port.
+
+      <p xmlns=""><a class="none" name="acceptor"></a>
+      [Standard class]
+      <br><b>acceptor</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          To create a Hunchentoot webserver, you make an instance of
+          this class or one of its subclasses and use the generic
+          function <code><a href="#start">START</a></code> to start it (and
+          <code><a href="#stop">STOP</a></code> to stop it).  Use the
+          <code xmlns="http://www.w3.org/1999/xhtml">:port</code> initarg if you don't want to listen
+          on the default http port 80.  If 0 is specified for the
+          port, the system chooses a random port to listen on.  The
+          port number choosen can be retrieved using the
+          <code><a href="#acceptor-port">ACCEPTOR-PORT</a></code> accessor.  The port
+          number chosen is retained across stopping and starting the
+          acceptor.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            There are other initargs most of which you probably
+            won't need very often.  They are explained in detail
+            in the docstrings of the slot definitions.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Unless you are in a Lisp without MP capabilities, you can
+            have several active instances of
+            <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> (listening on different
+            ports) at the same time.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="ssl-acceptor"></a>
+      [Standard class]
+      <br><b>ssl-acceptor</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Create and <code><a href="#start">START</a></code> an instance of this class
+        (instead of <code><a href="#acceptor">ACCEPTOR</a></code>) if you want an https server.  There are two
+        required initargs, <code xmlns="http://www.w3.org/1999/xhtml">:SSL-CERTIFICATE-FILE</code> and <code xmlns="http://www.w3.org/1999/xhtml">:SSL-PRIVATEKEY-FILE</code>, for
+        pathname designators denoting the certificate file and the key file in
+        PEM format.  On LispWorks, you can have both in one file in which case
+        the second initarg is optional.  You can also use the
+        <code xmlns="http://www.w3.org/1999/xhtml">:SSL-PRIVATEKEY-PASSWORD</code> initarg to provide a password
+        (as a string) for the key file (or <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>, the default, for
+        no password).
+        <p xmlns="http://www.w3.org/1999/xhtml">
+          The default port for <code xmlns=""><a href="#ssl-acceptor">SSL-ACCEPTOR</a></code> instances is 443 instead of 80
+        </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="start"></a>
+          [Generic function]
+          <br><b>start</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Starts <code><i>acceptor</i></code> so that it begins accepting
+        connections.  Returns the acceptor.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="stop"></a>
+          [Generic function]
+          <br><b>stop</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor &amp;key soft</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Stops the <code><i>acceptor</i></code> so
+        that it no longer accepts requests.  If
+        <code><i>soft</i></code> is true, and there are any requests
+        in progress, wait until all requests are fully processed, but
+        meanwhile do not accept new requests.  Note that
+        <code><i>soft</i></code> must not be set when calling
+        <code><a href="#stop">stop</a></code> from within a request handler, as
+        that will deadlock.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*acceptor*"></a>
+      [Special variable]
+      <br><b>*acceptor*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">The current ACCEPTOR object in the context of a request.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="acceptor-listen-backlog"></a>
+          [Generic function]
+          <br><b>acceptor-listen-backlog</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">listen-backlog
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">number-of-pending-connections
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Number of pending connections allowed in the listen socket
+          before the kernel rejects further incoming connections.
+          Non-LispWorks only.
+        </clix:description></blockquote></p>
+
+      <p xmlns="">
+      [Generic readers]<br><a class="none" name="acceptor-address"></a><b>acceptor-address</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">address
+          </clix:returns></i><br><a class="none" name="acceptor-port"></a><b>acceptor-port</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">port
+          </clix:returns></i><br><a class="none" name="acceptor-read-timeout"></a><b>acceptor-read-timeout</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">read-timeout
+          </clix:returns></i><br><a class="none" name="acceptor-ssl-certificate-file"></a><b>acceptor-ssl-certificate-file</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">ssl-acceptor
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">ssl-certificate-file
+          </clix:returns></i><br><a class="none" name="acceptor-ssl-privatekey-file"></a><b>acceptor-ssl-privatekey-file</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">ssl-acceptor
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">ssl-privatekey-file
+          </clix:returns></i><br><a class="none" name="acceptor-ssl-privatekey-password"></a><b>acceptor-ssl-privatekey-password</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">ssl-acceptor
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">ssl-privatekey-password
+          </clix:returns></i><br><a class="none" name="acceptor-write-timeout"></a><b>acceptor-write-timeout</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">write-timeout
+          </clix:returns></i><br><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          These are readers for various slots of <code><a href="#acceptor">ACCEPTOR</a></code>
+          objects (and some of them obviously only make sense
+          for <code><a href="#ssl-acceptor">SSL-ACCEPTOR</a></code> objects).  See the docstrings of
+          these slots for more information and note that there are corresponding
+          initargs for all of them.
+        </clix:description></blockquote></p>
+
+      <p xmlns="">
+      [Generic accessors]<br><a class="none" name="acceptor-access-log-destination"></a><b>acceptor-access-log-destination</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">(or pathname null)
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-access-log-destination</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="acceptor-document-root"></a><b>acceptor-document-root</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">(or pathname null)
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-document-root</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="acceptor-error-template-directory"></a><b>acceptor-error-template-directory</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">(or pathname null)
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-error-template-directory</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="acceptor-input-chunking-p"></a><b>acceptor-input-chunking-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">input-chunking-p
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-input-chunking-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="acceptor-message-log-destination"></a><b>acceptor-message-log-destination</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">(or pathname null)
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-message-log-destination</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="acceptor-name"></a><b>acceptor-name</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">name
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-name</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="acceptor-output-chunking-p"></a><b>acceptor-output-chunking-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">output-chunking-p
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-output-chunking-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="acceptor-persistent-connections-p"></a><b>acceptor-persistent-connections-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">persistent-connections-p
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-persistent-connections-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="acceptor-reply-class"></a><b>acceptor-reply-class</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">reply-class
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-reply-class</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="acceptor-request-class"></a><b>acceptor-request-class</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">request-class
+          </clix:returns></i><br><tt>(setf (</tt><b>acceptor-request-class</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          These are accessors for various slots of <code><a href="#acceptor">ACCEPTOR</a></code>
+          objects.  See the docstrings of these slots for more information and
+          note that there are corresponding initargs for all of them.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="acceptor-ssl-p"></a>
+          [Generic function]
+          <br><b>acceptor-ssl-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">generalized-boolean
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Returns a true value if <code><i>acceptor</i></code> uses SSL
+        connections.  The default is to unconditionally return <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> and
+        subclasses of <code><a href="#acceptor">ACCEPTOR</a></code> must specialize this method to signal that
+        they're using secure connections - see the <code><a href="#ssl-acceptor">SSL-ACCEPTOR</a></code> class.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*default-connection-timeout*"></a>
+      [Special variable]
+      <br><b>*default-connection-timeout*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">The default connection timeout used when an
+        acceptor is reading from and writing to a socket stream.  Note that
+        some Lisps allow you to set different timeouts for reading and writing
+        and you can specify both values via initargs when you create
+        an <a xmlns="http://www.w3.org/1999/xhtml" href="#acceptors">acceptor</a>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="acceptor-remove-session"></a>
+          [Generic function]
+          <br><b>acceptor-remove-session</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function is called whenever a session in
+          <code><a href="#acceptor">ACCEPTOR</a></code> is being destroyed because of
+          a session timout or an explicit
+          <code><a href="#remove-session">REMOVE-SESSION</a></code> call.
+        </clix:description></blockquote></p>
+
+    
+
+    <h4 xmlns=""><a name="acceptor-behaviour">Customizing acceptor behaviour</a></h4>
+
+      If you want to modify what acceptors do, you should subclass
+      <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> (or <code xmlns=""><a href="#ssl-acceptor">SSL-ACCEPTOR</a></code>) and
+      specialize the generic functions that constitute their behaviour (see
+      example below).  The life of an acceptor looks like this: It is started
+      with the function <code xmlns=""><a href="#start">START</a></code> which immediately calls
+      <code xmlns=""><a href="#start-listening">START-LISTENING</a></code> and then applies the function
+      <code xmlns=""><a href="#execute-acceptor">EXECUTE-ACCEPTOR</a></code> to its <a href="#taskmasters">taskmaster</a>.  This function will eventually call
+      <code xmlns=""><a href="#accept-connections">ACCEPT-CONNECTIONS</a></code> which is responsible for setting
+      things up to wait for clients to connect.  For each incoming connection
+      which comes in, <code xmlns=""><a href="#handle-incoming-connection">HANDLE-INCOMING-CONNECTION</a></code> is applied
+      to the taskmaster which will either call
+      <code xmlns=""><a href="#process-connection">PROCESS-CONNECTION</a></code> directly, or will create a thread
+      to call it.  <code xmlns=""><a href="#process-connection">PROCESS-CONNECTION</a></code> calls
+      <code xmlns=""><a href="#initialize-connection-stream">INITIALIZE-CONNECTION-STREAM</a></code> before it does anything
+      else, then it selects and calls a function which handles the <a href="#requests">request</a>, and finally it sends the <a href="#replies">reply</a> to the client before it calls
+      <code xmlns=""><a href="#reset-connection-stream">RESET-CONNECTION-STREAM</a></code>.  If the connection is
+      persistent, this procedure is repeated (except for the intialization step)
+      in a loop until the connection is closed.  The acceptor is stopped with
+      <code xmlns=""><a href="#stop">STOP</a></code>.
+
+      <p>
+        If you just want to use the standard acceptors that come with
+        Hunchentoot, you don't need to know anything about the functions
+        listed in this section.
+      </p>
+
+      <p xmlns=""><a class="none" name="start-listening"></a>
+          [Generic function]
+          <br><b>start-listening</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Sets up a listen socket for the given acceptor and
+        enables it to listen to incoming connections.  This function is called
+        from the thread that starts the acceptor initially and may return
+        errors resulting from the listening operation (like 'address in use'
+        or similar).
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="accept-connections"></a>
+          [Generic function]
+          <br><b>accept-connections</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">nil
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">In a loop, accepts a connection and hands it over
+        to the acceptor's taskmaster for processing using
+        <code><a href="#handle-incoming-connection">HANDLE-INCOMING-CONNECTION</a></code>. On LispWorks, this
+        function returns immediately, on other Lisps it returns only once the
+        acceptor has been stopped.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="process-connection"></a>
+          [Generic function]
+          <br><b>process-connection</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor socket
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">nil
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function is called by the taskmaster when a new client
+          connection has been established.  Its arguments are the
+          <code><a href="#acceptor">ACCEPTOR</a></code> object and a LispWorks socket
+          handle or a usocket socket stream object in
+          <code><i>socket</i></code>.  It reads the request headers,
+          sets up the <a xmlns="http://www.w3.org/1999/xhtml" href="#requests">request</a> and <a xmlns="http://www.w3.org/1999/xhtml" href="#replies">reply</a> objects, and hands over to
+          <code><a href="#process-request">PROCESS-REQUEST</a></code> which calls
+          <code><a href="#handle-request">HANDLE-REQUEST</a></code> to select and call a
+          handler for the request and sends its reply to the client.
+          This is done in a loop until the stream has to be closed or
+          until a connection timeout occurs.  It is probably not a
+          good idea to re-implement this method until you really,
+          really know what you're doing.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Handlers may call to the
+            <code xmlns=""><a href="#detach-socket">DETACH-SOCKET</a></code> generic function to
+            indicate that no further requests should be handled on
+            the connection by Hunchentoot, and that responsibility for
+            the socket is assumed by third-party software.  This can
+            be used by specialized handlers that wish to hand over
+            connection polling or processing to functions outside of
+            Hunchentoot, i.e. for connection multiplexing or
+            implementing specialized client protocols.  Hunchentoot
+            will finish processing the request and the
+            <code xmlns=""><a href="#process-connection">PROCESS-CONNECTION</a></code> function will
+            return without closing the connection.  At that point,
+            the acceptor may interact with the socket in whatever
+            fashion required.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="detach-socket"></a>
+          [Generic function]
+          <br><b>detach-socket</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">stream
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Indicate to Hunchentoot that it should stop serving requests
+          on the current request's socket.  Hunchentoot will finish
+          processing the current request and then return from
+          <code><a href="#process-connection">PROCESS-CONNECTION</a></code> without closing the
+          connection to the client.
+          <code><a href="#detach-socket">DETACH-SOCKET</a></code> can only be called from
+          within a request handler function.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="initialize-connection-stream"></a>
+          [Generic function]
+          <br><b>initialize-connection-stream</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor stream
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">stream
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Can be used to modify the stream which is used to
+          communicate between client and server before the request is
+          read.  The default method of <code><a href="#acceptor">ACCEPTOR</a></code>
+          does nothing, but see for example the method defined for
+          <code><a href="#ssl-acceptor">SSL-ACCEPTOR</a></code>.  All methods of this
+          generic function <em xmlns="http://www.w3.org/1999/xhtml">must</em> return the stream to use.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="reset-connection-stream"></a>
+          [Generic function]
+          <br><b>reset-connection-stream</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor stream
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">stream
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Resets the stream which is used to communicate
+          between client and server after one request has been served so that it
+          can be used to process the next request.  This generic function is
+          called after a request has been processed and <em xmlns="http://www.w3.org/1999/xhtml">must</em> return the
+          stream.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="acceptor-log-access"></a>
+          [Generic function]
+          <br><b>acceptor-log-access</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor &amp;key return-code</clix:lambda-list></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Function to call to log access to the acceptor.  The
+          <code><i>return-code</i></code> keyword argument contains additional
+          information about the request to log.  In addition, it can use the
+          standard request and reply accessor functions that are available to
+          handler functions to find out more information about the request.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="acceptor-log-message"></a>
+          [Generic function]
+          <br><b>acceptor-log-message</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor log-level format-string &amp;rest format-arguments</clix:lambda-list></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Function to call to log messages by the <code><i>acceptor</i></code>.  It must accept
+          a severity level for the message, which will be one of :ERROR, :INFO,
+          or :WARNING, a format string and an arbitary number of formatting
+          arguments.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="acceptor-status-message"></a>
+          [Generic function]
+          <br><b>acceptor-status-message</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor http-return-code &amp;key &amp;allow-other-keys</clix:lambda-list></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function is called when a request's handler has been
+          called but failed to provide content to send back to the
+          client.  It converts the
+          <code><i>HTTP-STATUS-CODE</i></code> to some request
+          contents, typically a human readable description of the
+          status code to be displayed to the user.
+
+          If an ERROR-TEMPLATE-DIRECTORY is set in the current
+          acceptor and the directory contains a file corresponding to
+          HTTP-STATUS-CODE named &lt;code&gt;.html, that file is sent
+          to the client after variable substitution.  Variables are
+          referenced by ${&lt;variable-name&gt;}.
+
+          Additional keyword arguments may be provided which are made
+          available to the templating logic as substitution variables.
+          These variables can be interpolated into error message
+          templates in, which contains the current URL relative to the
+          server and without GET parameters.
+
+          In addition to the variables corresponding to keyword
+          arguments, the script-name, lisp-implementation-type,
+          lisp-implementation-version and hunchentoot-version
+          variables are available.
+        </clix:description></blockquote></p>
+    
+
+  <h4 xmlns=""><a name="subclassing-acceptors">An example of how to subclass ACCEPTOR</a></h4>
+
+    This example shows how to subclass <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> in order to
+    provide Hunchentoot with basic virtual host support.  It assumes
+    Hunchentoot is sitting behind an Internet-facing reverse-proxy web server
+    that maps the host (or domain) part of incoming HTTP requests to unique
+    localhost ports.
+
+    <pre>(asdf:load-system "hunchentoot")
+(asdf:load-system "drakma")
+
+;;; Subclass ACCEPTOR
+(defclass vhost (tbnl:acceptor)
+  ;; slots
+  ((dispatch-table
+    :initform '()
+    :accessor dispatch-table
+    :documentation "List of dispatch functions"))
+  ;; options
+  (:default-initargs                    ; default-initargs must be used
+   :address "127.0.0.1"))               ; because ACCEPTOR uses it
+
+;;; Specialise ACCEPTOR-DISPATCH-REQUEST for VHOSTs
+(defmethod tbnl:acceptor-dispatch-request ((vhost vhost) request)
+  ;; try REQUEST on each dispatcher in turn
+  (mapc (lambda (dispatcher)
+         (let ((handler (funcall dispatcher request)))
+           (when handler               ; Handler found. FUNCALL it and return result
+             (return-from tbnl:acceptor-dispatch-request (funcall handler)))))
+       (dispatch-table vhost))
+  (call-next-method))
+
+;;; ======================================================================
+;;; Now all we need to do is test it
+
+;;; Instantiate VHOSTs
+(defvar vhost1 (make-instance 'vhost :port 50001))
+(defvar vhost2 (make-instance 'vhost :port 50002))
+
+;;; Populate each dispatch table
+(push
+ (tbnl:create-prefix-dispatcher "/foo" 'foo1)
+ (dispatch-table vhost1))
+(push
+ (tbnl:create-prefix-dispatcher "/foo" 'foo2)
+ (dispatch-table vhost2))
+
+;;; Define handlers
+(defun foo1 () "Hello")
+(defun foo2 () "Goodbye")
+
+;;; Start VHOSTs
+(tbnl:start vhost1)
+(tbnl:start vhost2)
+
+;;; Make some requests
+(drakma:http-request "http://127.0.0.1:50001/foo")
+;;; =|
+;;; 127.0.0.1 - [2012-06-08 14:30:39] "GET /foo HTTP/1.1" 200 5 "-" "Drakma/1.2.6 (SBCL 1.0.56; Linux; 2.6.32-5-686; http://weitz.de/drakma/)"
+;;; =&gt;
+;;; "Hello"
+;;; 200
+;;; ((:CONTENT-LENGTH . "5") (:DATE . "Fri, 08 Jun 2012 14:30:39 GMT")
+;;;  (:SERVER . "Hunchentoot 1.2.3") (:CONNECTION . "Close")
+;;;  (:CONTENT-TYPE . "text/html; charset=utf-8"))
+;;; #&lt;PURI:URI http://127.0.0.1:50001/foo&gt;
+;;; #&lt;FLEXI-STREAMS:FLEXI-IO-STREAM {CA90059}&gt;
+;;; T
+;;; "OK"
+(drakma:http-request "http://127.0.0.1:50002/foo")
+;;; =|
+;;; 127.0.0.1 - [2012-06-08 14:30:47] "GET /foo HTTP/1.1" 200 7 "-" "Drakma/1.2.6 (SBCL 1.0.56; Linux; 2.6.32-5-686; http://weitz.de/drakma/)"
+;;; =&gt;
+;;; "Goodbye"
+;;; 200
+;;; ((:CONTENT-LENGTH . "7") (:DATE . "Fri, 08 Jun 2012 14:30:47 GMT")
+;;;  (:SERVER . "Hunchentoot 1.2.3") (:CONNECTION . "Close")
+;;;  (:CONTENT-TYPE . "text/html; charset=utf-8"))
+;;; #&lt;PURI:URI http://127.0.0.1:50002/foo&gt;
+;;; #&lt;FLEXI-STREAMS:FLEXI-IO-STREAM {CAE8059}&gt;
+;;; T
+;;; "OK"</pre>
+
+    How to make each VHOST write to separate access log streams (or files) is
+    left as an exercise to the reader.
+
+  
+
+    <h4 xmlns=""><a name="taskmasters">Taskmasters</a></h4>
+      As a "normal" Hunchentoot user, you can completely ignore
+      taskmasters and skip this section.  But if you're still reading,
+      here are the dirty details: Each <a href="#acceptors">acceptor</a> has a taskmaster associated with
+      it at creation time.  It is the taskmaster's job to distribute
+      the work of accepting and handling incoming connections.  The
+      acceptor calls the taskmaster if appropriate and the taskmaster
+      calls back into the acceptor.  This is done using the generic
+      functions described in this and the <a href="#acceptor-behaviour">previous</a> section.  Hunchentoot
+      comes with two standard taskmaster implementations - one (which
+      is the default used on multi-threaded Lisps) which starts a new
+      thread for each incoming connection and one which handles all
+      requests sequentially.  It should for example be relatively
+      straightforward to create a taskmaster which allocates threads
+      from a fixed pool instead of creating a new one for each
+      connection.
+
+      <p>
+        You can control the resources consumed by a threaded taskmaster via
+        two initargs. <code>:max-thread-count</code> lets you set the maximum
+        number of request threads that can be processes simultaneously.  If
+        this is <code>nil</code>, the is no thread limit imposed.
+
+        <code>:max-accept-count</code> lets you set the maximum number of requests
+        that can be outstanding (i.e. being processed or queued for processing).
+
+        If <code>:max-thread-count</code> is supplied and <code>:max-accept-count</code>
+        is <code>NIL</code>, then a <code xmlns=""><a href="#+http-service-unavailable+">+HTTP-SERVICE-UNAVAILABLE+</a></code>
+        error will be generated if there are more than the max-thread-count
+        threads processing requests.  If both <code>:max-thread-count</code>
+        and <code>:max-accept-count</code> are supplied, then max-thread-count
+        must be less than max-accept-count; if more than max-thread-count
+        requests are being processed, then requests up to max-accept-count
+        will be queued until a thread becomes available.  If more than
+        max-accept-count requests are outstanding, then a <code xmlns=""><a href="#+http-service-unavailable+">+HTTP-SERVICE-UNAVAILABLE+</a></code>
+        error will be generated.
+
+        In a load-balanced environment with multiple Hunchentoot servers, it's
+        reasonable to provide <code>:max-thread-count</code> but leave
+        <code>:max-accept-count</code> null.   This will immediately result
+        in <code xmlns=""><a href="#+http-service-unavailable+">+HTTP-SERVICE-UNAVAILABLE+</a></code> when one server is
+        out of resources, so the load balancer can try to find another server.
+
+        In an environment with a single Hunchentoot server, it's reasonable
+        to provide both <code>:max-thread-count</code> and a somewhat larger value
+        for <code>:max-accept-count</code>.  This will cause a server that's almost
+        out of resources to wait a bit; if the server is completely out of resources,
+        then the reply will be <code xmlns=""><a href="#+http-service-unavailable+">+HTTP-SERVICE-UNAVAILABLE+</a></code>.
+        The default for these values is 100 and 120, respectively.
+      </p>
+
+      <p>
+        If you want to implement your own taskmasters, you should subclass
+        <code xmlns=""><a href="#taskmaster">TASKMASTER</a></code> or one of its subclasses,
+        <code xmlns=""><a href="#single-threaded-taskmaster">SINGLE-THREADED-TASKMASTER</a></code> or
+        <code xmlns=""><a href="#one-thread-per-connection-taskmaster">ONE-THREAD-PER-CONNECTION-TASKMASTER</a></code>, and
+        specialize the generic functions in this section.
+      </p>
+
+      <p xmlns=""><a class="none" name="taskmaster"></a>
+      [Standard class]
+      <br><b>taskmaster</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          An instance of this class is responsible for distributing
+          the work of handling requests for its acceptor.  This is an
+          "abstract" class in the sense that usually only instances of
+          subclasses of <code><a href="#taskmaster">TASKMASTER</a></code> will be used.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="one-thread-per-connection-taskmaster"></a>
+      [Standard class]
+      <br><b>one-thread-per-connection-taskmaster</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          A taskmaster that starts one thread for listening to
+          incoming requests and one thread for each incoming
+          connection.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            This is the default taskmaster implementation for multi-threaded Lisp
+            implementations.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="single-threaded-taskmaster"></a>
+      [Standard class]
+      <br><b>single-threaded-taskmaster</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          A taskmaster that runs synchronously in the
+          thread where the <code><a href="#start">START</a></code> function was invoked (or
+          in the case of LispWorks in the thread started
+          by <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/lw51/LWRM/html/lwref-61.htm#marker-910861"><code>COMM:START-UP-SERVER</code></a>).
+          This is the simplest possible taskmaster implementation in that its
+          methods do nothing but calling their acceptor "sister"
+          methods - <code><a href="#execute-acceptor">EXECUTE-ACCEPTOR</a></code> calls <code><a href="#accept-connections">ACCEPT-CONNECTIONS</a></code>,
+          <code><a href="#handle-incoming-connection">HANDLE-INCOMING-CONNECTION</a></code> calls <code><a href="#process-connection">PROCESS-CONNECTION</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="multi-threaded-taskmaster"></a>
+      [Standard class]
+      <br><b>multi-threaded-taskmaster</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This is an abstract class for taskmasters that use multiple threads;
+          it is not a concrete class and you should not instantiate it with
+          <code xmlns="http://www.w3.org/1999/xhtml">MAKE-INSTANCE</code>.
+          Instead, you should instantiate its subclass
+          <code><a href="#one-thread-per-connection-taskmaster">ONE-THREAD-PER-CONNECTION-TASKMASTER</a></code> described above.
+          <code><a href="#multi-threaded-taskmaster">MULTI-THREADED-TASKMASTER</a></code>
+          is intended to be inherited from by extensions to Hunchentoot,
+          such as <a xmlns="http://www.w3.org/1999/xhtml" href="http://common-lisp.net/project/qitab/">quux-hunchentoot</a>'s
+          <code xmlns="http://www.w3.org/1999/xhtml">THREAD-POOLING-TASKMASTER</code>,
+          though at the moment, doing so only inherits one slot and one method,
+          on <code><a href="#execute-acceptor">EXECUTE-ACCEPTOR</a></code>,
+          to have it start a new thread for the acceptor,
+          then saved in said slot.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="execute-acceptor"></a>
+          [Generic function]
+          <br><b>execute-acceptor</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">taskmaster
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">This is a callback called by the acceptor once it
+        has performed all initial processing to start listening for incoming
+        connections (see <code><a href="#start-listening">START-LISTENING</a></code>).  It usually calls the
+        <code><a href="#accept-connections">ACCEPT-CONNECTIONS</a></code> method of the acceptor, but depending on the
+        taskmaster instance the method might be called from a new thread.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="handle-incoming-connection"></a>
+          [Generic function]
+          <br><b>handle-incoming-connection</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">taskmaster socket
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function is called by the acceptor to start
+          processing of requests on a new incoming connection.  <code><i>socket</i></code> is the
+          usocket instance that represents the new connection (or a socket
+          handle on LispWorks).  The taskmaster starts processing requests on
+          the incoming connection by calling the <code><a href="#process-connection">PROCESS-CONNECTION</a></code>
+          method of the acceptor instance.  The <code><i>socket</i></code> argument is passed to
+          <code><a href="#process-connection">PROCESS-CONNECTION</a></code> as an argument.
+
+          If the taskmaster is a multi-threaded taskmaster, <code><a href="#handle-incoming-thread">HANDLE-INCOMING-THREAD</a></code>
+          will call <code><a href="#create-request-handler-thread">CREATE-REQUEST-HANDLER-THREAD</a></code>, which will call
+          <code><a href="#process-connection">PROCESS-CONNECTION</a></code> in a new thread.
+          <code><a href="#handle-incoming-thread">HANDLE-INCOMING-THREAD</a></code> might issue a
+          <code><a href="#+http-service-unavailable+">+HTTP-SERVICE-UNAVAILABLE+</a></code> error
+          if there are too many request threads or it might block waiting for a
+          request thread to finish.
+              </clix:description></blockquote></p>
+
+            <p xmlns=""><a class="none" name="start-thread"></a>
+          [Generic function]
+          <br><b>start-thread</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">taskmaster thunk &amp;key
+            </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">thread
+            </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">This function is a callback that
+                starts a new thread that will call the given <code><i>thunk</i></code>
+                in the context of the proper <code><i>taskmaster</i></code>,
+                with appropriate context-dependent keyword arguments.
+                <code><a href="#one-thread-per-connection-taskmaster">ONE-THREAD-PER-CONNECTION-TASKMASTER</a></code> uses it in
+                <code><a href="#execute-acceptor">EXECUTE-ACCEPTOR</a></code>
+                and <code><a href="#create-request-handler-thread">CREATE-REQUEST-HANDLER-THREAD</a></code>,
+                but specialized taskmasters may define more functions that use it.
+                By default, it just creates a thread calling the thunk
+                with a specified <code><i>name</i></code> keyword argument.
+                Specialized taskmasters may wrap special bindings and condition handlers
+                around the thunk call, register the thread in a management table, etc.
+              </clix:description></blockquote></p>
+
+            <p xmlns=""><a class="none" name="create-request-handler-thread"></a>
+          [Generic function]
+          <br><b>create-request-handler-thread</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">taskmaster socket
+            </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">thread
+            </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">This function is called by <code><a href="#handle-incoming-thread">HANDLE-INCOMING-THREAD</a></code>
+          to create a new thread which calls <code><a href="#process-connection">PROCESS-CONNECTION</a></code>.
+          If you specialize this function, you must be careful to have the thread
+          call <code><a href="#decrement-taskmaster-request-count">DECREMENT-TASKMASTER-REQUEST-COUNT</a></code> before
+          it exits.  A typical method will look like this:
+
+                <pre xmlns="http://www.w3.org/1999/xhtml">(defmethod create-request-handler-thread ((taskmaster monitor-taskmaster) socket)
+  (bt:make-thread
+   (lambda ()
+     (with-monitor-error-handlers
+         (unwind-protect
+              (with-monitor-variable-bindings
+                  (process-connection (taskmaster-acceptor taskmaster) socket))
+           (decrement-taskmaster-request-count taskmaster))))))</pre>
+
+
+
+
+
+
+
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="shutdown"></a>
+          [Generic function]
+          <br><b>shutdown</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">taskmaster
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">taskmaster
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Shuts down the taskmaster, i.e. frees all resources
+        that were set up by it.  For example, a multi-threaded taskmaster
+        might terminate all threads that are currently associated with it.
+        This function is called by the acceptor's <code><a href="#stop">STOP</a></code> method.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="taskmaster-acceptor"></a>
+      [Generic accessor]
+      <br><b>taskmaster-acceptor</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">taskmaster
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:returns></i><br><tt>(setf (</tt><b>taskmaster-acceptor</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">taskmaster
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This is an accessor for the slot of a <code><a href="#taskmaster">TASKMASTER</a></code>
+          object that links back to the <a xmlns="http://www.w3.org/1999/xhtml" href="#acceptors">acceptor</a> it is
+          associated with.
+        </clix:description></blockquote></p>
+
+    
+
+    <h4 xmlns=""><a name="request-dispatch">Request dispatch and handling</a></h4>
+
+      The main job of <code xmlns=""><a href="#handle-request">HANDLE-REQUEST</a></code> is to select
+      and call a function which handles the request, i.e. which looks
+      at the data the client has sent and prepares an appropriate
+      reply to send back.  This is by default implemented as follows:
+      <p>
+        The ACCEPTOR class defines a
+        <code xmlns=""><a href="#acceptor-dispatch-request">ACCEPTOR-DISPATCH-REQUEST</a></code> generic
+        function which is used to actually dispatch the request.  This
+        function is called by the default method of
+        <code xmlns=""><a href="#handle-request">HANDLE-REQUEST</a></code>.  Each
+        <code xmlns=""><a href="#acceptor-dispatch-request">ACCEPTOR-DISPATCH-REQUEST</a></code> method looks at
+        the request object and depending on its contents decides to
+        either handle the request or call the next method.
+      </p>
+      <p>
+        In order to dispatch a request, Hunchentoot calls the
+        <code xmlns=""><a href="#acceptor-dispatch-request">ACCEPTOR-DISPATCH-REQUEST</a></code> generic
+        functions.  The method for <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> tries
+        to serve a static file relative to it's
+        <code xmlns=""><a href="#acceptor-document-root">ACCEPTOR-DOCUMENT-ROOT</a></code>.  Application
+        specific acceptor subclasses will typically perform URL
+        parsing and dispatching according to the policy that is
+        required.
+      </p>
+      <p>
+        The default method of <code xmlns=""><a href="#handle-request">HANDLE-REQUEST</a></code> sets
+        up <a href="#logging">standard logging and error handling</a>
+        before it calls the acceptor's request dispatcher.
+      </p>
+      <p>
+        Request handlers do their work by modifying
+        the <a href="#replies">reply object</a> if necessary and by eventually
+        returning the response body in the form of a string or a binary
+        sequence.  As an alternative, they can also
+        call <code xmlns=""><a href="#send-headers">SEND-HEADERS</a></code> and write directly to a stream.
+      </p>
+    
+
+    <h4 xmlns=""><a name="easy-handlers">Using the easy-handler framework</a></h4>
+      <p>
+        The <code xmlns=""><a href="#easy-acceptor">EASY-ACCEPTOR</a></code> class defines a method
+        for <code xmlns=""><a href="#acceptor-dispatch-request">ACCEPTOR-DISPATCH-REQUEST</a></code> that walks
+        through the list <code xmlns=""><a href="#*dispatch-table*">*DISPATCH-TABLE*</a></code> which
+        consists of <em>dispatch functions</em>.  Each of these
+        functions accepts the request object as its only argument and
+        either returns a request handler to handle the request or
+        <code>NIL</code> which means that the next dispatcher in the
+        list will be tried. A <em>request handler</em> is a function
+        of zero arguments which relies on the special variable
+        <code xmlns=""><a href="#*request*">*REQUEST*</a></code> to access the request instance
+        being serviced. If all dispatch functions return
+        <code>NIL</code>, the next
+        <code xmlns=""><a href="#acceptor-dispatch-request">ACCEPTOR-DISPATCH-REQUEST</a></code> will be called.
+      </p>
+      <p>
+        <strong>N.B.</strong> All functions and variables in this
+        section are related to the easy request dispatch mechanism and
+        are meaningless if you're using your own request dispatcher.
+      </p>
+
+      <p xmlns=""><a class="none" name="easy-acceptor"></a>
+      [Standard class]
+      <br><b>easy-acceptor</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This class defines no additional slots with respect to
+          <code><a href="#acceptor">ACCEPTOR</a></code>.  It only serves as an
+          additional type for dispatching calls to
+          <code><a href="#acceptor-dispatch-request">ACCEPTOR-DISPATCH-REQUEST</a></code>.  In order to
+          use the easy handler framework, acceptors of this class or
+          one of its subclasses must be used.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="easy-ssl-acceptor"></a>
+      [Standard class]
+      <br><b>easy-ssl-acceptor</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This class mixes the <code><a href="#ssl-acceptor">SSL-ACCEPTOR</a></code> and
+          the <code><a href="#easy-acceptor">EASY-ACCEPTOR</a></code> classes.  It is used
+          when both ssl and the easy handler framework are required.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*dispatch-table*"></a>
+      [Special variable]
+      <br><b>*dispatch-table*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          A global list of dispatch functions.  The initial value is a
+          list consisting of the symbol
+          <code><a href="#dispatch-easy-handlers">DISPATCH-EASY-HANDLERS</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="create-prefix-dispatcher"></a>
+          [Function]
+          <br><b>create-prefix-dispatcher</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">prefix handler</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">dispatch-fn</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          A convenience function which will return a dispatcher that
+          returns <code><i>handler</i></code> whenever the path part of
+          the request URI starts with the
+          string <code><i>prefix</i></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="create-regex-dispatcher"></a>
+          [Function]
+          <br><b>create-regex-dispatcher</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">regex handler</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">dispatch-fn</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          A convenience function which will return a dispatcher that
+          returns <code><i>handler</i></code> whenever the path part of
+          the request URI matches
+          the <a xmlns="http://www.w3.org/1999/xhtml" href="http://weitz.de/cl-ppcre/">CL-PPCRE</a> regular
+          expression <code><i>regex</i></code> (which can be a string, an
+          s-expression, or a scanner).
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="create-folder-dispatcher-and-handler"></a>
+          [Function]
+          <br><b>create-folder-dispatcher-and-handler</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">uri-prefix base-path <tt>&amp;optional</tt> content-type</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">dispatch-fn</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Creates and returns a dispatch function which will dispatch to
+          a handler function which emits the file relative
+          to <code><i>base-path</i></code> that is denoted by the URI of
+          the request relative
+          to <code><i>uri-prefix</i></code>.  <code><i>uri-prefix</i></code>
+          must be a string ending with a
+          slash, <code><i>base-path</i></code> must be a pathname
+          designator for an existing directory.
+          Uses <code><a href="#handle-static-file">HANDLE-STATIC-FILE</a></code> internally.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If <code xmlns=""><i>content-type</i></code> is <em>not</em>
+            <code>NIL</code>, it will be used as a the content type for
+            all files in the folder.  Otherwise (which is the default)
+            the content type of each file will be
+            determined <a href="#handle-static-file">as usual</a>.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="create-static-file-dispatcher-and-handler"></a>
+          [Function]
+          <br><b>create-static-file-dispatcher-and-handler</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">uri path
+        <tt>&amp;optional
+        </tt> content-type
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Creates and returns a request dispatch function which will
+          dispatch to a handler function which emits the file denoted
+          by the pathname designator PATH with content type
+          CONTENT-TYPE if the SCRIPT-NAME of the request matches the
+          string URI.  If CONTENT-TYPE is NIL, tries to determine the
+          content type via the file's suffix.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="define-easy-handler"></a>
+          [Macro]
+          <br><b>define-easy-handler</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">description lambda-list [[declaration* | documentation]] form*</clix:lambda-list></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Defines a handler as if
+          by <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/m_defun.htm">
+            <code>DEFUN</code></a> and optionally registers it with a
+          URI so that it will be found
+          by <code><a href="#dispatch-easy-handlers">DISPATCH-EASY-HANDLERS</a></code>.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            <code xmlns=""><i>description</i></code> is either a
+            symbol <code xmlns=""><i>name</i></code> or a list matching the
+            <a href="http://www.lispworks.com/documentation/HyperSpec/Body/03_de.htm">destructuring
+              lambda list</a>
+          </p>
+          <pre xmlns="http://www.w3.org/1999/xhtml">(name &amp;key uri acceptor-names default-parameter-type default-request-type).</pre>
+          <code><i>lambda-list</i></code> is a list the elements of which
+          are either a symbol <code><i>var</i></code> or a list matching
+          the destructuring lambda list
+          <pre xmlns="http://www.w3.org/1999/xhtml">(var &amp;key real-name parameter-type init-form request-type).</pre>
+          The resulting handler will be a Lisp function with the
+          name <code><i>name</i></code> and keyword parameters named by
+          the <code><i>var</i></code> symbols.
+          Each <code><i>var</i></code> will be bound to the value of the
+          GET or POST parameter called <code><i>real-name</i></code> (a
+          string) before the body of the function is executed.
+          If <code><i>real-name</i></code> is not provided, it will be
+          computed
+          by <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_stg_up.htm#string-downcase">downcasing</a>
+          the symbol name of <code><i>var</i></code>.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If <code xmlns=""><i>uri</i></code> (which is evaluated) is provided,
+            then it must be a string or
+            a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#function_designator">function
+              designator</a> for a unary function.  In this case, the
+            handler will be returned
+            by <code xmlns=""><a href="#dispatch-easy-handlers">DISPATCH-EASY-HANDLERS</a></code>,
+            if <code xmlns=""><i>uri</i></code> is a string and
+            the <a href="#script-name">script name</a> of the current
+            request is <code xmlns=""><i>uri</i></code>, or
+            if <code xmlns=""><i>uri</i></code> designates a function and applying
+            this function to
+            the <a href="#*request*">current <code>REQUEST</code>
+              object</a> returns a true value.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            <code xmlns=""><i>acceptor-names</i></code> (which is evaluated) can be a
+            list of symbols which means that the handler will only be
+            returned by <code xmlns=""><a href="#dispatch-easy-handlers">DISPATCH-EASY-HANDLERS</a></code> in
+            acceptors which have one of these names
+            (see <code xmlns=""><a href="#acceptor-name">ACCEPTOR-NAME</a></code>).  <code xmlns=""><i>acceptor-names</i></code> can also be the
+            symbol <code>T</code> which means that the handler will be
+            returned by <code xmlns=""><a href="#dispatch-easy-handlers">DISPATCH-EASY-HANDLERS</a></code>
+            in <em>every</em> acceptor.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Whether the GET or POST parameter (or both) will be taken into
+            consideration, depends on <code xmlns=""><i>request-type</i></code>
+            which can
+            be <code>:GET</code>, <code>:POST</code>, <code>:BOTH</code>,
+            or <code>NIL</code>.  In the last case, the value of
+            <code xmlns=""><i>default-request-type</i></code> (the default of which
+            is <code>:BOTH</code>) will be used.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            The value of <code xmlns=""><i>var</i></code> will usually be a string
+            (unless it resulted from a <a href="#upload">file upload</a>
+            in which case it won't be converted at all), but
+            if <code xmlns=""><i>parameter-type</i></code> (which is evaluated) is
+            provided, the string will be converted to another Lisp type by
+            the following rules:
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If the corresponding GET or POST parameter wasn't provided by
+            the client, <code xmlns=""><i>var</i></code>'s value will
+            be <code>NIL</code>.  If <code xmlns=""><i>parameter-type</i></code>
+            is <code>'STRING</code>,
+            <code xmlns=""><i>var</i></code>'s value remains as is.
+            If <code xmlns=""><i>parameter-type</i></code> is <code>'INTEGER</code>
+            and the parameter string consists solely of decimal
+            digits, <code xmlns=""><i>var</i></code>'s value will be the
+            corresponding integer, otherwise <code>NIL</code>.
+            If <code xmlns=""><i>parameter-type</i></code> is
+            <code>'KEYWORD</code>, <code xmlns=""><i>var</i></code>'s value will be
+            the keyword obtained
+            by <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_intern.htm">interning</a>
+            the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_stg_up.htm#string-upcase">upcased</a>
+            parameter string into
+            the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/11_abc.htm">keyword
+              package</a>.  If <code xmlns=""><i>parameter-type</i></code>
+            is <code>'CHARACTER</code> and the parameter string is of
+            length one, <code xmlns=""><i>var</i></code>'s value will be the single
+            character of this string, otherwise <code>NIL</code>.
+            If <code xmlns=""><i>parameter-type</i></code>
+            is <code>'BOOLEAN</code>, <code xmlns=""><i>var</i></code>'s value will
+            always be <code>T</code> (unless it is <code>NIL</code> by the
+            first rule above, of course).
+            If <code xmlns=""><i>parameter-type</i></code> is any other atom, it is
+            supposed to be
+            a <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#function_designator">function
+              designator</a> for a unary function which will be called to
+            convert the string to something else.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Those were the rules for <em>simple</em> parameter types, but
+            <code xmlns=""><i>parameter-type</i></code> can also be a list starting
+            with one of the symbols
+            <code>LIST</code>, <code>ARRAY</code>,
+            or <code>HASH-TABLE</code>.  The second value of the list must
+            always be a simple parameter type as in the last paragraph -
+            we'll call it the <em>inner type</em> below.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            In the case of <code>'LIST</code>, all GET/POST parameters
+            called <code xmlns=""><i>real-name</i></code> will be collected,
+            converted to the inner type as by the rules above, and
+            assembled into a list which will be the value of
+            <code xmlns=""><i>var</i></code>.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            In the case of <code>'ARRAY</code>, all GET/POST parameters
+            which have a name like the result of
+          </p>
+          <pre xmlns="http://www.w3.org/1999/xhtml">(format nil "~A[~A]" real-name n)</pre>
+          where <code><i>n</i></code> is a non-negative integer, will be
+          assembled into an array where the <code><i>n</i></code>th element
+          will be set accordingly, after conversion to the inner type.
+          The array, which will become the value
+          of <code><i>var</i></code>, will be big enough to hold all
+          matching parameters, but not bigger.  Array elements not set as
+          described above will be <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>.  Note
+          that <code xmlns="http://www.w3.org/1999/xhtml">VAR</code> will always be bound to an array, which
+          may be empty, so it will never be <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>, even if no
+          appropriate GET/POST parameters are found.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            The full form of a <code>'HASH-TABLE</code> parameter type is
+          </p>
+          <pre xmlns="http://www.w3.org/1999/xhtml">(hash-table inner-type key-type test-function)</pre>
+          but <code><i>key-type</i></code>
+          and <code><i>test-function</i></code> can be left out in which
+          case they default to <code xmlns="http://www.w3.org/1999/xhtml">'STRING</code>
+          and <code xmlns="http://www.w3.org/1999/xhtml">'EQUAL</code>, respectively.  For this parameter type,
+          all GET/POST parameters which have a name like the result of
+          <pre xmlns="http://www.w3.org/1999/xhtml">(format nil "~A{~A}" real-name key)</pre>
+          (where <code><i>key</i></code> is a string that doesn't contain
+          curly brackets) will become the values (after conversion
+          to <code><i>inner-type</i></code>) of a hash table with test
+          function <code><i>test-function</i></code>
+          where <code><i>key</i></code> (after conversion
+          to <code><i>key-type</i></code>) will be the corresponding key.
+          Note that <code><i>var</i></code> will always be bound to a hash
+          table, which may be empty, so it will never be <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>,
+          even if no appropriate GET/POST parameters are found.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            To make matters even more complicated, the three compound
+            parameter types also have an abbreviated form - just one of
+            the symbols <code>LIST</code>, <code>ARRAY</code>,
+            or <code>HASH-TABLE</code>.  In this case, the inner type will
+            default to <code>'STRING</code>.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If <code xmlns=""><i>parameter-type</i></code> is not provided
+            or <code>NIL</code>, <code xmlns=""><i>default-parameter-type</i></code>
+            (the default of which is <code>'STRING</code>) will be used
+            instead.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If the result of the computations above would be
+            that <code xmlns=""><i>var</i></code> would be bound
+            to <code>NIL</code>, then <code xmlns=""><i>init-form</i></code> (if
+            provided) will be evaluated instead,
+            and <code xmlns=""><i>var</i></code> will be bound to the result of this
+            evaluation.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Handlers built with this macro are constructed in such a way
+            that the resulting Lisp function is useful even outside of
+            Hunchentoot.  Specifically, all the parameter computations
+            above will only happen if <code xmlns=""><a href="#*request*">*REQUEST*</a></code> is
+            bound, i.e. if we're within a Hunchentoot request.
+            Otherwise, <code xmlns=""><i>var</i></code> will always be bound to the
+            result of evaluating <code xmlns=""><i>init-form</i></code> unless a
+            corresponding keyword argument is provided.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            The <a href="#example">example code</a> that comes with
+            Hunchentoot contains an example which demonstrates some of the
+            features of <code xmlns=""><a href="#define-easy-handler">DEFINE-EASY-HANDLER</a></code>.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="dispatch-easy-handlers"></a>
+          [Function]
+          <br><b>dispatch-easy-handlers</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">This is a dispatcher which returns the appropriate handler
+        defined with <code><a href="#define-easy-handler">DEFINE-EASY-HANDLER</a></code>, if there is one.
+        </clix:description></blockquote></p>
+
+    
+
+    <h4 xmlns=""><a name="requests">Request objects</a></h4>
+
+      For each incoming request, the <a href="#acceptors">acceptor</a> (in
+      <code xmlns=""><a href="#process-connection">PROCESS-CONNECTION</a></code>) creates a
+      <code xmlns=""><a href="#request">REQUEST</a></code> object and makes it available to <a href="#request-dispatch">handlers</a> via the special variable
+      <code xmlns=""><a href="#*request*">*REQUEST*</a></code>.  This object contains all relevant
+      information about the request and this section collects the functions
+      which can be used to query such an object.  In all function where
+      <code xmlns=""><i>request</i></code> is an optional or keyword parameter, the
+      default is <code xmlns=""><a href="#*request*">*REQUEST*</a></code>.
+
+      <p>
+        If you need more fine-grained control over the behaviour of request
+        objects, you can subclass <code xmlns=""><a href="#request">REQUEST</a></code> and initialize
+        the <a href="#acceptor-request-class"><code>REQUEST-CLASS</code></a>
+        slot of the <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> class accordingly.  The
+        acceptor will generate request objects of the class named by this
+        slot.
+      </p>
+
+      <p xmlns=""><a class="none" name="request"></a>
+      [Standard class]
+      <br><b>request</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Objects of this class hold all the information
+          about an incoming request.  They are created automatically by
+          acceptors and can be accessed by the
+          corresponding <a xmlns="http://www.w3.org/1999/xhtml" href="#request-dispatch">handler</a>.
+
+          You should not mess with the slots of these objects directly, but you
+          can subclass <code><a href="#request">REQUEST</a></code> in order to implement your
+          own behaviour.  See
+          the <a xmlns="http://www.w3.org/1999/xhtml" href="#acceptor-request-class"><code>REQUEST-CLASS</code></a>
+          slot of the <code><a href="#acceptor">ACCEPTOR</a></code> class.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*request*"></a>
+      [Special variable]
+      <br><b>*request*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">The current REQUEST object while in the context of a request.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="real-remote-addr"></a>
+          [Function]
+          <br><b>real-remote-addr</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string{, list}
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the '<code xmlns="http://www.w3.org/1999/xhtml">X-Forwarded-For</code>' incoming http header as the
+          second value in the form of a list of IP addresses and the first
+          element of this list as the first value if this header exists.
+          Otherwise returns the value of <code><a href="#remote-addr">REMOTE-ADDR</a></code> as the only value.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="parameter"></a>
+          [Function]
+          <br><b>parameter</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">name
+        <tt>&amp;optional
+        </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the GET or the POST parameter with name
+          <code><i>name</i></code> (a string) - or <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>
+          if there is none.  If both a GET and a POST parameter with
+          the same name exist the GET parameter is returned.  Search
+          is case-sensitive.  See also
+          <code><a href="#get-parameter">GET-PARAMETER</a></code> and
+          <code><a href="#post-parameter">POST-PARAMETER</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="get-parameter"></a>
+          [Function]
+          <br><b>get-parameter</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">name <tt>&amp;optional</tt> request</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the value of the GET parameter (as provided via the
+          request URI) named by the string <code><i>name</i></code> as a
+          string (or <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> if there ain't no GET parameter
+          with this name). Note that only the first value will be
+          returned if the client provided more than one GET parameter
+          with the name <code><i>name</i></code>. See
+          also <code><a href="#get-parameters*">GET-PARAMETERS*</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="post-parameter"></a>
+          [Function]
+          <br><b>post-parameter</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">name <tt>&amp;optional</tt> request</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the value of the POST parameter (as provided in the
+          request's body) named by the
+          string <code><i>name</i></code>. Note that only the first value
+          will be returned if the client provided more than one POST
+          parameter with the name <code><i>name</i></code>.  This value
+          will usually be a string (or <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> if there ain't
+          no POST parameter with this name). If, however, the browser
+          sent a <a xmlns="http://www.w3.org/1999/xhtml" class="none" name="upload">file</a> through
+          a <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.faqs.org/rfcs/rfc2388.html">
+            <code>multipart/form-data</code>
+          </a> form, the value of this function is a three-element list
+          <pre xmlns="http://www.w3.org/1999/xhtml">(path file-name content-type)</pre>
+          where <code><i>path</i></code> is a pathname denoting the place
+          were the uploaded file was
+          stored, <code><i>file-name</i></code> (a string) is the file
+          name sent by the browser, and <code><i>content-type</i></code>
+          (also a string) is the content type sent by the browser. The
+          file denoted by <code><i>path</i></code> will be deleted after
+          the request has been handled - you have to move or copy it
+          somewhere else if you want to keep it.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            POST parameters will only be computed if the content type of
+            the request body was <code>multipart/form-data</code>
+            or <code>application/x-www-form-urlencoded</code>.  Although
+            this function is called <code>POST-PARAMETER</code>, you can
+            instruct Hunchentoot to compute these parameters for other
+            request methods by
+            setting <code xmlns=""><a href="#*methods-for-post-parameters*">*METHODS-FOR-POST-PARAMETERS*</a></code>.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            See also <code xmlns=""><a href="#post-parameters">POST-PARAMETERS</a></code>
+            and <code xmlns=""><a href="#*tmp-directory*">*TMP-DIRECTORY*</a></code>.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="get-parameters*"></a>
+          [Function]
+          <br><b>get-parameters*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc"><tt>&amp;optional</tt> request</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">alist</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns
+          an <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#alist">alist</a>
+          of all GET parameters (as provided via the request
+          URI). The <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#car">car</a>
+          of each element of this list is the parameter's name while
+          the <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#cdr">cdr</a>
+          is its value (as a string). The elements of this list are in
+          the same order as they were within the request URI. See
+          also <code><a href="#get-parameter">GET-PARAMETER</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="post-parameters*"></a>
+          [Function]
+          <br><b>post-parameters*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc"><tt>&amp;optional</tt> request</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">alist</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns
+          an <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#alist">alist</a>
+          of all POST parameters (as provided via the request's
+          body). The <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#car">car</a>
+          of each element of this list is the parameter's name while
+          the <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#cdr">cdr</a>
+          is its value. The elements of this list are in the same order
+          as they were within the request's body.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            See also <code xmlns=""><a href="#post-parameter">POST-PARAMETER</a></code>.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*methods-for-post-parameters*"></a>
+      [Special variable]
+      <br><b>*methods-for-post-parameters*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">A list of the request method types (as keywords) for which
+        Hunchentoot will try to compute <code><i>post-parameters</i></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="cookie-in"></a>
+          [Function]
+          <br><b>cookie-in</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">name
+        <tt>&amp;optional
+        </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the cookie with the name <code><i>name</i></code> (a string) as sent by the
+          browser - or <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> if there is none.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="cookies-in*"></a>
+          [Function]
+          <br><b>cookies-in*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">alist
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Returns an alist of all cookies associated with the <code><a href="#request">REQUEST</a></code> object
+        <code><i>request</i></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="host"></a>
+          [Function]
+          <br><b>host</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">host
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Returns the 'Host' incoming http header value.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="query-string*"></a>
+          [Function]
+          <br><b>query-string*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the query string of the <code><a href="#request">REQUEST</a></code> object <code><i>request</i></code>. That's
+          the part behind the question mark (i.e. the GET parameters).
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="referer"></a>
+          [Function]
+          <br><b>referer</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the 'Referer' (sic!) http header.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="request-method*"></a>
+          [Function]
+          <br><b>request-method*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">keyword
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the request method as a Lisp keyword.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="request-uri*"></a>
+          [Function]
+          <br><b>request-uri*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">uri
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the request URI.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="server-protocol*"></a>
+          [Function]
+          <br><b>server-protocol*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">keyword
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the request protocol as a Lisp keyword.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="user-agent"></a>
+          [Function]
+          <br><b>user-agent</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the 'User-Agent' http header.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="header-in*"></a>
+          [Function]
+          <br><b>header-in*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">name
+        <tt>&amp;optional
+        </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">header
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the incoming header with name
+          <code><i>name</i></code>.  <code><i>name</i></code> can be
+          a keyword (recommended) or a string.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="headers-in*"></a>
+          [Function]
+          <br><b>headers-in*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">alist
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns an alist of the incoming headers associated with the
+          <code><a href="#request">REQUEST</a></code> object
+          <code><i>request</i></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="remote-addr*"></a>
+          [Function]
+          <br><b>remote-addr*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">address
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the address the current request originated from.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="remote-port*"></a>
+          [Function]
+          <br><b>remote-port*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">port
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the port the current request originated from.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="local-addr*"></a>
+          [Function]
+          <br><b>local-addr*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">address
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The IP address of the local system that the client connected to.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="local-port*"></a>
+          [Function]
+          <br><b>local-port*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">port
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The TCP port number of the local system that the client connected to.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="script-name*"></a>
+          [Function]
+          <br><b>script-name*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">script-name
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the file name of the <code><a href="#request">REQUEST</a></code>
+          object <code><i>request</i></code>. That's the
+          requested URI without the query string (i.e the GET
+          parameters).
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="aux-request-value"></a>
+      [Accessor]
+      <br><b>aux-request-value</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">symbol
+        <tt>&amp;optional
+        </tt> request
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">value, present-p
+        </clix:returns></i><br><tt>(setf (</tt><b>aux-request-value</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">symbol
+        <tt>&amp;optional
+        </tt> request
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This accessor can be used to associate arbitrary
+          data with the the symbol <code><i>symbol</i></code> in the <code><a href="#request">REQUEST</a></code> object
+          <code><i>request</i></code>. <code><i>present-p</i></code> is true if such data was found, otherwise <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="delete-aux-request-value"></a>
+          [Function]
+          <br><b>delete-aux-request-value</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">symbol
+        <tt>&amp;optional
+        </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Removes the value associated with <code><i>symbol</i></code> from the <code><a href="#request">REQUEST</a></code> object
+          <code><i>request</i></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="authorization"></a>
+          [Function]
+          <br><b>authorization</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns as two values the user and password (if any) as
+          encoded in the 'AUTHORIZATION' header.  Returns
+          <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> if there is no such header.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*hunchentoot-default-external-format*"></a>
+      [Special variable]
+      <br><b>*hunchentoot-default-external-format*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The external format used to compute the <code><a href="#request">REQUEST</a></code> object.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*file-upload-hook*"></a>
+      [Special variable]
+      <br><b>*file-upload-hook*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          If this is not <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>, it should be a unary
+          function which will be called with a pathname for each file
+          which is <a xmlns="http://www.w3.org/1999/xhtml" href="#upload">uploaded</a> to Hunchentoot.  The
+          pathname denotes the temporary file to which the uploaded
+          file is written.  The hook is called directly before the
+          file is created. At this point,
+          <code><a href="#*request*">*REQUEST*</a></code> is already bound to the
+          current <code><a href="#request">REQUEST</a></code> object, but obviously
+          you can't access the post parameters yet.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="raw-post-data"></a>
+          [Function]
+          <br><b>raw-post-data</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;key</tt>
+          request external-format force-text force-binary want-stream
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">raw-body-or-stream</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the content sent by the client in the request body if
+          there was any (unless the content type
+          was <code xmlns="http://www.w3.org/1999/xhtml">multipart/form-data</code> in which
+          case <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> is returned).  By default, the result is
+          a string if the type of the <code xmlns="http://www.w3.org/1999/xhtml">Content-Type</code>
+          <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.faqs.org/rfcs/rfc1590.html">media type</a>
+          is <code xmlns="http://www.w3.org/1999/xhtml">"text"</code>, and a vector of octets otherwise.  In
+          the case of a string, the external format to be used to decode
+          the content will be determined from the <code xmlns="http://www.w3.org/1999/xhtml">charset</code>
+          parameter sent by the client (or
+          otherwise <code><a href="#*hunchentoot-default-external-format*">*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*</a></code>
+          will be used).
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            You can also provide an external format explicitly (through
+            <code xmlns=""><i>external-format</i></code>) in which case the result
+            will unconditionally be a string.  Likewise, you can provide
+            a true value for <code xmlns=""><i>force-text</i></code> which will
+            force Hunchentoot to act as if the type of the media type
+            had been <code>"text"</code>
+            (with <code xmlns=""><i>external-format</i></code> taking precedence
+            if provided).  Or you can provide a true value
+            for <code xmlns=""><i>force-binary</i></code> which means that you
+            want a vector of octets at any rate.  (If both
+            <code xmlns=""><i>force-text</i></code>
+            and <code xmlns=""><i>force-binary</i></code> are true, an error will
+            be signaled.)
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If, however, you provide a true value
+            for <code xmlns=""><i>want-stream</i></code>, the other parameters are
+            ignored and you'll get the content (flexi) stream to read
+            from it yourself.  It is then your responsibility to read
+            the correct amount of data, because otherwise you won't be
+            able to return a response to the client.  The stream will
+            have
+            its <a href="http://weitz.de/flexi-streams/#flexi-streams">octet
+              position</a> set to <code>0</code>.  If the client provided
+            a <code>Content-Length</code> header, the stream will also
+            have a
+            corresponding <a href="http://weitz.de/flexi-streams/#flexi-streams">bound</a>,
+            so no matter whether the client used chunked encoding or
+            not, you can always read until EOF.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If the content type of the request
+            was <code>multipart/form-data</code>
+            or <code>application/x-www-form-urlencoded</code>, the
+            content has been read by Hunchentoot already and you can't
+            read from the stream anymore.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            You can call <code xmlns=""><a href="#raw-post-data">RAW-POST-DATA</a></code> more than once
+            per request, but you can't mix calls which have different
+            values for <code xmlns=""><i>want-stream</i></code>.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Note that this function is slightly misnamed because a
+            client can send content even if the request method is not
+            POST.
+          </p>
+        </clix:description></blockquote></p>
+
+
+      <p xmlns=""><a class="none" name="recompute-request-parameters"></a>
+          [Function]
+          <br><b>recompute-request-parameters</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;key
+          </tt> request external-format
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Recomputes the GET and POST parameters for the <code><a href="#request">REQUEST</a></code> object
+          <code><i>request</i></code>.  This only makes sense if you're switching external formats
+          during the request.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="process-request"></a>
+          [Generic function]
+          <br><b>process-request</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">nil
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function is called by <code><a href="#process-connection">PROCESS-CONNECTION</a></code>
+          after the incoming headers have been read.  It
+          calls <code><a href="#handle-request">HANDLE-REQUEST</a></code> (and is more or less just a
+          thin wrapper around it) to select and call a
+          <a xmlns="http://www.w3.org/1999/xhtml" href="#request-dispatch">handler</a> and send the output of this handler to
+          the client.  Note that <code><a href="#process-connection">PROCESS-CONNECTION</a></code> is
+          called once per connection and loops in case of a persistent
+          connection while <code><a href="#process-request">PROCESS-REQUEST</a></code> is called anew
+          for each request.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            The return value of this function is ignored.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Like <code xmlns=""><a href="#process-connection">PROCESS-CONNECTION</a></code>, this is another function
+            the behaviour of which you should only modify if you really, really
+            know what you're doing.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="handle-request"></a>
+          [Generic function]
+          <br><b>handle-request</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">content
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function is called by <code><a href="#process-request">PROCESS-REQUEST</a></code> once
+          the request has been read and a <code><a href="#request">REQUEST</a></code> object
+          has been created.  Its job is to actually handle the request, i.e. to
+          return something to the client.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            The default method calls the
+            acceptor's <a href="#request-dispatch">request dispatcher</a>, but you
+            can of course implement a different behaviour.  The default method
+            also sets up <a href="#logging">standard error handling</a> for
+            the <a href="#request-dispatch">handler</a>.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Might be a good place to bind or rebind special variables which can
+            then be accessed by your <a href="#request-dispatch">handlers</a>.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="acceptor-dispatch-request"></a>
+          [Generic function]
+          <br><b>acceptor-dispatch-request</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">content
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function is called to actually dispatch the request
+          once the standard logging and error handling has been set
+          up.  <code><a href="#acceptor">ACCEPTOR</a></code> subclasses implement
+          methods for this function in order to perform their own
+          request routing.  If a method does not want to handle the
+          request, it is supposed to invoke <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_call_n.htm">CALL-NEXT-METHOD</a>
+          so that the next <code><a href="#acceptor">ACCEPTOR</a></code> in the
+          inheritance chain gets a chance to handle the request.
+        </clix:description></blockquote></p>
+
+      <p xmlns="">
+      [Generic readers]<br><a class="none" name="cookies-in"></a><b>cookies-in</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">cookies
+          </clix:returns></i><br><a class="none" name="get-parameters"></a><b>get-parameters</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">get-parameters
+          </clix:returns></i><br><a class="none" name="header-in"></a><b>header-in</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">name request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+          </clix:returns></i><br><a class="none" name="headers-in"></a><b>headers-in</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">headers
+          </clix:returns></i><br><a class="none" name="post-parameters"></a><b>post-parameters</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">post-parameters
+          </clix:returns></i><br><a class="none" name="query-string"></a><b>query-string</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">query-string
+          </clix:returns></i><br><a class="none" name="remote-addr"></a><b>remote-addr</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">address
+          </clix:returns></i><br><a class="none" name="remote-port"></a><b>remote-port</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">port
+          </clix:returns></i><br><a class="none" name="local-addr"></a><b>local-addr</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">address
+          </clix:returns></i><br><a class="none" name="local-port"></a><b>local-port</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">port
+          </clix:returns></i><br><a class="none" name="request-acceptor"></a><b>request-acceptor</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">acceptor
+          </clix:returns></i><br><a class="none" name="request-method"></a><b>request-method</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">method
+          </clix:returns></i><br><a class="none" name="request-uri"></a><b>request-uri</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">uri
+          </clix:returns></i><br><a class="none" name="server-protocol"></a><b>server-protocol</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">protocol
+          </clix:returns></i><br><a class="none" name="script-name"></a><b>script-name</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+          </clix:returns></i><br><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          These are various generic readers which are used
+          to read information about a <code><a href="#request">REQUEST</a></code> object.  If you are writing a
+          <a xmlns="http://www.w3.org/1999/xhtml" href="#request-dispatch">handler</a>, you should <em xmlns="http://www.w3.org/1999/xhtml">not</em> use these readers but instead utilize the
+          corresponding functions with an asterisk at the end of their name,
+          also listed in this section.  These generic readers are only
+          exported for users who want to create their own subclasses of
+          <code><a href="#request">REQUEST</a></code>.
+        </clix:description></blockquote></p>
+
+    
+
+    <h4 xmlns=""><a name="replies">Reply objects</a></h4>
+
+      For each incoming request, the <a href="#acceptors">acceptor</a>
+      (in <code xmlns=""><a href="#process-connection">PROCESS-CONNECTION</a></code>) creates
+      a <code xmlns=""><a href="#reply">REPLY</a></code> object and makes it available
+      to <a href="#request-dispatch">handlers</a> via the special variable
+      <code xmlns=""><a href="#*reply*">*REPLY*</a></code>.  This object contains all relevant
+      information (except for the content body) about the reply that will be
+      sent to the client and this section collects the functions which can
+      be used to query and modify such an object.  In all function
+      where <code xmlns=""><i>reply</i></code> is an optional or keyword parameter,
+      the default is <code xmlns=""><a href="#*reply*">*REPLY*</a></code>.
+
+      <p>
+        If you need more fine-grained control over the behaviour of reply
+        objects, you can subclass <code xmlns=""><a href="#reply">REPLY</a></code> and initialize
+        the <a href="#acceptor-reply-class"><code>REPLY-CLASS</code></a>
+        slot of the <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> class accordingly.  The
+        acceptor will generate reply objects of the class named by this
+        slot.
+      </p>
+
+      <p xmlns=""><a class="none" name="reply"></a>
+      [Standard class]
+      <br><b>reply</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Objects of this class hold all the information about an
+          outgoing reply.  They are created automatically by
+          Hunchentoot and can be accessed and modified by the
+          corresponding <a xmlns="http://www.w3.org/1999/xhtml" href="#request-dispatch">handler</a>.
+        <p xmlns="http://www.w3.org/1999/xhtml">
+          You should not mess with the slots of these objects directly, but you
+          can subclass <code xmlns=""><a href="#reply">REPLY</a></code> in order to implement your own behaviour.  See the
+          <a href="#acceptor-reply-class"><code>:reply-class</code></a> initarg
+          of the <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> class.
+        </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*reply*"></a>
+      [Special variable]
+      <br><b>*reply*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The current <code><a href="#reply">REPLY</a></code> object in the context of a request.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="header-out"></a>
+      [Accessor]
+      <br><b>header-out</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">name
+        <tt>&amp;optional
+        </tt> reply
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:returns></i><br><tt>(setf (</tt><b>header-out</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">name
+        <tt>&amp;optional
+        </tt> reply
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          <code><a href="#header-out">HEADER-OUT</a></code> returns the outgoing http
+          header named by the keyword <code><i>name</i></code> if
+          there is one, otherwise <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>. <code xmlns="http://www.w3.org/1999/xhtml">SETF</code>
+          of <code><a href="#header-out">HEADER-OUT</a></code> changes the current value
+          of the header named <code><i>name</i></code>. If no header
+          named <code><i>name</i></code> exists, it is created. For
+          backwards compatibility, <code><i>name</i></code> can also
+          be a string in which case the association between a header
+          and its name is case-insensitive.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Note that the header 'Set-Cookie' cannot be queried by
+            <code xmlns=""><a href="#header-out">HEADER-OUT</a></code> and must not be set by
+            <code>SETF</code> of <code xmlns=""><a href="#header-out">HEADER-OUT</a></code>.  See
+            also <code xmlns=""><a href="#headers-out*">HEADERS-OUT*</a></code>,
+            <code xmlns=""><a href="#content-type*">CONTENT-TYPE*</a></code>,
+            <code xmlns=""><a href="#content-length*">CONTENT-LENGTH*</a></code>,
+            <code xmlns=""><a href="#cookies-out*">COOKIES-OUT*</a></code>, and
+            <code xmlns=""><a href="#cookie-out">COOKIE-OUT</a></code>.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="headers-out*"></a>
+          [Function]
+          <br><b>headers-out*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">alist
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Returns an alist of the outgoing headers associated with the
+        <code><a href="#reply">REPLY</a></code> object <code><i>reply</i></code>.  See also <code><a href="#header-out">HEADER-OUT</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="content-length*"></a>
+      [Accessor]
+      <br><b>content-length*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">content-length
+        </clix:returns></i><br><tt>(setf (</tt><b>content-length*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The outgoing 'Content-Length' http header of <code><i>reply</i></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="content-type*"></a>
+      [Accessor]
+      <br><b>content-type*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">content-type
+        </clix:returns></i><br><tt>(setf (</tt><b>content-type*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The outgoing 'Content-Type' http header of <code><i>reply</i></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="cookie-out"></a>
+          [Function]
+          <br><b>cookie-out</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">name
+        <tt>&amp;optional
+        </tt> reply
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the current value of the outgoing <a xmlns="http://www.w3.org/1999/xhtml" href="#cookies">cookie</a> named
+          <code><i>name</i></code>. Search is case-sensitive.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="cookies-out*"></a>
+      [Accessor]
+      <br><b>cookies-out*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">alist
+        </clix:returns></i><br><tt>(setf (</tt><b>cookies-out*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns or sets an alist of the outgoing <a xmlns="http://www.w3.org/1999/xhtml" href="#cookies">cookies</a> associated with the
+          <code><a href="#reply">REPLY</a></code> object
+          <code><i>reply</i></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="return-code*"></a>
+      [Accessor]
+      <br><b>return-code*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">return-code
+        </clix:returns></i><br><tt>(setf (</tt><b>return-code*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Gets or sets the http return code of
+          <code><i>reply</i></code>.  The return code of each
+          <code><a href="#reply">REPLY</a></code> object is initially set to
+          <code><a href="#+http-ok+">+HTTP-OK+</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="send-headers"></a>
+          [Function]
+          <br><b>send-headers</b> <i></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">stream</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Sends the initial status line and all headers as determined
+          by the <code><a href="#reply">REPLY</a></code>
+          object <code><a href="#*reply*">*REPLY*</a></code>.  Returns
+          a <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_b.htm#binary">binary</a>
+          stream to which the body of the reply can be written.  Once
+          this function has been called, further changes
+          to <code><a href="#*reply*">*REPLY*</a></code> don't have any effect.
+          Also, automatic handling of errors (i.e. sending the
+          corresponding status code to the browser, etc.) is turned
+          off for this request and functions
+          like <code><a href="#redirect">REDIRECT</a></code> or
+          to <code><a href="#abort-request-handler">ABORT-REQUEST-HANDLER</a></code> won't have the
+          desired effect once the headers are sent.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If your handlers return the full body as a string or as an
+            array of octets, you should <em>not</em> call this function.
+            If a handler calls <code xmlns=""><a href="#send-headers">SEND-HEADERS</a></code> , its
+            return value is ignored.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="reply-external-format*"></a>
+      [Accessor]
+      <br><b>reply-external-format*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">external-format
+        </clix:returns></i><br><tt>(setf (</tt><b>reply-external-format*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> reply
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Gets or sets the external format of <code><i>reply</i></code> which is used for character output.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*default-content-type*"></a>
+      [Special variable]
+      <br><b>*default-content-type*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The default content-type header which is returned to the client.
+        </clix:description></blockquote></p>
+
+      <p xmlns="">
+      [Constants]<br><b>+http-continue+</b><br><b>+http-switching-protocols+</b><br><b>+http-ok+</b><br><b>+http-created+</b><br><b>+http-accepted+</b><br><b>+http-non-authoritative-information+</b><br><b>+http-no-content+</b><br><b>+http-reset-content+</b><br><b>+http-partial-content+</b><br><b>+http-multi-status+</b><br><b>+http-multiple-choices+</b><br><b>+http-moved-permanently+</b><br><b>+http-moved-temporarily+</b><br><b>+http-see-other+</b><br><b>+http-not-modified+</b><br><b>+http-use-proxy+</b><br><b>+http-temporary-redirect+</b><br><b>+http-bad-request+</b><br><b>+http-authorization-required+</b><br><b>+http-payment-required+</b><br><b>+http-forbidden+</b><br><b>+http-not-found+</b><br><b>+http-method-not-allowed+</b><br><b>+http-not-acceptable+</b><br><b>+http-proxy-authentication-required+</b><br><b>+http-request-time-out+</b><br><b>+http-conflict+</b><br><b>+http-gone+</b><br><b>+http-length-required+</b><br><b>+http-precondition-failed+</b><br><b>+http-request-entity-too-large+</b><br><b>+http-request-uri-too-large+</b><br><b>+http-unsupported-media-type+</b><br><b>+http-requested-range-not-satisfiable+</b><br><b>+http-expectation-failed+</b><br><b>+http-failed-dependency+</b><br><b>+http-internal-server-error+</b><br><b>+http-not-implemented+</b><br><b>+http-bad-gateway+</b><br><b>+http-service-unavailable+</b><br><b>+http-gateway-time-out+</b><br><b>+http-version-not-supported+</b><br><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The values of these constants are 100, 101, 200, 201, 202,
+          203, 204, 205, 206, 207, 300, 301, 302, 303, 304, 305, 307,
+          400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411,
+          412, 413, 414, 415, 416, 417, 424, 500, 501, 502, 503, 504,
+          and 505. See <code><a href="#return-code">RETURN-CODE</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns="">
+      [Generic readers]<br><a class="none" name="content-length"></a><b>content-length</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">reply
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">content-length
+          </clix:returns></i><br><a class="none" name="content-type"></a><b>content-type</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">reply
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">content-type
+          </clix:returns></i><br><a class="none" name="headers-out"></a><b>headers-out</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">reply
+          </clix:lambda-list></i>
+        =&gt;
+        <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">headers-out
+          </clix:returns></i><br><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          These are various generic readers which are used
+          to read information about a <code><a href="#reply">REPLY</a></code> object.  If you are writing a
+          <a xmlns="http://www.w3.org/1999/xhtml" href="#request-dispatch">handler</a>, you should <em xmlns="http://www.w3.org/1999/xhtml">not</em> use these readers but instead utilize the
+          corresponding functions with an asterisk at the end of their name,
+          also listed in this section.  These generic readers are only
+          exported for users who want to create their own subclasses of
+          <code><a href="#reply">REPLY</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns="">
+      [Generic accessors]<br><a class="none" name="cookies-out"></a><b>cookies-out</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">reply
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+          </clix:returns></i><br><tt>(setf (</tt><b>cookies-out</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">reply
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="return-code"></a><b>return-code</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">reply
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+          </clix:returns></i><br><tt>(setf (</tt><b>return-code</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">reply
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><a class="none" name="reply-external-format"></a><b>reply-external-format</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">reply
+          </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+          </clix:returns></i><br><tt>(setf (</tt><b>reply-external-format</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">reply
+          </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><br><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          These are various generic accessors which are
+          used to query and modify a <code><a href="#reply">REPLY</a></code> objects.  If
+          you are writing a
+          <a xmlns="http://www.w3.org/1999/xhtml" href="#request-dispatch">handler</a>, you should <em xmlns="http://www.w3.org/1999/xhtml">not</em> use these
+          accessors but instead utilize the corresponding functions with an
+          asterisk at the end of their name, also listed in this section.
+          These generic accessors are only exported for users who want to
+          create their own subclasses of
+          <code><a href="#reply">REPLY</a></code>.
+        </clix:description></blockquote></p>
+
+
+    
+
+    <h4 xmlns=""><a name="sessions">Sessions</a></h4>
+      Hunchentoot supports <em>sessions</em>: Once a <a href="#request-dispatch">request
+      handler</a> has called <code xmlns=""><a href="#start-session">START-SESSION</a></code>, Hunchentoot
+      uses either cookies or (if the client doesn't send the cookies
+      back) <a href="#*rewrite-for-session-urls*">rewrites URLs</a> to keep
+      track of this client, i.e. to provide a kind of 'state' for the
+      stateless http protocol. The session associated with the client is a
+      <a href="#session">CLOS object</a> which can be used
+      to <a href="#session-value">store arbitrary data</a> between requests.
+      <p>
+        Hunchentoot makes some reasonable effort to prevent eavesdroppers from
+        hijacking sessions (see below), but this should not be considered
+        really secure. Don't store sensitive data in sessions and rely solely
+        on the session mechanism as a safeguard against malicious users who
+        want to get at this data!
+      </p>
+      <p>
+        For each request there's one <code xmlns=""><a href="#session">SESSION</a></code> object which is accessible to the
+        <a href="#handler">handler</a> via the special
+        variable <code xmlns=""><a href="#*session*">*SESSION*</a></code>. This object holds all the
+        information available about the session and can be accessed with the
+        functions described in this chapter. Note that the internal structure
+        of <code xmlns=""><a href="#session">SESSION</a></code> objects should be considered opaque
+        and may change in future releases of Hunchentoot.
+      </p>
+      <p>
+        Sessions are automatically <a href="#session-verify">verified</a> for
+        validity and age when the <code xmlns=""><a href="#request">REQUEST</a></code> object is
+        instantiated, i.e. if <code xmlns=""><a href="#*session*">*SESSION*</a></code> is not NIL then
+        this session is valid (as far as Hunchentoot is concerned) and
+        not <a href="#session-too-old-p">too old</a>.  Old sessions
+        are <a href="#session-gc">automatically removed</a>.
+      </p>
+      <p>
+        Hunchentoot also provides a <code xmlns=""><a href="#session-regenerate-cookie-value">SESSION-REGENERATE-COOKIE-VALUE</a></code>
+        function that creates a new cookie value. This helps to prevent against
+        <a href="https://www.owasp.org/index.php/Session_fixation">session fixation
+        attacks</a>, and should be used when a user logs in according to the application.
+      </p>
+
+      <p xmlns=""><a class="none" name="session"></a>
+      [Standard class]
+      <br><b>session</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          <code><a href="#session">SESSION</a></code> objects are
+          automatically maintained by Hunchentoot.  They should not be created
+          explicitly with <code xmlns="http://www.w3.org/1999/xhtml">MAKE-INSTANCE</code> but implicitly
+          with <code><a href="#start-session">START-SESSION</a></code> and they should be treated as
+          opaque objects.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            You can ignore Hunchentoot's <code xmlns=""><a href="#session">SESSION</a></code> objects and
+            <a href="#session-behaviour">implement your own sessions</a> if you provide corresponding methods for
+            <code xmlns=""><a href="#session-cookie-value">SESSION-COOKIE-VALUE</a></code>
+            and <code xmlns=""><a href="#session-verify">SESSION-VERIFY</a></code>.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="start-session"></a>
+          [Function]
+          <br><b>start-session</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the current <code><a href="#session">SESSION</a></code>
+          object. If there is no current session, creates one and updates the
+          corresponding data structures. In this case the function will also
+          send a session cookie to the browser.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-value"></a>
+      [Accessor]
+      <br><b>session-value</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">symbol
+        <tt>&amp;optional
+        </tt> session
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">value, present-p
+        </clix:returns></i><br><tt>(setf (</tt><b>session-value</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">symbol
+        <tt>&amp;optional
+        </tt> session
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This accessor can be used to associate arbitrary data with the the
+          symbol <code><i>symbol</i></code> in the <code><a href="#session">SESSION</a></code>
+          object <code><i>session</i></code>. <code><i>present-p</i></code> is
+          true if such data was found, otherwise <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>. The default
+          value for <code><i>session</i></code> is
+          <code><a href="#*session*">*SESSION*</a></code>.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If <code>SETF</code> of <code xmlns=""><a href="#session-value">SESSION-VALUE</a></code> is called
+            with <code xmlns=""><i>session</i></code> being <code>NIL</code> then a
+            session is automatically instantiated
+            with <code xmlns=""><a href="#start-session">START-SESSION</a></code>.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="delete-session-value"></a>
+          [Function]
+          <br><b>delete-session-value</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">symbol
+        <tt>&amp;optional
+        </tt> session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Removes the value associated with
+          <code><i>symbol</i></code> from
+          <code><i>session</i></code> if there is one.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*session*"></a>
+      [Special variable]
+      <br><b>*session*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The current session while in the context of a request, or
+          <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="remove-session"></a>
+          [Function]
+          <br><b>remove-session</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Completely removes the <code><a href="#session">SESSION</a></code> object
+          <code><i>session</i></code> from Hunchentoot's
+          internal <a xmlns="http://www.w3.org/1999/xhtml" href="#session-db">session database</a>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="reset-sessions"></a>
+          [Function]
+          <br><b>reset-sessions</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> acceptor
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Removes <em xmlns="http://www.w3.org/1999/xhtml">all</em> stored sessions of
+          <code><i>acceptor</i></code>.  The default for
+          <code><i>acceptor</i></code> is
+          <code><a href="#*acceptor*">*ACCEPTOR*</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="regenerate-session-cookie-value"></a>
+          [Function]
+          <br><b>regenerate-session-cookie-value</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">cookie
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Regenerates the session cookie value. This should be used
+          when a user logs in according to the application to prevent
+          against session fixation attacks. The cookie value being
+          dependent on ID, USER-AGENT, REMOTE-ADDR, START, and
+          *SESSION-SECRET*, the only value we can change is START to
+          regenerate a new value. Since we're generating a new cookie,
+          it makes sense to have the session being restarted, in
+          time. That said, because of this fact, calling this function
+          twice in the same second will regenerate twice the same
+          value.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*rewrite-for-session-urls*"></a>
+      [Special variable]
+      <br><b>*rewrite-for-session-urls*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Whether HTML pages should possibly be rewritten for cookie-less
+          session-management.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*content-types-for-url-rewrite*"></a>
+      [Special variable]
+      <br><b>*content-types-for-url-rewrite*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The content types for which url-rewriting is OK. See
+          <code><a href="#*rewrite-for-session-urls*">*REWRITE-FOR-SESSION-URLS*</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*use-remote-addr-for-sessions*"></a>
+      [Special variable]
+      <br><b>*use-remote-addr-for-sessions*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Whether the client's remote IP (as returned by <code><a href="#real-remote-addr">REAL-REMOTE-ADDR</a></code>)
+          should be encoded into the session string.  If this value is true, a
+          session will cease to be accessible if the client's remote IP changes.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            This might for example be an issue if the client uses a proxy server
+            which doesn't send correct 'X-Forwarded-For' headers.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-remote-addr"></a>
+          [Generic function]
+          <br><b>session-remote-addr</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">remote-addr
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The remote IP address of the client when this session was started (as
+          returned by <code><a href="#real-remote-addr">REAL-REMOTE-ADDR</a></code>).
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*use-user-agent-for-sessions*"></a>
+      [Special variable]
+      <br><b>*use-user-agent-for-sessions*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">Whether the 'User-Agent' header should
+        be encoded into the session string.  If this value is true, a session
+        will cease to be accessible if the client sends a different
+        'User-Agent' header.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-user-agent"></a>
+          [Generic function]
+          <br><b>session-user-agent</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">user-agent
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The incoming 'User-Agent' header that
+          was sent when this session was created.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-max-time"></a>
+      [Generic accessor]
+      <br><b>session-max-time</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">max-time
+        </clix:returns></i><br><tt>(setf (</tt><b>session-max-time</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Gets or sets the time (in seconds) after
+          which <code><i>session</i></code> expires if it's not used.
+        </clix:description></blockquote></p>
+
+
+      <p xmlns=""><a class="none" name="*session-max-time*"></a>
+      [Special variable]
+      <br><b>*session-max-time*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The default time (in seconds) after which a session times out.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*session-gc-frequency*"></a>
+      [Special variable]
+      <br><b>*session-gc-frequency*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          A session GC (see function <code><a href="#session-gc">SESSION-GC</a></code>) will happen every
+          <code><a href="#*session-gc-frequency*">*SESSION-GC-FREQUENCY*</a></code> requests (counting only
+          requests which create a new session) if this variable is
+          not <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>.  See <code><a href="#session-created">SESSION-CREATED</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-gc"></a>
+          [Function]
+          <br><b>session-gc</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Removes sessions from the current session database which are
+          too old - see <code><a href="#session-too-old-p">SESSION-TOO-OLD-P</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-too-old-p"></a>
+          [Function]
+          <br><b>session-too-old-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">generalized-boolean
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns true if the <code><a href="#session">SESSION</a></code> object <code><i>session</i></code> has not been active in
+          the last <code xmlns="http://www.w3.org/1999/xhtml">(session-max-time session)</code> seconds.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-id"></a>
+          [Generic function]
+          <br><b>session-id</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">session-id
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The unique ID (an INTEGER) of the session.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-start"></a>
+          [Generic function]
+          <br><b>session-start</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">universal-time
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          The time this session was started.
+        </clix:description></blockquote></p>
+
+    
+
+    <h4 xmlns=""><a name="session-behaviour">Customizing session behaviour</a></h4>
+
+      For everyday session usage, you will probably just
+      use <code xmlns=""><a href="#start-session">START-SESSION</a></code>,
+      <code xmlns=""><a href="#session-value">SESSION-VALUE</a></code>,
+      and maybe <code xmlns=""><a href="#delete-session-value">DELETE-SESSION-VALUE</a></code>
+      and <code xmlns=""><a href="#*session*">*SESSION*</a></code>.  However, there are two ways to
+      customize the way Hunchentoot maintains sessions.
+      <p>
+        One way is to mostly leave the session mechanism intact but to tweak
+        it a bit:
+        <ul>
+          <li>The publicly visible part of a session is encoded using a
+          <a href="#*session-secret*">secret</a> which you can set yourself.</li>
+          <li>And it is stored using a cookie (or GET
+          parameter) <a href="#session-cookie-name">name</a> that you can
+          override.</li>
+          <li>Each session receives a <a href="#next-session-id">new ID</a> when
+          it is created and you can implement a more robust way to do that.</li>
+          <li>You can arrange to be called whenever a session
+          is <a href="#session-created">created</a> to trigger some action.  You
+          might also do this to invent your own
+          session <a href="#session-gc">garbage collection</a>.</li>
+          <li>By default, all sessions are stored in a global alist in memory.
+          You can't change the alist part, but you can distribute your sessions
+          over different <a href="#session-db">"databases"</a>.</li>
+          <li>By default, every operation which modifies sessions or one of the
+          session databases is guarded by a global lock, but you can arrange to
+          <a href="#session-db-lock">provide</a> different locks for this.</li>
+        </ul>
+      </p>
+      <p>
+        The other way to customize Hunchentoot's sessions is to completely
+        replace them.  This is actually pretty easy: Create your own class to
+        store state (which doesn't have to and probably shouldn't inherit
+        from <code xmlns=""><a href="#session">SESSION</a></code>) and implement methods for
+        <code xmlns=""><a href="#session-verify">SESSION-VERIFY</a></code>
+        and <code xmlns=""><a href="#session-cookie-value">SESSION-COOKIE-VALUE</a></code> - that's it.
+        Hunchentoot will continue to use cookies and/or to rewrite URLs to
+        keep track of session state and it will store "the current session"
+        (whatever that is in your implementation)
+        in <code xmlns=""><a href="#*session*">*SESSION*</a></code>.  Everything else (like persisting
+        sessions, GC, getting and setting values) you'll have to take care of
+        yourself and the other session functions
+        (like <code xmlns=""><a href="#start-session">START-SESSION</a></code> or
+        <code xmlns=""><a href="#session-value">SESSION-VALUE</a></code>) won't work anymore.  (Almost)
+        total freedom, but a lot of responsibility as well... :)
+      </p>
+
+      <p xmlns=""><a class="none" name="*session-secret*"></a>
+      [Special variable]
+      <br><b>*session-secret*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          A random ASCII string that's used to encode the public
+          session data.  This variable is initially unbound and will
+          be set (using <code><a href="#reset-session-secret">RESET-SESSION-SECRET</a></code>) the
+          first time a session is created, if necessary.  You can
+          prevent this from happening if you set the value yourself
+          before starting <a xmlns="http://www.w3.org/1999/xhtml" href="#acceptors">acceptors</a>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="reset-session-secret"></a>
+          [Function]
+          <br><b>reset-session-secret</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">secret
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Sets <code><a href="#*session-secret*">*SESSION-SECRET*</a></code> to a
+          new random value. All old sessions will cease to be valid.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-cookie-name"></a>
+          [Generic function]
+          <br><b>session-cookie-name</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">name
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the name (a string) of the cookie (or
+          the GET parameter) which is used to store a session on the client
+          side.  The default is to use the
+          string <code xmlns="http://www.w3.org/1999/xhtml">"hunchentoot-session"</code>, but you can
+          specialize this function if you want another name.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-created"></a>
+          [Generic function]
+          <br><b>session-created</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor new-session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function is called whenever a new session
+          has been created.  There's a default method which might trigger
+          a <a xmlns="http://www.w3.org/1999/xhtml" href="#session-gc">session GC</a> based on the value of
+          <code><a href="#*session-gc-frequency*">*SESSION-GC-FREQUENCY*</a></code>.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            The return value is ignored.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="next-session-id"></a>
+          [Generic function]
+          <br><b>next-session-id</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">id
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the next sequential session ID, an
+          integer, which should be unique per session.  The default method uses
+          a simple global counter and isn't guarded by a lock.  For a
+          high-performance production environment you might consider using a
+          more robust implementation.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-db"></a>
+      [Generic accessor]
+      <br><b>session-db</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:lambda-list></i>
+      =&gt;
+      <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">database
+        </clix:returns></i><br><tt>(setf (</tt><b>session-db</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        </clix:lambda-list></i><tt>) <i>new-value</i>)</tt><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns the current session database which is an
+          alist where each car is a session's ID and the cdr is the
+          corresponding <code><a href="#session">SESSION</a></code> object itself.  The default
+          is to use a global list for all acceptors.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-db-lock"></a>
+          [Generic function]
+          <br><b>session-db-lock</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">acceptor
+        <tt>&amp;key
+        </tt> whole-db-p
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">lock
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          A function which returns a lock that will be
+          used to prevent concurrent access to sessions.  The first argument
+          will be the <a xmlns="http://www.w3.org/1999/xhtml" href="#acceptors">acceptor</a> that handles the
+          current <a xmlns="http://www.w3.org/1999/xhtml" href="#requests">request</a>, the second argument is true
+          if the whole (current) session database is modified.  If it
+          is <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>, only one existing session in the database is
+          modified.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            This function can return <code>NIL</code> which means that sessions or
+            session databases will be modified without a lock held (for example
+            for single-threaded environments).  The default is to always return a
+            global lock (ignoring the <code xmlns=""><i>acceptor</i></code> argument) for
+            Lisps that support threads and <code>NIL</code> otherwise.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-verify"></a>
+          [Generic function]
+          <br><b>session-verify</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">request
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">session-or-nil
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Tries to get a session identifier from the cookies
+          (or alternatively from the GET parameters) sent by the client (see
+          <code><a href="#session-cookie-name">SESSION-COOKIE-NAME</a></code>
+          and <code><a href="#session-cookie-value">SESSION-COOKIE-VALUE</a></code>).  This identifier is
+          then checked for validity against the <code><a href="#request">REQUEST</a></code>
+          object
+          <code><i>request</i></code>.  On success the corresponding session object (if not too
+          old) is returned (and updated).  Otherwise <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> is returned.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            A default method is provided and you only need to write your own one
+            if you want to maintain your own sessions.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="session-cookie-value"></a>
+          [Generic function]
+          <br><b>session-cookie-value</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">session
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns a string which can be used to safely
+          restore the session <code><i>session</i></code> if as session has
+          already been established.  This is used as the value stored in the
+          session cookie or in the corresponding GET parameter and verified
+          by <code><a href="#session-verify">SESSION-VERIFY</a></code>.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            A default
+            method is provided and there's no reason to change it unless you
+            want to use your own session objects.
+          </p>
+        </clix:description></blockquote></p>
+
+    
+
+    <h4 xmlns=""><a name="cookies">Cookies</a></h4>
+
+      Outgoing cookies are stored in the request's <code xmlns=""><a href="#reply">REPLY</a></code>
+      object (see <code xmlns=""><a href="#cookie-out">COOKIE-OUT</a></code>
+      and <code xmlns=""><a href="#cookies-out*">COOKIES-OUT*</a></code>). They are CLOS objects
+      defined like this:
+
+      <pre>(defclass cookie ()
+  ((name :initarg :name
+         :reader <a class="noborder" name="cookie-name">cookie-name</a>
+         :type string
+         :documentation "The name of the cookie - a string.")
+   (value :initarg :value
+          :accessor <a class="noborder" name="cookie-value">cookie-value</a>
+          :initform ""
+          :documentation "The value of the cookie. Will be URL-encoded when sent to the browser.")
+   (expires :initarg :expires
+            :initform nil
+            :accessor <a class="noborder" name="cookie-expires">cookie-expires</a>
+            :documentation "The time (a universal time) when the cookie expires (or NIL).")
+   (max-age :initarg :max-age
+            :initform nil
+            :accessor <a class="noborder" name="cookie-max-age">cookie-max-age</a>
+            :documentation "The time delta (in seconds) after which the cookie expires (or NIL).")
+   (path :initarg :path
+         :initform nil
+         :accessor <a class="noborder" name="cookie-path">cookie-path</a>
+         :documentation "The path this cookie is valid for (or NIL).")
+   (domain :initarg :domain
+           :initform nil
+           :accessor <a class="noborder" name="cookie-domain">cookie-domain</a>
+           :documentation "The domain this cookie is valid for (or NIL).")
+   (secure :initarg :secure
+           :initform nil
+           :accessor <a class="noborder" name="cookie-secure">cookie-secure</a>
+           :documentation "A generalized boolean denoting whether this is a secure cookie.")
+   (http-only :initarg :http-only
+              :initform nil
+              :accessor <a class="noborder" name="cookie-http-only">cookie-http-only</a>
+              :documentation "A generalized boolean denoting whether this is a <a href="http://msdn2.microsoft.com/en-us/library/ms533046.aspx">HttpOnly</a> cookie.")))
+      </pre>
+
+      The <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_r.htm#reader">reader</a>
+      <code xmlns=""><a href="#cookie-name">COOKIE-NAME</a></code> and
+      the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#accessor">accessors</a>
+      <code xmlns=""><a href="#cookie-value">COOKIE-VALUE</a></code>, <code xmlns=""><a href="#cookie-expires">COOKIE-EXPIRES</a></code>, <code xmlns=""><a href="#cookie-max-age">COOKIE-MAX-AGE</a></code>,
+      <code xmlns=""><a href="#cookie-path">COOKIE-PATH</a></code>, <code xmlns=""><a href="#cookie-domain">COOKIE-DOMAIN</a></code>, <code xmlns=""><a href="#cookie-secure">COOKIE-SECURE</a></code>,
+      and <code xmlns=""><a href="#cookie-http-only">COOKIE-HTTP-ONLY</a></code> are all exported from
+      the <code>HUNCHENTOOT</code> package.  For now, the class name itself is <em>not</em> exported.
+
+      <p xmlns=""><a class="none" name="set-cookie"></a>
+          [Function]
+          <br><b>set-cookie</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          name <tt>&amp;key</tt> value expires path
+          domain secure http-only reply
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">cookie</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Creates a <code xmlns="http://www.w3.org/1999/xhtml">COOKIE</code> object from the parameters
+          provided to this function and adds it to the outgoing cookies
+          of the <a xmlns="http://www.w3.org/1999/xhtml" href="#replies"><code>REPLY</code> object</a>
+          <code><i>reply</i></code>. If a cookie with the same name
+          (case-sensitive) already exists, it is replaced. The default
+          for <code><i>reply</i></code>
+          is <code><a href="#*reply*">*REPLY*</a></code>. The default
+          for <code><i>value</i></code> is the empty string.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="set-cookie*"></a>
+          [Function]
+          <br><b>set-cookie*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">cookie <tt>&amp;optional</tt> reply</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">cookie</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Adds the <code xmlns="http://www.w3.org/1999/xhtml">COOKIE</code> object <code><i>cookie</i></code>
+          to the outgoing cookies of
+          the <a xmlns="http://www.w3.org/1999/xhtml" href="#replies"><code>REPLY</code> object</a>
+          <code><i>reply</i></code>. If a cookie with the same name
+          (case-sensitive) already exists, it is replaced. The default
+          for <code><i>reply</i></code> is <code><a href="#*reply*">*REPLY*</a></code>.
+        </clix:description></blockquote></p>
+    
+
+    <h4 xmlns=""><a name="logging">Logging</a></h4>
+      Hunchentoot can log accesses and diagnostic messages to two
+      separate destinations, which can be either files in the file
+      system or streams.  Logging can also be disabled by setting the
+      <clix:code xmlns:clix="http://bknr.net/clixdoc">ACCESS-LOG-DESTINATION</clix:code> and
+      <clix:code xmlns:clix="http://bknr.net/clixdoc">MESSAGE-LOG-DESTINATION</clix:code> slots in the
+      <code xmlns=""><a href="#acceptor">ACCEPTOR</a></code> to <code>NIL</code>.  The two
+      slots can be initialized by providing the
+      :ACCESS-LOG-DESTINATION and :MESSAGE-LOG-DESTINATION
+      initialization arguments when creating the acceptor or set by
+      setting the slots through its
+      <code xmlns=""><a href="#acceptor-message-log-destination">ACCEPTOR-MESSAGE-LOG-DESTINATION</a></code> and
+      <code xmlns=""><a href="#acceptor-access-log-destination">ACCEPTOR-ACCESS-LOG-DESTINATION</a></code> accessors.
+      <p>
+        When the path for the message or accept log is set to a
+        variable holding an output stream, hunchentoots writes
+        corresponding log entries to that stream.  By default,
+        Hunchentoot logs to *STANDARD-ERROR*.
+      </p>
+      <p>
+        Access logging is done in a format similar to what
+        the Apache web server can write so that logfile analysis using
+        standard tools is possible.  Errors during request processing are
+        logged to a separate file.
+      </p>
+      <p>
+        The standard logging mechanism is deliberately simple and slow.  The
+        log files are opened for each log entry and closed again after
+        writing, and access to them is protected by a global lock.  Derived
+        acceptor classes can implement methods for the
+        <code xmlns=""><a href="#acceptor-log-message">ACCEPTOR-LOG-MESSAGE</a></code> and
+        <code xmlns=""><a href="#acceptor-log-access">ACCEPTOR-LOG-ACCESS</a></code> generic functions in order to
+        log differently (e.g. to a central logging server or in a different
+        file format.
+      </p>
+      <p>
+        Errors happening within a <a href="#request-dispatch">handler</a>
+        which are not caught by the handler itself are handled by
+        Hunchentoot by logging them to the established
+        <code xmlns=""><a href="#acceptor-message-log-destination">ACCEPTOR-MESSAGE-LOG-DESTINATION</a></code>.
+      </p>
+
+      <p xmlns=""><a class="none" name="log-message*"></a>
+          [Function]
+          <br><b>log-message*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">log-level format-string
+        <tt>&amp;rest
+        </tt> format-arguments
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Convenience function which calls the message
+          logger of the current acceptor (if there is one) with the same
+          arguments it accepts.  Returns <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> if there is no message
+          logger or whatever the message logger returns.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            This is the function which Hunchentoot itself uses to log errors it
+            catches during request processing.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*log-lisp-errors-p*"></a>
+      [Special variable]
+      <br><b>*log-lisp-errors-p*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Whether Lisp errors in request handlers should be logged.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*log-lisp-backtraces-p*"></a>
+      [Special variable]
+      <br><b>*log-lisp-backtraces-p*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Whether Lisp backtraces should be logged.  Only
+          has an effect if <code><a href="#*log-lisp-errors-p*">*LOG-LISP-ERRORS-P*</a></code> is true
+          as well.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*log-lisp-warnings-p*"></a>
+      [Special variable]
+      <br><b>*log-lisp-warnings-p*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Whether Lisp warnings in request handlers should be logged.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*lisp-errors-log-level*"></a>
+      [Special variable]
+      <br><b>*lisp-errors-log-level*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Log level for Lisp errors.  Should be one
+          of <code xmlns="http://www.w3.org/1999/xhtml">:ERROR</code> (the default), <code xmlns="http://www.w3.org/1999/xhtml">:WARNING</code>,
+          or <code xmlns="http://www.w3.org/1999/xhtml">:INFO</code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*lisp-warnings-log-level*"></a>
+      [Special variable]
+      <br><b>*lisp-warnings-log-level*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Log level for Lisp warnings.
+          Should be one of <code xmlns="http://www.w3.org/1999/xhtml">:ERROR</code>, <code xmlns="http://www.w3.org/1999/xhtml">:WARNING</code>
+          (the default), or <code xmlns="http://www.w3.org/1999/xhtml">:INFO</code>.
+        </clix:description></blockquote></p>
+    
+
+    <h4 xmlns=""><a name="conditions">Conditions and error handling</a></h4>
+      <p>
+        This section describes how Hunchentoot deals with exceptional
+        situations.  See also the secion about <a href="#logging">logging</a>.
+      </p>
+      <p>
+        When an error occurs while processing a request, Hunchentoot's
+        default behavior is to catch the error, log it and
+        optionally display it to the client in the HTML response.
+        This behavior can be customized through the values of a number
+        of special variables, which are documented below.
+      </p>
+
+      <p xmlns=""><a class="none" name="*catch-errors-p*"></a>
+      [Special variable]
+      <br><b>*catch-errors-p*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          If the value of this variable is <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>
+          (the default is <code xmlns="http://www.w3.org/1999/xhtml">T</code>), then errors which happen while a
+          request is handled aren't <a xmlns="http://www.w3.org/1999/xhtml" href="#logging">caught as usual</a>, but
+          instead your
+          Lisp's <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_d.htm#debugger">debugger</a>
+          is <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_invoke.htm">invoked</a>.
+          This variable should obviously always be set to a <em xmlns="http://www.w3.org/1999/xhtml">true</em> value
+          in a production environment.
+          See <code><a href="#maybe-invoke-debugger">MAYBE-INVOKE-DEBUGGER</a></code>
+          if you want to fine-tune this behaviour.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*show-lisp-errors-p*"></a>
+      [Special variable]
+      <br><b>*show-lisp-errors-p*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Whether Lisp errors should be shown in HTML output.  Note
+          that this only affects canned responses generated by Lisp.
+          If an error template is present for the "internal server
+          error" status code, this special variable is not used (see
+          <code><a href="#acceptor-status-message">acceptor-status-message</a></code>).
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*show-lisp-backtraces-p*"></a>
+      [Special variable]
+      <br><b>*show-lisp-backtraces-p*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Whether Lisp backtraces should be shown in HTML output if
+          <code><a href="#*show-lisp-errors-p*">*SHOW-LISP-ERRORS-P*</a></code> is true and an error occurs.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="maybe-invoke-debugger"></a>
+          [Generic function]
+          <br><b>maybe-invoke-debugger</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">condition
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This generic function is called whenever a
+          <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/09_.htm">condition</a> <code xmlns="http://www.w3.org/1999/xhtml"><i>condition</i></code>
+          is signaled in Hunchentoot.  You might want to specialize it on
+          specific condition classes for debugging purposes.  The default
+          method <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_invoke.htm">invokes
+          the debugger</a> with <code><i>condition</i></code> if
+          <code><a href="#*catch-errors-p*">*CATCH-ERRORS-P*</a></code> is <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="hunchentoot-condition"></a>
+      [Condition type]
+      <br><b>hunchentoot-condition</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Superclass for all conditions related to Hunchentoot.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="hunchentoot-error"></a>
+      [Condition type]
+      <br><b>hunchentoot-error</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Superclass for all errors related to Hunchentoot and a subclass of <code><a href="#hunchentoot-condition">HUNCHENTOOT-CONDITION</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="parameter-error"></a>
+      [Condition type]
+      <br><b>parameter-error</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Signalled if a function was called with incosistent or illegal parameters.  A subclass of <code><a href="#hunchentoot-error">HUNCHENTOOT-ERROR</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="hunchentoot-warning"></a>
+      [Condition type]
+      <br><b>hunchentoot-warning</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Superclass for all warnings related to Hunchentoot and a subclass of <code><a href="#hunchentoot-condition">HUNCHENTOOT-CONDITION</a></code>.
+        </clix:description></blockquote></p>
+
+    
+
+    <h4 xmlns=""><a name="misc">Miscellaneous</a></h4>
+
+      Various functions and variables which didn't fit into one of the
+      other categories.
+
+      <p xmlns=""><a class="none" name="abort-request-handler"></a>
+          [Function]
+          <br><b>abort-request-handler</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> result
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function can be called by a request handler
+          at any time to immediately abort handling the request.  This works as
+          if the handler had returned <code><i>result</i></code>.  See the
+          source code of <code><a href="#redirect">REDIRECT</a></code> for an example.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="handle-if-modified-since"></a>
+          [Function]
+          <br><b>handle-if-modified-since</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">time <tt>&amp;optional</tt> request</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function is designed to be used inside
+          a <a xmlns="http://www.w3.org/1999/xhtml" href="#request-dispatch">handler</a>. If the client has sent an
+          'If-Modified-Since' header
+          (see <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.faqs.org/rfcs/rfc2616.html">RFC 2616</a>,
+          section 14.25) and the time specified matches the universal
+          time
+          <code><i>time</i></code> then the
+          header <code><a href="#+http-not-modified+">+HTTP-NOT-MODIFIED+</a></code> with no content
+          is immediately returned to the client.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Note that for this function to be useful you should usually
+            send 'Last-Modified' headers back to the client. See the
+            code
+            of <code xmlns=""><a href="#create-static-file-dispatcher-and-handler">CREATE-STATIC-FILE-DISPATCHER-AND-HANDLER</a></code>
+            for an example.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="handle-static-file"></a>
+          [Function]
+          <br><b>handle-static-file</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">path <tt>&amp;optional</tt> content-type</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">nil</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Sends the file denoted by the pathname designator
+          <code><i>path</i></code> with content type
+          <code><i>content-type</i></code> to the client.  Sets the
+          necessary handlers.  In particular the function employs
+          <code><a href="#handle-if-modified-since">HANDLE-IF-MODIFIED-SINCE</a></code>.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If <code xmlns=""><i>content-type</i></code> is <code>NIL</code> the
+            function tries to determine the correct content type from
+            the file's suffix or falls back
+            to <code>"application/octet-stream"</code> as a last resort.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            Note that this function
+            calls <code xmlns=""><a href="#send-headers">SEND-HEADERS</a></code> internally, so after
+            you've called it, the headers are sent and the return value
+            of your handler is ignored.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="redirect"></a>
+          [Function]
+          <br><b>redirect</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">target <tt>&amp;key</tt> host port protocol add-session-id code</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Sends back appropriate headers to redirect the client
+          to <code><i>target</i></code> (a string).
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            If <code xmlns=""><i>target</i></code> is a full URL starting with a
+            scheme, <code xmlns=""><i>host</i></code>, <code xmlns=""><i>port</i></code>,
+            and <code xmlns=""><i>protocol</i></code> are ignored.
+            Otherwise, <code xmlns=""><i>target</i></code> should denote the path
+            part of a URL, <code xmlns=""><i>protocol</i></code> must be one of
+            the keywords <code>:HTTP</code> or <code>:HTTPS</code>, and
+            the URL to redirect to will be constructed
+            from <code xmlns=""><i>host</i></code>, <code xmlns=""><i>port</i></code>, <code xmlns=""><i>protocol</i></code>,
+            and <code xmlns=""><i>target</i></code>.
+          </p>
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            <code xmlns=""><i>code</i></code> must be a 3xx HTTP redirection
+            status code to send to the client.  It defaults to 302
+            ("Found").  If <code xmlns=""><i>host</i></code> is not provided,
+            the current host (see <code xmlns=""><a href="#host">HOST</a></code>) will be
+            used. If <code xmlns=""><i>protocol</i></code> is the keyword
+            <code>:HTTPS</code>, the client will be redirected to a
+            https URL, if it's <code>:HTTP</code> it'll be sent to a
+            http URL.  If both <code xmlns=""><i>host</i></code> and
+            <code xmlns=""><i>protocol</i></code> aren't provided, then the
+            value of <code xmlns=""><i>protocol</i></code> will match the
+            current request.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="require-authorization"></a>
+          [Function]
+          <br><b>require-authorization</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc"><tt>&amp;optional</tt> realm</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Sends back appropriate headers to require basic HTTP
+          authentication
+          (see <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.faqs.org/rfcs/rfc2617.html">RFC 2617</a>)
+          for the realm <code><i>realm</i></code>. The default value
+          for <code><i>realm</i></code> is <code xmlns="http://www.w3.org/1999/xhtml">"Hunchentoot"</code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="no-cache"></a>
+          [Function]
+          <br><b>no-cache</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Adds appropriate headers to completely prevent caching on most browsers.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="ssl-p"></a>
+          [Function]
+          <br><b>ssl-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> acceptor
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">generalized-boolean
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Whether the current connection to the client is secure.  See <code><a href="#acceptor-ssl-p">ACCEPTOR-SSL-P</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="reason-phrase"></a>
+          [Function]
+          <br><b>reason-phrase</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">return-code
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns a reason phrase for the HTTP return code <code><i>return-code</i></code>
+          (which should be an integer) or <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> for return codes Hunchentoot
+          doesn't know.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="rfc-1123-date"></a>
+          [Function]
+          <br><b>rfc-1123-date</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+          <tt>&amp;optional
+          </tt> time
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Generates a time string according to <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.faqs.org/rfcs/rfc1123.html">RFC 1123</a>.  Default is current time.
+          This can be used to send a 'Last-Modified' header - see <code><a href="#handle-if-modified-since">HANDLE-IF-MODIFIED-SINCE</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="url-encode"></a>
+          [Function]
+          <br><b>url-encode</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">string
+        <tt>&amp;optional
+        </tt> external-format
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          URL-encodes a string using the external format <code><i>external-format</i></code>.  The default for <code><i>external-format</i></code> is the value of <code><a href="#*hunchentoot-default-external-format*">*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="url-decode"></a>
+          [Function]
+          <br><b>url-decode</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">string
+        <tt>&amp;optional
+        </tt> external-format
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Decodes a URL-encoded string which is assumed to
+          be encoded using the external
+          format <code><i>external-format</i></code>, i.e. this is the inverse
+          of <code><a href="#url-encode">URL-ENCODE</a></code>. It is assumed that you'll rarely
+          need this function, if ever. But just in case - here it is.  The
+          default for <code><i>external-format</i></code> is the value
+          of <code><a href="#*hunchentoot-default-external-format*">*HUNCHENTOOT-DEFAULT-EXTERNAL-FORMAT*</a></code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="escape-for-html"></a>
+          [Function]
+          <br><b>escape-for-html</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">string
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Escapes the characters #\&lt;, #\&gt;, #\', #\", and #\&amp; for HTML output.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="http-token-p"></a>
+          [Function]
+          <br><b>http-token-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object</clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">generalized-boolean</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This function tests whether <code><i>object</i></code> is a
+          non-empty string which is a <em xmlns="http://www.w3.org/1999/xhtml">token</em> according
+          to <a xmlns="http://www.w3.org/1999/xhtml" href="http://www.faqs.org/rfcs/rfc2068.html">RFC
+          2068</a> (i.e. whether it may be used for, say, cookie names).
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="mime-type"></a>
+          [Function]
+          <br><b>mime-type</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">pathspec
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">result
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Given a pathname designator <code><i>pathspec</i></code> returns the <a xmlns="http://www.w3.org/1999/xhtml" href="http://en.wikipedia.org/wiki/Internet_media_type">MIME type</a>
+          (as a string) corresponding to the suffix of the file denoted by
+          <code><i>pathspec</i></code> (or <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>).
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="within-request-p"></a>
+          [Function]
+          <br><b>within-request-p</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">
+        </clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">generalized-boolean
+        </clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Returns true if in the context of a request. Otherwise, <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*tmp-directory*"></a>
+      [Special variable]
+      <br><b>*tmp-directory*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          This should be a pathname denoting a directory where temporary
+          files can be stored. It is used for <a xmlns="http://www.w3.org/1999/xhtml" href="#upload">file
+          uploads</a>.
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*header-stream*"></a>
+      [Special variable]
+      <br><b>*header-stream*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          If this variable is not <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>, it should be bound to a stream to
+          which incoming and outgoing headers will be written for debugging
+          purposes.
+        </clix:description></blockquote></p>
+
+
+      <p xmlns=""><a class="none" name="*cleanup-function*"></a>
+      [Special variable]
+      <br><b>*cleanup-function*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          A designator for a function without arguments which is called on a
+          regular basis if <code><a href="#*cleanup-interval*">*CLEANUP-INTERVAL*</a></code> is not <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>.  The initial value is
+          the name of a function which invokes a garbage collection on 32-bit
+          versions of LispWorks.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            This variable is only available on LispWorks.
+          </p>
+        </clix:description></blockquote></p>
+
+      <p xmlns=""><a class="none" name="*cleanup-interval*"></a>
+      [Special variable]
+      <br><b>*cleanup-interval*</b><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+          Should be <code xmlns="http://www.w3.org/1999/xhtml">NIL</code> or a positive integer.  The system calls
+          <code><a href="#*cleanup-function*">*CLEANUP-FUNCTION*</a></code>
+          whenever <code><a href="#*cleanup-interval*">*CLEANUP-INTERVAL*</a></code> new worker threads
+          (counted globally across all acceptors) have been created unless the
+          value is <code xmlns="http://www.w3.org/1999/xhtml">NIL</code>.  The initial value is 100.
+          <p xmlns="http://www.w3.org/1999/xhtml">
+            This variable is only available on LispWorks.
+          </p>
+        </clix:description></blockquote></p>
+    
+  
+
+  <h3 xmlns=""><a class="none" name="testing">Testing</a></h3>
+    Hunchentoot comes with a test script which verifies that the
+    example web server responds as expected.  This test script uses the
+    <a href="http://weitz.de/drakma/">Drakma</a> HTTP client library
+    and thus shares a significant amount of its base code with
+    Hunchentoot itself.  Still, running the test script is a useful
+    confidence test, and it is also possible to run the script across
+    machines in order to verify a new Hunchentoot (or, for that matter
+    Drakma) port.
+    <p>
+      To run the confidence test, <a href="#teen-age">start
+      the example web server</a>.  Then, in your Lisp
+      listener, type
+<pre>(<a class="noborder" href="hunchentoot-test:test-hunchentoot">hunchentoot-test:test-hunchentoot</a> "http://localhost:4242")</pre>
+You will see some diagnostic output and a summary line that
+reports whether any tests have failed.  (You can also use the
+example certificate and key files in the test directory and
+start and test an https server instead.)
+    </p>
+
+    <p xmlns=""><a class="none" name="hunchentoot-test:test-hunchentoot"></a>
+          [Function]
+          <br><b>hunchentoot-test:test-hunchentoot</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">base-url <tt>&amp;key</tt></clix:lambda-list></i>
+            =&gt;
+            <i><clix:returns xmlns:clix="http://bknr.net/clixdoc">|</clix:returns></i><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+        Runs the built-in confidence
+        test.  <code><i>base-url</i></code> is the base URL to use
+        for testing, it should not have a trailing slash.  The keyword
+        arguments accepted are for future extension and should not
+        currently be used.
+        <p xmlns="http://www.w3.org/1999/xhtml">
+          The script expects the Hunchentoot example test server to be
+          running at the given <code xmlns=""><i>base-url</i></code> and
+          retrieves various pages from that server, expecting certain
+          responses.
+        </p>
+      </clix:description></blockquote></p>
+  
+
+  <h3 xmlns=""><a class="none" name="debugging">Debugging</a></h3>
+    By default, Hunchentoot intercepts all errors that occur while
+    executing request handlers, logs them to the log file and displays
+    a static error page to the user.  While developing applications,
+    you may want to change that behavior so that the debugger is
+    invoked when an error occurs.  You can set
+    the <code xmlns=""><a href="#*catch-errors-p*">*CATCH-ERRORS-P*</a></code> to <code>NIL</code> to
+    make that happen.  Alternatively, you may want to have Hunchentoot
+    display detailed error information in the error response page.
+    You can set the <code xmlns=""><a href="#*show-lisp-errors-p*">*SHOW-LISP-ERRORS-P*</a></code> to a
+    true value to make that happen.  If you don't want to see Lisp
+    backtraces in these error pages, you can
+    set <code xmlns=""><a href="#*show-lisp-backtraces-p*">*SHOW-LISP-BACKTRACES-P*</a></code>
+    to <code>NIL</code>.
+  
+
+  <h3 xmlns=""><a class="none" name="history">History</a></h3>
+
+    Hunchentoot's predecessor <a href="http://weitz.de/tbnl/">TBNL</a>
+    (which is short for "To Be Named Later") grew over the years as a
+    toolkit that I used for various commercial and private
+    projects. In August 2003, Daniel Barlow started
+    a <a href="http://article.gmane.org/gmane.lisp.web/148">review of
+      web APIs</a> on
+    the <a href="http://www.red-bean.com/lispweb/">lispweb</a> mailing
+    list and
+    I <a href="http://article.gmane.org/gmane.lisp.web/153">described</a>
+    the API of my hitherto-unreleased bunch of code (and christened it
+    "TBNL").
+    <p>
+      It turned out that
+      <a href="http://www.jeffcaldwell.com/">Jeff Caldwell</a> had
+      worked on something similar so he emailed me and proposed to
+      join our efforts. As I had no immediate plans to release my code
+      (which was poorly organized, undocumented, and mostly
+      CMUCL-specific), I gave it to Jeff and he worked towards a
+      release. He added docstrings, refactored, added some stuff, and
+      based it on KMRCL to make it portable across several Lisp
+      implementations.
+    </p>
+    <p>
+      Unfortunately, Jeff is at least as busy as I am so he didn't
+      find the time to finish a full release.  But in spring 2004 I
+      needed a documented version of the code for a client of mine who
+      thought it would be good if the toolkit were publicly available
+      under an open source license. So I took Jeff's code, refactored
+      again (to sync with the changes I had done in the meantime), and
+      added documentation.  This resulted in TBNL 0.1.0 (which
+      initially required mod_lisp as its front-end).
+    </p>
+    <p>
+      In March 2005, Bob Hutchinson sent patches which enabled TBNL to
+      use other front-ends than mod_lisp.  This made me aware that
+      TBNL was already <em>almost</em> a full web server, so
+      eventually I wrote Hunchentoot which <em>was</em> a full web
+      server, implemented as a wrapper around TBNL.  Hunchentoot 0.1.0
+      was released at the end of 2005 and was originally
+      LispWorks-only.
+    </p>
+    <p>
+      Hunchentoot 0.4.0, released in October 2006, was the first
+      release which also worked with other Common Lisp
+      implementations.  It is a major rewrite and also incorporates
+      most of TBNL and replaces it completely.
+    </p>
+    <p>
+      Hunchentoot 1.0.0, released in February 2009, is again a major
+      rewrite and should be considered work in progress.  It moved to
+      using
+      the <a href="http://common-lisp.net/project/usocket/">usocket</a>
+      and <a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux
+      Threads</a> libraries for non-LispWorks Lisps, thereby removing most of
+      the platform dependent code.  Threading behaviour was made
+      controllable through the introduction of
+      taskmasters.  <a href="http://www.cliki.net/mod_lisp">mod_lisp</a>
+      support and several other things were removed in this release to
+      simplify the code base (and partly due to the lack of interest).
+      Several architectural changes (lots of them not
+      backwards-compatible) were made to ease customization of
+      Hunchentoot's behaviour.  A significant part of the 1.0.0
+      redesign was done
+      by <a href="http://netzhansa.blogspot.com/">Hans Hübner</a>.
+    </p>
+  
+
+  <h3 xmlns=""><a class="none" name="index">Symbol index</a></h3>
+
+    Here are all exported symbols of the <code>HUNCHENTOOT</code>
+    package in alphabetical order linked to their corresponding
+    documentation entries:
+
+    <ul xmlns="">
+<li>
+<code><a href="#*acceptor*">*acceptor*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*catch-errors-p*">*catch-errors-p*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*cleanup-function*">*cleanup-function*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*cleanup-interval*">*cleanup-interval*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*content-types-for-url-rewrite*">*content-types-for-url-rewrite*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*default-connection-timeout*">*default-connection-timeout*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*default-content-type*">*default-content-type*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*dispatch-table*">*dispatch-table*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*file-upload-hook*">*file-upload-hook*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*header-stream*">*header-stream*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*hunchentoot-default-external-format*">*hunchentoot-default-external-format*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*lisp-errors-log-level*">*lisp-errors-log-level*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*lisp-warnings-log-level*">*lisp-warnings-log-level*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*log-lisp-backtraces-p*">*log-lisp-backtraces-p*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*log-lisp-errors-p*">*log-lisp-errors-p*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*log-lisp-warnings-p*">*log-lisp-warnings-p*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*methods-for-post-parameters*">*methods-for-post-parameters*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*reply*">*reply*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*request*">*request*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*rewrite-for-session-urls*">*rewrite-for-session-urls*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*session*">*session*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*session-gc-frequency*">*session-gc-frequency*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*session-max-time*">*session-max-time*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*session-secret*">*session-secret*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*show-lisp-backtraces-p*">*show-lisp-backtraces-p*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*show-lisp-errors-p*">*show-lisp-errors-p*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*tmp-directory*">*tmp-directory*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*use-remote-addr-for-sessions*">*use-remote-addr-for-sessions*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#*use-user-agent-for-sessions*">*use-user-agent-for-sessions*</a></code><span class="entry-type">Special variable</span>
+</li>
+<li>
+<code><a href="#+http-accepted+">+http-accepted+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-authorization-required+">+http-authorization-required+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-bad-gateway+">+http-bad-gateway+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-bad-request+">+http-bad-request+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-conflict+">+http-conflict+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-continue+">+http-continue+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-created+">+http-created+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-expectation-failed+">+http-expectation-failed+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-failed-dependency+">+http-failed-dependency+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-forbidden+">+http-forbidden+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-gateway-time-out+">+http-gateway-time-out+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-gone+">+http-gone+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-internal-server-error+">+http-internal-server-error+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-length-required+">+http-length-required+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-method-not-allowed+">+http-method-not-allowed+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-moved-permanently+">+http-moved-permanently+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-moved-temporarily+">+http-moved-temporarily+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-multi-status+">+http-multi-status+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-multiple-choices+">+http-multiple-choices+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-no-content+">+http-no-content+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-non-authoritative-information+">+http-non-authoritative-information+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-not-acceptable+">+http-not-acceptable+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-not-found+">+http-not-found+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-not-implemented+">+http-not-implemented+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-not-modified+">+http-not-modified+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-ok+">+http-ok+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-partial-content+">+http-partial-content+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-payment-required+">+http-payment-required+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-precondition-failed+">+http-precondition-failed+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-proxy-authentication-required+">+http-proxy-authentication-required+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-request-entity-too-large+">+http-request-entity-too-large+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-request-time-out+">+http-request-time-out+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-request-uri-too-large+">+http-request-uri-too-large+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-requested-range-not-satisfiable+">+http-requested-range-not-satisfiable+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-reset-content+">+http-reset-content+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-see-other+">+http-see-other+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-service-unavailable+">+http-service-unavailable+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-switching-protocols+">+http-switching-protocols+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-temporary-redirect+">+http-temporary-redirect+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-unsupported-media-type+">+http-unsupported-media-type+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-use-proxy+">+http-use-proxy+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#+http-version-not-supported+">+http-version-not-supported+</a></code><span class="entry-type">Constant</span>
+</li>
+<li>
+<code><a href="#abort-request-handler">abort-request-handler</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#accept-connections">accept-connections</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#acceptor">acceptor</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#acceptor-access-log-destination">acceptor-access-log-destination</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-address">acceptor-address</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#acceptor-dispatch-request">acceptor-dispatch-request</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#acceptor-document-root">acceptor-document-root</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-error-template-directory">acceptor-error-template-directory</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-input-chunking-p">acceptor-input-chunking-p</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-listen-backlog">acceptor-listen-backlog</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#acceptor-log-access">acceptor-log-access</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#acceptor-log-message">acceptor-log-message</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#acceptor-message-log-destination">acceptor-message-log-destination</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-name">acceptor-name</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-output-chunking-p">acceptor-output-chunking-p</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-persistent-connections-p">acceptor-persistent-connections-p</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-port">acceptor-port</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#acceptor-read-timeout">acceptor-read-timeout</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#acceptor-remove-session">acceptor-remove-session</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#acceptor-reply-class">acceptor-reply-class</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-request-class">acceptor-request-class</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#acceptor-ssl-certificate-file">acceptor-ssl-certificate-file</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#acceptor-ssl-p">acceptor-ssl-p</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#acceptor-ssl-privatekey-file">acceptor-ssl-privatekey-file</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#acceptor-ssl-privatekey-password">acceptor-ssl-privatekey-password</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#acceptor-status-message">acceptor-status-message</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#acceptor-write-timeout">acceptor-write-timeout</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#authorization">authorization</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#aux-request-value">aux-request-value</a></code><span class="entry-type">Accessor</span>
+</li>
+<li>
+<code><a href="#content-length">content-length</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#content-length*">content-length*</a></code><span class="entry-type">Accessor</span>
+</li>
+<li>
+<code><a href="#content-type">content-type</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#content-type*">content-type*</a></code><span class="entry-type">Accessor</span>
+</li>
+<li>
+<code><a href="#cookie-in">cookie-in</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#cookie-out">cookie-out</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#cookies-in">cookies-in</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#cookies-in*">cookies-in*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#cookies-out">cookies-out</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#cookies-out*">cookies-out*</a></code><span class="entry-type">Accessor</span>
+</li>
+<li>
+<code><a href="#create-folder-dispatcher-and-handler">create-folder-dispatcher-and-handler</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#create-prefix-dispatcher">create-prefix-dispatcher</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#create-regex-dispatcher">create-regex-dispatcher</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#create-request-handler-thread">create-request-handler-thread</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#create-static-file-dispatcher-and-handler">create-static-file-dispatcher-and-handler</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#define-easy-handler">define-easy-handler</a></code><span class="entry-type">Macro</span>
+</li>
+<li>
+<code><a href="#delete-aux-request-value">delete-aux-request-value</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#delete-session-value">delete-session-value</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#detach-socket">detach-socket</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#dispatch-easy-handlers">dispatch-easy-handlers</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#easy-acceptor">easy-acceptor</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#easy-ssl-acceptor">easy-ssl-acceptor</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#escape-for-html">escape-for-html</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#execute-acceptor">execute-acceptor</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#get-parameter">get-parameter</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#get-parameters">get-parameters</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#get-parameters*">get-parameters*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#handle-if-modified-since">handle-if-modified-since</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#handle-incoming-connection">handle-incoming-connection</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#handle-request">handle-request</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#handle-static-file">handle-static-file</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#header-in">header-in</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#header-in*">header-in*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#header-out">header-out</a></code><span class="entry-type">Accessor</span>
+</li>
+<li>
+<code><a href="#headers-in">headers-in</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#headers-in*">headers-in*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#headers-out">headers-out</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#headers-out*">headers-out*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#host">host</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#http-token-p">http-token-p</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#hunchentoot-condition">hunchentoot-condition</a></code><span class="entry-type">Condition type</span>
+</li>
+<li>
+<code><a href="#hunchentoot-error">hunchentoot-error</a></code><span class="entry-type">Condition type</span>
+</li>
+<li>
+<code><a href="#hunchentoot-test:test-hunchentoot">hunchentoot-test:test-hunchentoot</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#hunchentoot-warning">hunchentoot-warning</a></code><span class="entry-type">Condition type</span>
+</li>
+<li>
+<code><a href="#initialize-connection-stream">initialize-connection-stream</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#local-addr">local-addr</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#local-addr*">local-addr*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#local-port">local-port</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#local-port*">local-port*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#log-message*">log-message*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#maybe-invoke-debugger">maybe-invoke-debugger</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#mime-type">mime-type</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#multi-threaded-taskmaster">multi-threaded-taskmaster</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#next-session-id">next-session-id</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#no-cache">no-cache</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#one-thread-per-connection-taskmaster">one-thread-per-connection-taskmaster</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#parameter">parameter</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#parameter-error">parameter-error</a></code><span class="entry-type">Condition type</span>
+</li>
+<li>
+<code><a href="#post-parameter">post-parameter</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#post-parameters">post-parameters</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#post-parameters*">post-parameters*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#process-connection">process-connection</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#process-request">process-request</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#query-string">query-string</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#query-string*">query-string*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#raw-post-data">raw-post-data</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#real-remote-addr">real-remote-addr</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#reason-phrase">reason-phrase</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#recompute-request-parameters">recompute-request-parameters</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#redirect">redirect</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#referer">referer</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#regenerate-session-cookie-value">regenerate-session-cookie-value</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#remote-addr">remote-addr</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#remote-addr*">remote-addr*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#remote-port">remote-port</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#remote-port*">remote-port*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#remove-session">remove-session</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#reply">reply</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#reply-external-format">reply-external-format</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#reply-external-format*">reply-external-format*</a></code><span class="entry-type">Accessor</span>
+</li>
+<li>
+<code><a href="#request">request</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#request-acceptor">request-acceptor</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#request-method">request-method</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#request-method*">request-method*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#request-uri">request-uri</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#request-uri*">request-uri*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#require-authorization">require-authorization</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#reset-connection-stream">reset-connection-stream</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#reset-session-secret">reset-session-secret</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#reset-sessions">reset-sessions</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#return-code">return-code</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#return-code*">return-code*</a></code><span class="entry-type">Accessor</span>
+</li>
+<li>
+<code><a href="#rfc-1123-date">rfc-1123-date</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#script-name">script-name</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#script-name*">script-name*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#send-headers">send-headers</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#server-protocol">server-protocol</a></code><span class="entry-type">Generic reader</span>
+</li>
+<li>
+<code><a href="#server-protocol*">server-protocol*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#session">session</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#session-cookie-name">session-cookie-name</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#session-cookie-value">session-cookie-value</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#session-created">session-created</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#session-db">session-db</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#session-db-lock">session-db-lock</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#session-gc">session-gc</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#session-id">session-id</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#session-max-time">session-max-time</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#session-remote-addr">session-remote-addr</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#session-start">session-start</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#session-too-old-p">session-too-old-p</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#session-user-agent">session-user-agent</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#session-value">session-value</a></code><span class="entry-type">Accessor</span>
+</li>
+<li>
+<code><a href="#session-verify">session-verify</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#set-cookie">set-cookie</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#set-cookie*">set-cookie*</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#shutdown">shutdown</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#single-threaded-taskmaster">single-threaded-taskmaster</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#ssl-acceptor">ssl-acceptor</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#ssl-p">ssl-p</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#start">start</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#start-listening">start-listening</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#start-session">start-session</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#start-thread">start-thread</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#stop">stop</a></code><span class="entry-type">Generic function</span>
+</li>
+<li>
+<code><a href="#taskmaster">taskmaster</a></code><span class="entry-type">Standard class</span>
+</li>
+<li>
+<code><a href="#taskmaster-acceptor">taskmaster-acceptor</a></code><span class="entry-type">Generic accessor</span>
+</li>
+<li>
+<code><a href="#url-decode">url-decode</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#url-encode">url-encode</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#user-agent">user-agent</a></code><span class="entry-type">Function</span>
+</li>
+<li>
+<code><a href="#within-request-p">within-request-p</a></code><span class="entry-type">Function</span>
+</li>
+</ul>
+
+  
+
+  <h3 xmlns=""><a class="none" name="ack">Acknowledgements</a></h3>
+
+    Thanks to Jeff Caldwell - TBNL would not have been released
+    without his efforts.  Thanks
+    to <a href="http://www.cliki.net/Stefan%20Scholl">Stefan
+    Scholl</a> and Travis Cross for various additions and fixes to
+    TBNL, to <a href="http://www.foldr.org/~michaelw/">Michael
+    Weber</a> for initial file upload code, and
+    to <a href="http://www.ltn.lv/~jonis/">Janis Dzerins</a> for
+    his <a href="http://common-lisp.net/project/rfc2388/">RFC 2388
+    code</a>. Thanks to Bob Hutchison for his code for multiple
+    front-ends (which made me realize that TBNL was already pretty
+    close to a "real" web server) and the initial UTF-8 example.
+    Thanks to <a href="http://netzhansa.blogspot.com/">Hans Hübner</a>
+    for a lot of architectural and implementation enhancements for the
+    1.0.0 release and also for transferring the documentation to sane
+    XHTML.  Thanks to John
+    Foderaro's <a href="http://opensource.franz.com/aserve/index.html">AllegroServe</a>
+    for inspiration.  Thanks to <a href="http://www.htg1.de/">Uwe von
+    Loh</a> for
+    the <a href="http://www.htg1.de/hunchentoot/hunchentoot.html">Hunchentoot
+    logo</a>.
+
+    <p>
+      Hunchentoot originally used code
+      from <a href="http://www.cliki.net/ACL-COMPAT">ACL-COMPAT</a>,
+      specifically the chunking code from Jochen Schmidt.  (This has been
+      replaced by <a href="http://weitz.de/chunga/">Chunga</a>.)  When I ported
+      Hunchentoot to other Lisps than LispWorks, I stole code from
+      ACL-COMPAT, <a href="http://www.cliki.net/kmrcl">KMRCL</a>,
+      and <a href="http://www.cliki.net/trivial-sockets">trivial-sockets</a> for
+      implementation-dependent stuff like sockets and MP.  (This has been replaced by
+      <a href="http://common-lisp.net/project/bordeaux-threads/">Bordeaux
+      Threads</a>
+      and <a href="http://common-lisp.net/project/usocket/">usocket</a>.)
+    </p>
+    <p>
+      Parts of this documentation were prepared
+      with <a href="http://weitz.de/documentation-template/">DOCUMENTATION-TEMPLATE</a>,
+      no animals were harmed.
+    </p>
+  
+  <p>
+    <a href="http://weitz.de/index.html">BACK TO MY HOMEPAGE
+    </a>
+  </p>
+</body></html>
diff --git a/deps/hunchentoot/www/hunchentoot.gif b/deps/hunchentoot/www/hunchentoot.gif
new file mode 100644 (file)
index 0000000..6957435
Binary files /dev/null and b/deps/hunchentoot/www/hunchentoot.gif differ
diff --git a/deps/hunchentoot/www/img/made-with-lisp-logo.jpg b/deps/hunchentoot/www/img/made-with-lisp-logo.jpg
new file mode 100644 (file)
index 0000000..843cc7f
Binary files /dev/null and b/deps/hunchentoot/www/img/made-with-lisp-logo.jpg differ
diff --git a/deps/hunchentoot/www/index.html b/deps/hunchentoot/www/index.html
new file mode 100644 (file)
index 0000000..ddcd905
--- /dev/null
@@ -0,0 +1,17 @@
+<html>
+  <head>
+    <title>Welcome to Hunchentoot!</title>
+  </head>
+  <body>
+    <h1>Welcome</h1>
+    <p>
+      When you're reading this message, Hunchentoot has been properly installed.
+    </p>
+    <p>
+      Please read the <a href="hunchentoot-doc.html">documentation</a>.
+    </p>
+    <p>
+      <img src="img/made-with-lisp-logo.jpg" width="300" height="100"/>
+    </p>
+  </body>
+</html>
diff --git a/deps/md5/.gitattributes b/deps/md5/.gitattributes
new file mode 100644 (file)
index 0000000..1a1f266
--- /dev/null
@@ -0,0 +1,2 @@
+/*.lisp ident
+/*.asd ident
diff --git a/deps/md5/.gitignore b/deps/md5/.gitignore
new file mode 100644 (file)
index 0000000..a6d5a49
--- /dev/null
@@ -0,0 +1,12 @@
+# FASLs
+*.fas
+*.fasl
+*.ofasl
+*.nfasl
+*.xfasl
+*.dx32fsl
+*.dx64fsl
+*.abcl
+*.sse2f
+# Backup files
+*~
diff --git a/deps/md5/COPYING b/deps/md5/COPYING
new file mode 100644 (file)
index 0000000..f4f5db7
--- /dev/null
@@ -0,0 +1,5 @@
+This software has been placed into the public domain.
+
+This software is "as is", and has no warranty of any kind.  The
+authors assume no responsibility for the consequences of any use of
+this software.
diff --git a/deps/md5/NEWS b/deps/md5/NEWS
new file mode 100755 (executable)
index 0000000..3955f40
--- /dev/null
@@ -0,0 +1,51 @@
+Release 2.0.3
+=============
+
+ * Add support for md5sum-string for other implementations through
+   through flexi-streams.  Patch supplied by Daniel Kochmanski.
+
+
+Release 2.0.2
+=============
+
+ * This release adds support for LispWorks 7.0, reworking the fixes
+   for 64-bit implementations of Lispworks in 2.0.1 and incorrect
+   use of the sys:simple-int32-vector type.  Patch supplied by
+   Martin Simmons from LispWorks.
+
+ * Minor fix to prevent style-warnings for implementations with no
+   support for md5sum-string.
+
+
+Release 2.0.1
+=============
+
+ * This release fixes problems on 64-bit implementations of Lispworks,
+   where sys:int32 arithmetic can overflow 32-bit values and hence
+   yield wrong results.
+
+
+Release 2.0.0
+=============
+
+ * This release consolidates the various versions of md5 that have
+   been floating around, including versions with Lispworks
+   optimizations, the version maintained by Kevin M. Rosenberg, the
+   SBCL-optimized version, and various patches and fixes.
+
+ * This release tries to separate out string-handling from actual
+   byte-based MD5 hash generation.  Hence users who want to generate
+   hashes from strings should use the new md5sum-string function,
+   which will go through your implementation's external-format
+   handling to generate the actual byte-array that is hashed.  Usage
+   of md5sum-sequence for strings and md5sum-stream for character
+   streams is deprecated, since it only ever worked correctly for 8bit
+   characters in most implementations.
+
+ * This release should work correctly on at least recentish versions
+   of CMUCL, SBCL, ECL, CCL, Lispworks, AllegroCL and ABCL and
+   generally most other conforming implementations.  It has been
+   optimized for CMUCL, SBCL and Lispworks, and should work with
+   adequate performance on most 64bit implementations.  Performance on
+   other 32bit implementations is still sub-optimal if they don't
+   support unboxed bit-operations on (unsigned-byte 32).
diff --git a/deps/md5/README b/deps/md5/README
new file mode 100644 (file)
index 0000000..26e4fea
--- /dev/null
@@ -0,0 +1,22 @@
+This package implements The MD5 Message-Digest Algorithm, as defined
+in RFC 1321 by R. Rivest, published April 1992.
+
+It was originally written by Pierre R. Mai, with copious input from
+the cmucl-help mailing-list hosted at cons.org, in November 2001 and
+has been placed into the public domain.  In the meantime various fixes
+and improvements for other implementations as well as maintenance have
+been provided by Christophe Rhodes, Alexey Dejneka, Nathan Froyd,
+Andreas Fuchs, John Desoi, Dmitriy Ivanov, and Kevin M. Rosenberg, and
+have been reintegrated into this consolidated version by Pierre R. Mai.
+
+WARNING: The MD5 Message-Digest Algorithm has been compromised as a
+cryptographically secure hash for some time, with known theoretical
+and practical attacks.  Therefore use of this implemenation is only
+recommended for legacy uses or uses which do not require a
+cryptographically secure hash.  Use one of the newer SHA-2 and SHA-3
+secure hash standards, or whatever is currently deemed
+cryptographically secure for all other uses.
+
+This software is "as is", and has no warranty of any kind.  The
+authors assume no responsibility for the consequences of any use of
+this software.
diff --git a/deps/md5/md5.asd b/deps/md5/md5.asd
new file mode 100755 (executable)
index 0000000..6b80497
--- /dev/null
@@ -0,0 +1,23 @@
+;;;; MD5 --- RFC 1321 The MD5 Message-Digest Algorithm
+
+(cl:in-package #:cl-user)
+
+;;;; %File Description:
+;;;; 
+;;;; This file contains the system definition form for the MD5
+;;;; Library.  System definitions use the ASDF system definition
+;;;; facility.
+;;;; 
+
+(asdf:defsystem "md5"
+  :description "The MD5 Message-Digest Algorithm RFC 1321"
+  :author "Pierre R. Mai <pmai@pmsf.de>"
+  :maintainer "Pierre R. Mai <pmai@pmsf.de>"
+  :licence "Public Domain"
+  :version "2.0.3"
+  :depends-on (#+sbcl "sb-rotate-byte"
+               #-(or :cmu :sbcl
+                     (and :lispworks (not :lispworks4))
+                     :ccl :allegro)
+               "flexi-streams")
+  :components ((:file "md5")))
diff --git a/deps/md5/md5.lisp b/deps/md5/md5.lisp
new file mode 100755 (executable)
index 0000000..05ed5d9
--- /dev/null
@@ -0,0 +1,976 @@
+;;;; This file implements The MD5 Message-Digest Algorithm, as defined in
+;;;; RFC 1321 by R. Rivest, published April 1992.
+;;;;
+;;;; It was originally written by Pierre R. Mai, with copious input
+;;;; from the cmucl-help mailing-list hosted at cons.org, in November
+;;;; 2001 and has been placed into the public domain.  In the meantime
+;;;; various fixes and improvements for other implementations as well
+;;;; as maintenance have been provided by Christophe Rhodes, Alexey
+;;;; Dejneka, Nathan Froyd, Andreas Fuchs, John Desoi, Dmitriy Ivanov,
+;;;; and Kevin M. Rosenberg, and have been reintegrated into this
+;;;; consolidated version by Pierre R. Mai.
+;;;;
+;;;; WARNING: The MD5 Message-Digest Algorithm has been compromised as
+;;;; a cryptographically secure hash for some time, with known
+;;;; theoretical and practical attacks.  Therefore use of this
+;;;; implemenation is only recommended for legacy uses or uses which
+;;;; do not require a cryptographically secure hash.  Use one of the
+;;;; newer SHA-2 and SHA-3 secure hash standards, or whatever is
+;;;; currently deemed cryptographically secure for all other uses.
+;;;;
+;;;; $Id$
+;;;;
+;;;; While the implementation should work on all conforming Common
+;;;; Lisp implementations, it has originally been optimized for CMU
+;;;; CL, where it achieved comparable performance to the standard
+;;;; md5sum utility (within a factor of 1.5 or less on iA32 and
+;;;; UltraSparc hardware).
+;;;;
+;;;; Currently, this implementation has also been optimized for SBCL
+;;;; and LispWorks.
+;;;;
+;;;; Since the implementation makes heavy use of arithmetic on
+;;;; (unsigned-byte 32) numbers, acceptable performance is likely only
+;;;; on CL implementations that support unboxed arithmetic on such
+;;;; numbers in some form.  This should include most 64bit CL
+;;;; implementations.  For other CL implementations a 16bit
+;;;; implementation of MD5 is probably more suitable.
+;;;;
+;;;; The code implements correct operation for files/sequences of
+;;;; unbounded size as is, at the cost of having to do a single
+;;;; generic integer addition for each call to update-md5-state.  If
+;;;; you call update-md5-state frequently with little data, this can
+;;;; pose a performance problem.  If you can live with a size
+;;;; restriction of 512 MB, then you can enable fast fixnum arithmetic
+;;;; by putting :md5-small-length onto *features* prior to compiling
+;;;; this file.
+;;;;
+;;;; Testing code can be compiled by including :md5-testing on
+;;;; *features* prior to compilation.  In that case evaluating
+;;;; (md5::test-rfc1321) will run all the test-cases present in
+;;;; Appendix A.5 of RFC 1321 and report on the results.
+;;;; Evaluating (md5::test-other) will run further test-cases
+;;;; gathered by the author to cover regressions, etc.
+;;;;
+;;;; This software is "as is", and has no warranty of any kind.  The
+;;;; authors assume no responsibility for the consequences of any use
+;;;; of this software.
+
+(cl:defpackage #:md5 (:use #:cl)
+  (:export
+   ;; Low-Level types and functions
+   #:md5-regs #:initial-md5-regs #:md5regs-digest
+   #:update-md5-block #:fill-block #:fill-block-ub8 #:fill-block-char
+   ;; Mid-Level types and functions
+   #:md5-state #:md5-state-p #:make-md5-state
+   #:update-md5-state #:finalize-md5-state
+   ;; High-Level functions on sequences, streams and files
+   #:md5sum-sequence #:md5sum-string #:md5sum-stream #:md5sum-file))
+
+(cl:in-package #:md5)
+
+#+cmu
+(eval-when (:compile-toplevel)
+  (defparameter *old-expansion-limit* ext:*inline-expansion-limit*)
+  (setq ext:*inline-expansion-limit* (max ext:*inline-expansion-limit* 1000)))
+
+#+cmu
+(eval-when (:compile-toplevel :execute)
+  (defparameter *old-features* *features*)
+  (pushnew (c:backend-byte-order c:*target-backend*) *features*))
+
+#+sbcl
+(eval-when (:compile-toplevel :execute)
+  (defparameter *old-features* *features*)
+  (pushnew sb-c:*backend-byte-order* *features*))
+
+#+(and :lispworks (or (not :lispworks4) :lispworks4.4))
+(eval-when (:compile-toplevel :execute)
+  (defparameter *old-features* *features*)
+  (pushnew :lw-int32 *features*)
+  (defmacro lw-int32-no-overflow (value)
+    ;; Prevent overflow in 64-bit prior to LispWorks 7.0.
+    #+(and :lispworks-64bit (or :lispworks5 :lispworks6))
+    `(sys:int32>> (sys:int32<< ,value #.(sys:integer-to-int32 32))
+                  #.(sys:integer-to-int32 32))
+    #-(and :lispworks-64bit (or :lispworks5 :lispworks6))
+    value))
+
+;;; Section 2:  Basic Datatypes
+
+(deftype ub32 ()
+  "Corresponds to the 32bit quantity word of the MD5 Spec"
+  #+lw-int32 'sys:int32
+  #-lw-int32 '(unsigned-byte 32))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defmacro assemble-ub32 (a b c d)
+    "Assemble an ub32 value from the given (unsigned-byte 8) values,
+where a is the intended low-order byte and d the high-order byte."
+    #+lw-int32
+    `(lw-int32-no-overflow
+      (sys:int32-logior (sys:int32<< ,d 24)
+                        (sys:int32-logior (sys:int32<< ,c 16)
+                                          (sys:int32-logior (sys:int32<< ,b 8) ,a))))
+    #-lw-int32
+    `(the ub32 (logior (ash ,d 24) (ash ,c 16) (ash ,b 8) ,a))))
+
+(deftype ub32-vector (length)
+  #+lw-int32 (declare (ignore length))
+  #+lw-int32 'sys:simple-int32-vector
+  #-lw-int32 `(simple-array (unsigned-byte 32) (,length)))
+
+(defmacro make-ub32-vector (length &rest args)
+  #+lw-int32 `(sys:make-simple-int32-vector ,length ,@args)
+  #-lw-int32 `(make-array ,length :element-type 'ub32 ,@args))
+
+(defmacro ub32-aref (vector index)
+  #+lw-int32
+  `(sys:int32-aref ,vector ,index)
+  #-lw-int32
+  `(aref ,vector ,index))
+
+;;; Section 3.4:  Auxilliary functions
+
+(declaim (inline f g h i)
+         (ftype (function (ub32 ub32 ub32) ub32) f g h i))
+
+(defun f (x y z)
+  (declare (type ub32 x y z)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0) #+lw-int32 (float 0)))
+  #+cmu
+  (kernel:32bit-logical-or (kernel:32bit-logical-and x y)
+                           (kernel:32bit-logical-andc1 x z))
+  #+lw-int32
+  (sys:int32-logior (sys:int32-logand x y) (sys:int32-logandc1 x z))
+  #-(or :cmu :lw-int32)
+  (logior (logand x y) (logandc1 x z)))
+
+(defun g (x y z)
+  (declare (type ub32 x y z)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0) #+lw-int32 (float 0)))
+  #+cmu
+  (kernel:32bit-logical-or (kernel:32bit-logical-and x z)
+                           (kernel:32bit-logical-andc2 y z))
+  #+lw-int32
+  (sys:int32-logior (sys:int32-logand x z) (sys:int32-logandc2 y z))
+  #-(or :cmu :lw-int32)
+  (logior (logand x z) (logandc2 y z)))
+
+(defun h (x y z)
+  (declare (type ub32 x y z)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0) #+lw-int32 (float 0)))
+  #+cmu
+  (kernel:32bit-logical-xor x (kernel:32bit-logical-xor y z))
+  #+lw-int32
+  (sys:int32-logxor x (sys:int32-logxor y z))
+  #-(or :cmu :lw-int32)
+  (logxor x y z))
+
+(defun i (x y z)
+  (declare (type ub32 x y z)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0) #+lw-int32 (float 0)))
+  #+cmu
+  (kernel:32bit-logical-xor y (kernel:32bit-logical-orc2 x z))
+  #+lw-int32
+  (lw-int32-no-overflow (sys:int32-logxor y (sys:int32-logorc2 x z)))
+  #-(or :cmu :lw-int32)
+  (ldb (byte 32 0) (logxor y (logorc2 x z))))
+
+(declaim (inline mod32+)
+         (ftype (function (ub32 ub32) ub32) mod32+))
+(defun mod32+ (a b)
+  (declare (type ub32 a b)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0) #+lw-int32 (float 0)))
+  #+lw-int32
+  (lw-int32-no-overflow (sys:int32+ a b))
+  #-lw-int32
+  (ldb (byte 32 0) (+ a b)))
+
+#+cmu
+(define-compiler-macro mod32+ (a b)
+  `(ext:truly-the ub32 (+ ,a ,b)))
+
+;;; Dunno why we need this, but without it MOD32+ wasn't being
+;;; inlined.  Oh well.  -- CSR, 2003-09-14
+#+sbcl
+(define-compiler-macro mod32+ (a b)
+  `(ldb (byte 32 0) (+ ,a ,b)))
+
+#+lw-int32
+(declaim (inline int32>>logical)
+         (ftype (function (sys:int32 (unsigned-byte 5)) sys:int32) int32>>logical))
+#+lw-int32
+(defun int32>>logical (a s)
+  (declare (type ub32 a) (type (unsigned-byte 5) s)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0) #+lw-int32 (float 0)))
+  (if (sys:int32-minusp a)
+      (sys:int32-logandc2 (sys:int32>> a s) (sys:int32<< -1 (- 32 s)))
+      (sys:int32>> a s)))
+
+(declaim (inline rol32)
+         (ftype (function (ub32 (unsigned-byte 5)) ub32) rol32))
+(defun rol32 (a s)
+  (declare (type ub32 a) (type (unsigned-byte 5) s)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0) #+lw-int32 (float 0)))
+  #+cmu
+  (kernel:32bit-logical-or #+little-endian (kernel:shift-towards-end a s)
+                           #+big-endian (kernel:shift-towards-start a s)
+                           (ash a (- s 32)))
+  #+sbcl
+  (sb-rotate-byte:rotate-byte s (byte 32 0) a)
+  #+lw-int32
+  (sys:int32-logior (lw-int32-no-overflow (sys:int32<< a s))
+                    (int32>>logical a (- 32 s)))
+  #-(or :cmu :sbcl :lw-int32)
+  (logior (ldb (byte 32 0) (ash a s)) (ash a (- s 32))))
+
+;;; Section 3.4:  Table T
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defparameter *t* (make-array 64 :element-type 'ub32
+                                :initial-contents
+                                (loop for i from 1 to 64
+                                      collect
+                                      (truncate
+                                       (* 4294967296
+                                          (abs (sin (float i 0.0d0)))))))))
+
+;;; Section 3.4:  Helper Macro for single round definitions
+
+#-lw-int32
+(defmacro with-md5-round ((op block) &rest clauses)
+  (loop for (a b c d k s i) in clauses
+        collect
+        `(setq ,a (mod32+ ,b (rol32 (mod32+ (mod32+ ,a (,op ,b ,c ,d))
+                                            (mod32+ (ub32-aref ,block ,k)
+                                                    ,(aref *t* (1- i))))
+                                    ,s)))
+        into result
+        finally
+        (return `(progn ,@result))))
+
+#+lw-int32
+(defmacro with-md5-round ((op block) &rest clauses)
+  (loop for (a b c d k s i) in clauses
+        collect
+        `(setq ,a (mod32+ ,b (rol32 (mod32+ (mod32+ ,a (,op ,b ,c ,d))
+                                            (mod32+ (ub32-aref ,block ,k)
+                                                    (sys:integer-to-int32 
+                                                     ,(let ((t-val (aref *t* (1- i))))
+                                                        (dpb (ldb (byte 32 0) t-val)
+                                                             (byte 32 0) 
+                                                             (if (logbitp 31 t-val)
+                                                                 -1
+                                                                 0))))))
+                                    ,s)))
+        into result
+        finally
+        (return `(progn ,@result))))
+
+;;; Section 3.3:  (Initial) MD5 Working Set
+
+(deftype md5-regs ()
+  "The working state of the MD5 algorithm, which contains the 4 32-bit
+registers A, B, C and D."
+  `(ub32-vector 4))
+
+(defmacro md5-regs-a (regs)
+  `(ub32-aref ,regs 0))
+
+(defmacro md5-regs-b (regs)
+  `(ub32-aref ,regs 1))
+
+(defmacro md5-regs-c (regs)
+  `(ub32-aref ,regs 2))
+
+(defmacro md5-regs-d (regs)
+  `(ub32-aref ,regs 3))
+
+(defconstant +md5-magic-a+ (assemble-ub32 #x01 #x23 #x45 #x67)
+  "Initial value of Register A of the MD5 working state.")
+(defconstant +md5-magic-b+ (assemble-ub32 #x89 #xab #xcd #xef)
+  "Initial value of Register B of the MD5 working state.")
+(defconstant +md5-magic-c+ (assemble-ub32 #xfe #xdc #xba #x98)
+  "Initial value of Register C of the MD5 working state.")
+(defconstant +md5-magic-d+ (assemble-ub32 #x76 #x54 #x32 #x10)
+  "Initial value of Register D of the MD5 working state.")
+
+(declaim (inline initial-md5-regs))
+(defun initial-md5-regs ()
+  "Create the initial working state of an MD5 run."
+  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0) #+lw-int32 (float 0)))
+  (let ((regs (make-ub32-vector 4)))
+    (declare (type md5-regs regs))
+    (setf (md5-regs-a regs) +md5-magic-a+
+          (md5-regs-b regs) +md5-magic-b+
+          (md5-regs-c regs) +md5-magic-c+
+          (md5-regs-d regs) +md5-magic-d+)
+    regs))
+
+;;; Section 3.4:  Operation on 16-Word Blocks
+
+(deftype md5-block ()
+  "The basic 16x32-bit word blocks that MD5 operates on."
+  `(ub32-vector 16))
+
+(defun update-md5-block (regs block)
+  "This is the core part of the MD5 algorithm.  It takes a complete 16
+word block of input, and updates the working state in A, B, C, and D
+accordingly."
+  (declare (type md5-regs regs)
+           (type md5-block block)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0) #+lw-int32 (float 0)))
+  (let ((A (md5-regs-a regs)) (B (md5-regs-b regs))
+        (C (md5-regs-c regs)) (D (md5-regs-d regs)))
+    (declare (type ub32 A B C D))
+    ;; Round 1
+    (with-md5-round (f block)
+      (A B C D  0  7  1)(D A B C  1 12  2)(C D A B  2 17  3)(B C D A  3 22  4)
+      (A B C D  4  7  5)(D A B C  5 12  6)(C D A B  6 17  7)(B C D A  7 22  8)
+      (A B C D  8  7  9)(D A B C  9 12 10)(C D A B 10 17 11)(B C D A 11 22 12)
+      (A B C D 12  7 13)(D A B C 13 12 14)(C D A B 14 17 15)(B C D A 15 22 16))
+    ;; Round 2
+    (with-md5-round (g block)
+      (A B C D  1  5 17)(D A B C  6  9 18)(C D A B 11 14 19)(B C D A  0 20 20)
+      (A B C D  5  5 21)(D A B C 10  9 22)(C D A B 15 14 23)(B C D A  4 20 24)
+      (A B C D  9  5 25)(D A B C 14  9 26)(C D A B  3 14 27)(B C D A  8 20 28)
+      (A B C D 13  5 29)(D A B C  2  9 30)(C D A B  7 14 31)(B C D A 12 20 32))
+    ;; Round 3
+    (with-md5-round (h block)
+      (A B C D  5  4 33)(D A B C  8 11 34)(C D A B 11 16 35)(B C D A 14 23 36)
+      (A B C D  1  4 37)(D A B C  4 11 38)(C D A B  7 16 39)(B C D A 10 23 40)
+      (A B C D 13  4 41)(D A B C  0 11 42)(C D A B  3 16 43)(B C D A  6 23 44)
+      (A B C D  9  4 45)(D A B C 12 11 46)(C D A B 15 16 47)(B C D A  2 23 48))
+    ;; Round 4
+    (with-md5-round (i block)
+      (A B C D  0  6 49)(D A B C  7 10 50)(C D A B 14 15 51)(B C D A  5 21 52)
+      (A B C D 12  6 53)(D A B C  3 10 54)(C D A B 10 15 55)(B C D A  1 21 56)
+      (A B C D  8  6 57)(D A B C 15 10 58)(C D A B  6 15 59)(B C D A 13 21 60)
+      (A B C D  4  6 61)(D A B C 11 10 62)(C D A B  2 15 63)(B C D A  9 21 64))
+    ;; Update and return
+    (setf (md5-regs-a regs) (mod32+ (md5-regs-a regs) A)
+          (md5-regs-b regs) (mod32+ (md5-regs-b regs) B)
+          (md5-regs-c regs) (mod32+ (md5-regs-c regs) C)
+          (md5-regs-d regs) (mod32+ (md5-regs-d regs) D))
+    regs))
+
+;;; Section 3.4:  Converting 8bit-vectors into 16-Word Blocks
+
+(declaim (inline fill-block fill-block-ub8 fill-block-char))
+(defun fill-block-ub8 (block buffer offset)
+  "Convert a complete 64 (unsigned-byte 8) input vector segment
+starting from `offset' into the given 16 word MD5 block."
+  (declare (type (integer 0 #.(- most-positive-fixnum 64)) offset)
+           (type md5-block block)
+           (type (simple-array (unsigned-byte 8) (*)) buffer)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0)
+                     #+lw-int32 (float 0) #+lw-int32 (hcl:fixnum-safety 0)))
+  #+(and :cmu :little-endian)
+  (kernel:bit-bash-copy
+   buffer (+ (* vm:vector-data-offset vm:word-bits) (* offset vm:byte-bits))
+   block (* vm:vector-data-offset vm:word-bits)
+   (* 64 vm:byte-bits))
+  #+(and :sbcl :little-endian)
+  (sb-kernel:ub8-bash-copy buffer offset block 0 64)
+  #-(or (and :sbcl :little-endian) (and :cmu :little-endian))
+  (loop for i of-type (integer 0 16) from 0
+        for j of-type (integer 0 #.most-positive-fixnum)
+        from offset to (+ offset 63) by 4
+        do
+        (setf (ub32-aref block i)
+              (assemble-ub32 (aref buffer j)
+                             (aref buffer (+ j 1))
+                             (aref buffer (+ j 2))
+                             (aref buffer (+ j 3))))))
+
+(defun fill-block-char (block buffer offset)
+  "DEPRECATED: Convert a complete 64 character input string segment
+starting from `offset' into the given 16 word MD5 block."
+  (declare (type (integer 0 #.(- most-positive-fixnum 64)) offset)
+           (type md5-block block)
+           (type simple-string buffer)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0)
+                     #+lw-int32 (float 0) #+lw-int32 (hcl:fixnum-safety 0)))
+  #+(and :cmu :little-endian)
+  (kernel:bit-bash-copy
+   buffer (+ (* vm:vector-data-offset vm:word-bits) (* offset vm:byte-bits))
+   block (* vm:vector-data-offset vm:word-bits)
+   (* 64 vm:byte-bits))
+  #+(and :sbcl :little-endian)
+  (sb-kernel:ub8-bash-copy buffer offset block 0 64)
+  #-(or (and :sbcl :little-endian) (and :cmu :little-endian))
+  (loop for i of-type (integer 0 16) from 0
+        for j of-type (integer 0 #.most-positive-fixnum)
+        from offset to (+ offset 63) by 4
+        do
+        (setf (ub32-aref block i)
+              (assemble-ub32 (char-code (schar buffer j))
+                             (char-code (schar buffer (+ j 1)))
+                             (char-code (schar buffer (+ j 2)))
+                             (char-code (schar buffer (+ j 3)))))))
+
+(defun fill-block (block buffer offset)
+  "Convert a complete 64 byte input vector segment into the given 16
+word MD5 block.  This currently works on (unsigned-byte 8) and
+character simple-arrays, via the functions `fill-block-ub8' and
+`fill-block-char' respectively.  Note that it will not work correctly
+on character simple-arrays if `char-code-limit' is greater than 256."
+  (declare (type (integer 0 #.(- most-positive-fixnum 64)) offset)
+           (type md5-block block)
+           (type (simple-array * (*)) buffer)
+           (optimize (speed 3) (safety 0) (space 0) (debug 0)
+                     #+lw-int32 (float 0) #+lw-int32 (hcl:fixnum-safety 0)))
+  (etypecase buffer
+    ((simple-array (unsigned-byte 8) (*))
+     (fill-block-ub8 block buffer offset))
+    (simple-string
+     (fill-block-char block buffer offset))))
+
+;;; Section 3.5:  Message Digest Output
+
+(declaim (inline md5regs-digest))
+(defun md5regs-digest (regs)
+  "Create the final 16 byte message-digest from the MD5 working state
+in `regs'.  Returns a (simple-array (unsigned-byte 8) (16))."
+  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0)
+                     #+lw-int32 (float 0) #+lw-int32 (hcl:fixnum-safety 0))
+           (type md5-regs regs))
+  (let ((result (make-array 16 :element-type '(unsigned-byte 8))))
+    (declare (type (simple-array (unsigned-byte 8) (16)) result))
+    (macrolet ((frob (reg offset)
+                 (let ((var (gensym)))
+                   `(let ((,var #+lw-int32 (ldb (byte 32 0) (sys:int32-to-integer ,reg))
+                                #-lw-int32 ,reg))
+                      (declare (type (unsigned-byte 32) ,var))
+                      (setf
+                       (aref result ,offset) (ldb (byte 8 0) ,var)
+                       (aref result ,(+ offset 1)) (ldb (byte 8 8) ,var)
+                       (aref result ,(+ offset 2)) (ldb (byte 8 16) ,var)
+                       (aref result ,(+ offset 3)) (ldb (byte 8 24) ,var))))))
+      (frob (md5-regs-a regs) 0)
+      (frob (md5-regs-b regs) 4)
+      (frob (md5-regs-c regs) 8)
+      (frob (md5-regs-d regs) 12))
+    result))
+
+;;; Mid-Level Drivers
+
+(locally
+  (declare (optimize (speed 3) (safety 1) (space 0) (debug 1)
+                     #+lw-int32 (float 0)))
+(defstruct (md5-state
+             (:constructor make-md5-state ())
+             (:copier))
+  (regs (initial-md5-regs) :type md5-regs :read-only t)
+  (amount 0 :type
+          #-md5-small-length (integer 0 *)
+          #+md5-small-length (unsigned-byte 29))
+  (block (make-ub32-vector 16) :read-only t :type md5-block)
+  (buffer (make-array 64 :element-type '(unsigned-byte 8)) :read-only t
+         :type (simple-array (unsigned-byte 8) (64)))
+  (buffer-index 0 :type (integer 0 63))
+  (finalized-p nil))
+)
+
+(declaim (inline copy-to-buffer))
+(defun copy-to-buffer (from from-offset count buffer buffer-offset)
+  "Copy a partial segment from input vector `from' starting at
+`from-offset' and copying `count' elements into the 64 byte buffer
+starting at `buffer-offset'."
+  (declare (optimize (speed 3) (safety 0) (space 0) (debug 0)
+                     #+lw-int32 (float 0) #+lw-int32 (hcl:fixnum-safety 0))
+           (type (unsigned-byte 29) from-offset)
+           (type (integer 0 63) count buffer-offset)
+           (type (simple-array * (*)) from)
+           (type (simple-array (unsigned-byte 8) (64)) buffer))
+  #+cmu
+  (kernel:bit-bash-copy
+   from (+ (* vm:vector-data-offset vm:word-bits) (* from-offset vm:byte-bits))
+   buffer (+ (* vm:vector-data-offset vm:word-bits)
+             (* buffer-offset vm:byte-bits))
+   (* count vm:byte-bits))
+  #+sbcl
+  (sb-kernel:ub8-bash-copy from from-offset buffer buffer-offset count)
+  #-(or :cmu :sbcl)
+  (etypecase from
+    (simple-string
+     (loop for buffer-index of-type (integer 0 64) from buffer-offset
+           for from-index of-type fixnum from from-offset
+           below (+ from-offset count)
+           do
+           (setf (aref buffer buffer-index)
+                 (char-code (schar (the simple-string from) from-index)))))
+    ((simple-array (unsigned-byte 8) (*))
+     (loop for buffer-index of-type (integer 0 64) from buffer-offset
+           for from-index of-type fixnum from from-offset
+           below (+ from-offset count)
+           do
+           (setf (aref buffer buffer-index)
+                 (aref (the (simple-array (unsigned-byte 8) (*)) from)
+                       from-index))))))
+
+(defun update-md5-state (state sequence &key (start 0) (end (length sequence)))
+  "Update the given md5-state from `sequence', which is either a
+simple-string or a simple-array with element-type (unsigned-byte 8),
+bounded by `start' and `end', which must be numeric bounding-indices.
+Note that usage on simple-strings is DEPRECATED, since this will not
+work correctly if `char-code-limit' is more than 256.  String input
+should be converted to (unsigned-byte 8) simple-arrays with
+external-format conversion routines beforehand."
+  (declare (type md5-state state)
+           (type (simple-array * (*)) sequence)
+           (type fixnum start end)
+           (optimize (speed 3) (safety 1) (space 0) (debug 1)
+                     #+lw-int32 (float 0) #+lw-int32 (hcl:fixnum-safety 0)))
+  (locally 
+      (declare (optimize (safety 0) (debug 0)))
+    (let ((regs (md5-state-regs state))
+          (block (md5-state-block state))
+          (buffer (md5-state-buffer state))
+          (buffer-index (md5-state-buffer-index state))
+          (length (- end start)))
+      (declare (type md5-regs regs) (type fixnum length)
+               (type (integer 0 63) buffer-index)
+               (type md5-block block)
+               (type (simple-array (unsigned-byte 8) (64)) buffer))
+      ;; Handle old rest
+      (unless (zerop buffer-index)
+        (let ((amount (min (- 64 buffer-index) length)))
+          (declare (type (integer 0 63) amount))
+          (copy-to-buffer sequence start amount buffer buffer-index)
+          (setq start (the fixnum (+ start amount)))
+          (let ((new-index (+ buffer-index amount)))
+            (when (= new-index 64)
+              (fill-block-ub8 block buffer 0)
+              (update-md5-block regs block)
+              (setq new-index 0))
+            (when (>= start end)
+              (setf (md5-state-buffer-index state) new-index
+                    (md5-state-amount state)
+                    #-md5-small-length (+ (md5-state-amount state) length)
+                    #+md5-small-length (the (unsigned-byte 29)
+                                         (+ (md5-state-amount state) length)))
+              (return-from update-md5-state state)))))
+      ;; Handle main-part and new-rest
+      (etypecase sequence
+        ((simple-array (unsigned-byte 8) (*))
+           (locally
+               (declare (type (simple-array (unsigned-byte 8) (*)) sequence))
+             (loop for offset of-type (unsigned-byte 29) from start below end by 64
+                   until (< (- end offset) 64)
+                   do
+                (fill-block-ub8 block sequence offset)
+                (update-md5-block regs block)
+                   finally
+                (let ((amount (- end offset)))
+                  (unless (zerop amount)
+                    (copy-to-buffer sequence offset amount buffer 0))
+                  (setf (md5-state-buffer-index state) amount)))))
+        (simple-string
+           (locally
+               (declare (type simple-string sequence))
+             (loop for offset of-type (unsigned-byte 29) from start below end by 64
+                   until (< (- end offset) 64)
+                   do
+                (fill-block-char block sequence offset)
+                (update-md5-block regs block)
+                   finally
+                (let ((amount (- end offset)))
+                  (unless (zerop amount)
+                    (copy-to-buffer sequence offset amount buffer 0))
+                  (setf (md5-state-buffer-index state) amount))))))
+      (setf (md5-state-amount state)
+            #-md5-small-length (+ (md5-state-amount state) length)
+            #+md5-small-length (the (unsigned-byte 29)
+                                 (+ (md5-state-amount state) length)))
+      state)))
+
+(defun finalize-md5-state (state)
+  "If the given md5-state has not already been finalized, finalize it,
+by processing any remaining input in its buffer, with suitable padding
+and appended bit-length, as specified by the MD5 standard.
+
+The resulting MD5 message-digest is returned as an array of sixteen
+(unsigned-byte 8) values.  Calling `update-md5-state' after a call to
+`finalize-md5-state' results in unspecified behaviour."
+  (declare (type md5-state state)
+           (optimize (speed 3) (safety 1) (space 0) (debug 1) #+lw-int32 (float 0)))
+  (locally
+      (declare (optimize (safety 0) (debug 0)))
+    (or (md5-state-finalized-p state)
+        (let ((regs (md5-state-regs state))
+              (block (md5-state-block state))
+              (buffer (md5-state-buffer state))
+              (buffer-index (md5-state-buffer-index state))
+              (total-length (* 8 (md5-state-amount state))))
+          (declare (type md5-regs regs)
+                   (type (integer 0 63) buffer-index)
+                   (type md5-block block)
+                   (type (simple-array (unsigned-byte 8) (*)) buffer))
+          ;; Add mandatory bit 1 padding
+          (setf (aref buffer buffer-index) #x80)
+          ;; Fill with 0 bit padding
+          (loop for index of-type (integer 0 64)
+                  from (1+ buffer-index) below 64
+                do (setf (aref buffer index) #x00))
+          (fill-block-ub8 block buffer 0)
+          ;; Flush block first if length wouldn't fit
+          (when (>= buffer-index 56)
+            (update-md5-block regs block)
+            ;; Create new fully 0 padded block
+            (loop for index of-type (integer 0 16) from 0 below 16
+                  do (setf (ub32-aref block index) #x00000000)))
+          ;; Add 64bit message bit length
+          (setf (ub32-aref block 14) (ldb (byte 32 0) total-length))
+          #-md5-small-length
+          (setf (ub32-aref block 15) (ldb (byte 32 32) total-length))
+          ;; Flush last block
+          (update-md5-block regs block)
+          ;; Done, remember digest for later calls
+          (setf (md5-state-finalized-p state)
+                (md5regs-digest regs))))))
+
+;;; High-Level Drivers
+
+(defun md5sum-sequence (sequence &key (start 0) end)
+  "Calculate the MD5 message-digest of data in `sequence', which should
+be a 1d simple-array with element type (unsigned-byte 8).  On CMU CL
+and SBCL non-simple and non-1d arrays with this element-type are also
+supported.  Use with strings is DEPRECATED, since this will not work
+correctly on implementations with `char-code-limit' > 256 and ignores
+character-coding issues.  Use md5sum-string instead, or convert to the
+required (unsigned-byte 8) format through other means before-hand."
+  (declare (optimize (speed 3) (safety 3) (space 0) (debug 1))
+           (type vector sequence) (type fixnum start))
+  (locally
+      (declare (optimize (safety 1) (debug 0)))
+    (let ((state (make-md5-state)))
+      (declare (type md5-state state))
+      #+cmu
+      (let ((end (or end (length sequence))))
+        (lisp::with-array-data ((data sequence) (real-start start) (real-end end))
+          (declare (ignore real-end))
+          (update-md5-state state data :start real-start
+                            :end (+ real-start (- end start)))))
+      #+sbcl
+      (let ((end (or end (length sequence))))
+        (sb-kernel:with-array-data ((data sequence)
+                                    (real-start start)
+                                    (real-end end)
+                                    :check-fill-pointer t)
+          (declare (ignore real-end))
+          (update-md5-state state data :start real-start
+                            :end (+ real-start (- end start)))))
+      #-(or :cmu :sbcl)
+      (let ((real-end (or end (length sequence))))
+        (declare (type fixnum real-end))
+        (update-md5-state state sequence :start start :end real-end))
+      (finalize-md5-state state))))
+
+(defun md5sum-string (string &key (external-format :default) (start 0) end)
+  "Calculate the MD5 message-digest of the binary representation of
+`string' (as octets) in the external format specified by
+`external-format'. The boundaries `start' and `end' refer to character
+positions in the string, not to octets in the resulting binary
+representation.  The permissible external format specifiers are
+determined by the underlying implementation."
+  (declare (optimize (speed 3) (safety 3) (space 0) (debug 1))
+           (type string string) (type fixnum start)
+           (ignorable external-format))
+  (locally
+    (declare (optimize (safety 1) (debug 0)))
+    #+cmu
+    (md5sum-sequence
+     (stream:string-to-octets string
+                              :external-format external-format
+                              :start start :end end))
+    #+sbcl
+    (md5sum-sequence
+     (sb-ext:string-to-octets string
+                              :external-format external-format
+                              :start start :end end))
+    #+(and :lispworks (not :lispworks4))
+    (let ((external-format (system:merge-ef-specs external-format :utf-8)))
+      (if (equal (external-format:external-format-foreign-type external-format)
+                 '(unsigned-byte 8))
+          (md5sum-sequence 
+           (coerce (external-format:encode-lisp-string string external-format
+                                                       :start start :end end)
+                   '(simple-array (unsigned-byte 8) (*))))
+          (error "External Format ~S does not yield (unsigned-byte 8) vector!"
+                 external-format)))
+    #+ccl
+    (md5sum-sequence
+     (ccl:encode-string-to-octets string :external-format external-format
+                                  :start start :end end))
+    #+allegro
+    (md5sum-sequence
+     (excl:string-to-octets string :external-format external-format
+                             :null-terminate nil :start start :end end))
+    #-(or :cmu :sbcl (and :lispworks (not :lispworks4)) :ccl :allegro)
+    (if (<= char-code-limit 256)
+        (md5sum-sequence string :start start :end end)
+        (md5sum-sequence
+         (flexi-streams:string-to-octets string
+                                         :external-format
+                                         (if (eq external-format :default)
+                                             :UTF-8
+                                             external-format))))))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defconstant +buffer-size+ (* 128 1024)
+    "Size of internal buffer to use for `md5sum-stream' and `md5sum-file'
+operations.  This should be a multiple of 64, the MD5 block size."))
+
+(deftype buffer-index () `(integer 0 ,+buffer-size+))
+
+(defun md5sum-stream (stream)
+  "Calculate an MD5 message-digest of the contents of `stream'.  Its
+element-type has to be (unsigned-byte 8). Use on character streams is
+DEPRECATED, as this will not work correctly on implementations with
+`char-code-limit' > 256 and ignores character coding issues."
+  (declare (optimize (speed 3) (safety 3) (space 0) (debug 1)))
+  (locally
+      (declare (optimize (safety 1) (debug 0)))
+    (let ((state (make-md5-state)))
+      (declare (type md5-state state))
+      (cond
+        ((equal (stream-element-type stream) '(unsigned-byte 8))
+         (let ((buffer (make-array +buffer-size+
+                                   :element-type '(unsigned-byte 8))))
+           (declare (type (simple-array (unsigned-byte 8) (#.+buffer-size+))
+                          buffer))
+           (loop for bytes of-type buffer-index = (read-sequence buffer stream)
+                 do (update-md5-state state buffer :end bytes)
+                 until (< bytes +buffer-size+)
+                 finally
+              (return (finalize-md5-state state)))))
+        ((equal (stream-element-type stream) 'character)
+         (let ((buffer (make-string +buffer-size+)))
+           (declare (type (simple-string #.+buffer-size+) buffer))
+           (loop for bytes of-type buffer-index = (read-sequence buffer stream)
+                 do (update-md5-state state buffer :end bytes)
+                 until (< bytes +buffer-size+)
+                 finally
+              (return (finalize-md5-state state)))))
+        (t
+         (error "Unsupported stream element-type ~S for stream ~S."
+                (stream-element-type stream) stream))))))
+
+(defun md5sum-file (pathname)
+  "Calculate the MD5 message-digest of the file specified by `pathname'."
+  (declare (optimize (speed 3) (safety 3) (space 0) (debug 1)))
+  (with-open-file (stream pathname :element-type '(unsigned-byte 8))
+    (md5sum-stream stream)))
+
+#+md5-testing
+(defparameter *rfc1321-testsuite*
+  '(("" . "d41d8cd98f00b204e9800998ecf8427e")
+    ("a" ."0cc175b9c0f1b6a831c399e269772661")
+    ("abc" . "900150983cd24fb0d6963f7d28e17f72")
+    ("message digest" . "f96b697d7cb7938d525a2f31aaf161d0")
+    ("abcdefghijklmnopqrstuvwxyz" . "c3fcd3d76192e4007dfb496cca67e13b")
+    ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" .
+     "d174ab98d277d9f5a5611c2c9f419d9f")
+    ("12345678901234567890123456789012345678901234567890123456789012345678901234567890" .
+     "57edf4a22be3c955ac49da2e2107b67a"))
+  "AList of test input strings and stringified message-digests
+according to the test suite in Appendix A.5 of RFC 1321")
+
+#+md5-testing
+(defparameter *other-testsuite*
+  '(;; From padding bug report by Edi Weitz
+    ("1631901HERR BUCHHEISTERCITROEN NORD1043360796beckenbauer" .
+     "d734945e5930bb28859ccd13c830358b")
+    ;; Test padding for strings from 0 to 69*8 bits in size.
+    ("" . "d41d8cd98f00b204e9800998ecf8427e")
+    ("a" . "0cc175b9c0f1b6a831c399e269772661")
+    ("aa" . "4124bc0a9335c27f086f24ba207a4912")
+    ("aaa" . "47bce5c74f589f4867dbd57e9ca9f808")
+    ("aaaa" . "74b87337454200d4d33f80c4663dc5e5")
+    ("aaaaa" . "594f803b380a41396ed63dca39503542")
+    ("aaaaaa" . "0b4e7a0e5fe84ad35fb5f95b9ceeac79")
+    ("aaaaaaa" . "5d793fc5b00a2348c3fb9ab59e5ca98a")
+    ("aaaaaaaa" . "3dbe00a167653a1aaee01d93e77e730e")
+    ("aaaaaaaaa" . "552e6a97297c53e592208cf97fbb3b60")
+    ("aaaaaaaaaa" . "e09c80c42fda55f9d992e59ca6b3307d")
+    ("aaaaaaaaaaa" . "d57f21e6a273781dbf8b7657940f3b03")
+    ("aaaaaaaaaaaa" . "45e4812014d83dde5666ebdf5a8ed1ed")
+    ("aaaaaaaaaaaaa" . "c162de19c4c3731ca3428769d0cd593d")
+    ("aaaaaaaaaaaaaa" . "451599a5f9afa91a0f2097040a796f3d")
+    ("aaaaaaaaaaaaaaa" . "12f9cf6998d52dbe773b06f848bb3608")
+    ("aaaaaaaaaaaaaaaa" . "23ca472302f49b3ea5592b146a312da0")
+    ("aaaaaaaaaaaaaaaaa" . "88e42e96cc71151b6e1938a1699b0a27")
+    ("aaaaaaaaaaaaaaaaaa" . "2c60c24e7087e18e45055a33f9a5be91")
+    ("aaaaaaaaaaaaaaaaaaa" . "639d76897485360b3147e66e0a8a3d6c")
+    ("aaaaaaaaaaaaaaaaaaaa" . "22d42eb002cefa81e9ad604ea57bc01d")
+    ("aaaaaaaaaaaaaaaaaaaaa" . "bd049f221af82804c5a2826809337c9b")
+    ("aaaaaaaaaaaaaaaaaaaaaa" . "ff49cfac3968dbce26ebe7d4823e58bd")
+    ("aaaaaaaaaaaaaaaaaaaaaaa" . "d95dbfee231e34cccb8c04444412ed7d")
+    ("aaaaaaaaaaaaaaaaaaaaaaaa" . "40edae4bad0e5bf6d6c2dc5615a86afb")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaa" . "a5a8bfa3962f49330227955e24a2e67c")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaa" . "ae791f19bdf77357ff10bb6b0e97e121")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaa" . "aaab9c59a88bf0bdfcb170546c5459d6")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaa" . "b0f0545856af1a340acdedce23c54b97")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa" . "f7ce3d7d44f3342107d884bfa90c966a")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" . "59e794d45697b360e18ba972bada0123")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" . "3b0845db57c200be6052466f87b2198a")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" . "5eca9bd3eb07c006cd43ae48dfde7fd3")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" . "b4f13cb081e412f44e99742cb128a1a5")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" . "4c660346451b8cf91ef50f4634458d41")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "11db24dc3f6c2145701db08625dd6d76")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "80dad3aad8584778352c68ab06250327")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "1227fe415e79db47285cb2689c93963f")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "8e084f489f1bdf08c39f98ff6447ce6d")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "08b2f2b0864bac1ba1585043362cbec9")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "4697843037d962f62a5a429e611e0f5f")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "10c4da18575c092b486f8ab96c01c02f")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "af205d729450b663f48b11d839a1c8df")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "0d3f91798fac6ee279ec2485b25f1124")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "4c3c7c067634daec9716a80ea886d123")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "d1e358e6e3b707282cdd06e919f7e08c")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "8c6ded4f0af86e0a7e301f8a716c4363")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "4c2d8bcb02d982d7cb77f649c0a2dea8")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "bdb662f765cd310f2a547cab1cfecef6")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "08ff5f7301d30200ab89169f6afdb7af")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "6eb6a030bcce166534b95bc2ab45d9cf")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "1bb77918e5695c944be02c16ae29b25e")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "b6fe77c19f0f0f4946c761d62585bfea")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "e9e7e260dce84ffa6e0e7eb5fd9d37fc")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "eced9e0b81ef2bba605cbc5e2e76a1d0")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "ef1772b6dff9a122358552954ad0df65")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "3b0c8ac703f828b04c6c197006d17218")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "652b906d60af96844ebd21b674f35e93")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "dc2f2f2462a0d72358b2f99389458606")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "762fc2665994b217c52c3c2eb7d9f406")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "cc7ed669cf88f201c3297c6a91e1d18d")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "cced11f7bbbffea2f718903216643648")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "24612f0ce2c9d2cf2b022ef1e027a54f")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "b06521f39153d618550606be297466d5")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "014842d480b571495a4a0363793f7367")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "c743a45e0d2e6a95cb859adae0248435")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "def5d97e01e1219fb2fc8da6c4d6ba2f")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "92cb737f8687ccb93022fdb411a77cca")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "a0d1395c7fb36247bfe2d49376d9d133")
+    ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" .
+     "ab75504250558b788f99d1ebd219abf2"))
+  "AList of test input strings and stringified message-digests
+according to my additional test suite")
+
+#+md5-testing
+(defparameter *ascii-map*
+  '((#\A .  65) (#\B .  66) (#\C .  67) (#\D .  68) (#\E .  69) (#\F .  70) 
+    (#\G .  71) (#\H .  72) (#\I .  73) (#\J .  74) (#\K .  75) (#\L .  76)
+    (#\M .  77) (#\N .  78) (#\O .  79) (#\P .  80) (#\Q .  81) (#\R .  82)
+    (#\S .  83) (#\T .  84) (#\U .  85) (#\V .  86) (#\W .  87) (#\X .  88)
+    (#\Y .  89) (#\Z .  90) (#\a .  97) (#\b .  98) (#\c .  99) (#\d . 100)
+    (#\e . 101) (#\f . 102) (#\g . 103) (#\h . 104) (#\i . 105) (#\j . 106)
+    (#\k . 107) (#\l . 108) (#\m . 109) (#\n . 110) (#\o . 111) (#\p . 112)
+    (#\q . 113) (#\r . 114) (#\s . 115) (#\t . 116) (#\u . 117) (#\v . 118)
+    (#\w . 119) (#\x . 120) (#\y . 121) (#\z . 122) (#\0 .  48) (#\1 .  49)
+    (#\2 .  50) (#\3 .  51) (#\4 .  52) (#\5 .  53) (#\6 .  54) (#\7 .  55)
+    (#\8 .  56) (#\9 .  57) (#\Space .  32))
+  "AList mapping string characters to ASCII codes for safe binary testing.")
+        
+#+md5-testing
+(defun test-with-testsuite (testsuite)
+  (flet ((to-vector (string)
+           (loop with result = (make-array (list (length string)) 
+                                           :element-type '(unsigned-byte 8))
+                 for char across string
+                 for byte = (or (cdr (assoc char *ascii-map*)) 
+                                (error "Missing Char in *ascii-map*: ~S" char))
+                 for index upfrom 0
+                 do (setf (aref result index) byte)
+                 finally (return result)))
+         (incremental-md5sum (input)
+           (loop with state = (make-md5-state)
+                 for index from 0 below (length input)
+                 do (update-md5-state state input :start index :end (1+ index))
+                 finally (return (finalize-md5-state state)))))
+    (loop for count from 1
+          for (source . md5-string) in testsuite
+          for binary-source = (to-vector source)
+          for md5-digest = (md5sum-sequence binary-source)
+          for md5-digest-inc = (incremental-md5sum binary-source)
+          for md5-result-string = (format nil "~(~{~2,'0X~}~)"
+                                              (map 'list #'identity md5-digest))
+          for md5-result-string-inc = (format nil 
+                                              "~(~{~2,'0X~}~)"
+                                              (map 'list #'identity md5-digest-inc))
+          do
+       (format
+         *trace-output*
+         "~2&Test-Case ~D:~%  Input: ~S~%  Required: ~A~%  Returned: ~A~%  ~
+          Returned incrementally: ~A~%"
+         count source md5-string md5-result-string md5-result-string-inc)
+          when (and (string= md5-string md5-result-string)
+                    (string= md5-string md5-result-string-inc))
+            do (format *trace-output* "  OK~%")
+          else
+            count 1 into failed
+            and do (format *trace-output* "  FAILED~%")
+          finally
+       (format *trace-output*
+               "~2&~[All ~D test cases succeeded~:;~:*~D of ~D test cases failed~].~%"
+               failed (1- count))
+       (return (zerop failed)))))
+
+#+md5-testing
+(defun test-rfc1321 ()
+  (test-with-testsuite *rfc1321-testsuite*))
+
+#+md5-testing
+(defun test-other ()
+  (test-with-testsuite *other-testsuite*))
+
+#+cmu
+(eval-when (:compile-toplevel :execute)
+  (setq *features* *old-features*))
+
+#+cmu
+(eval-when (:compile-toplevel)
+  (setq ext:*inline-expansion-limit* *old-expansion-limit*))
+
+#+sbcl
+(eval-when (:compile-toplevel :execute)
+  (setq *features* *old-features*))
+
+#+(and :lispworks (or (not :lispworks4) :lispworks4.4))
+(eval-when (:compile-toplevel :execute)
+  (setq *features* *old-features*))
diff --git a/deps/rfc2388/packages.lisp b/deps/rfc2388/packages.lisp
new file mode 100644 (file)
index 0000000..5d110f2
--- /dev/null
@@ -0,0 +1,46 @@
+;;;; -*- mode: LISP; package: RFC2388 -*-
+;;;; Copyright (c) 2003 Janis Dzerins
+;;;;
+;;;; Redistribution and use in source and binary forms, with or without
+;;;; modification, are permitted provided that the following conditions
+;;;; are met:
+;;;; 1. Redistributions of source code must retain the above copyright
+;;;;    notice, this list of conditions and the following disclaimer.
+;;;; 2. Redistributions in binary form must reproduce the above copyright
+;;;;    notice, this list of conditions and the following disclaimer in the
+;;;;    documentation and/or other materials provided with the distribution.
+;;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+;;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+;;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+;;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+;;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+;;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+;;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+;;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+;;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#+xcvb (module ())
+
+(in-package :cl-user)
+
+(defpackage :rfc2388
+  (:use :common-lisp)
+  (:export
+   #:parse-header
+   #:header
+   #:header-name
+   #:header-value
+   #:header-parameters
+
+   #:content-type
+   #:find-header
+   #:find-parameter
+   #:find-content-disposition-header
+   #:get-file-name
+
+   #:parse-mime
+   #:mime-part
+   #:mime-part-contents
+   #:mime-part-headers
+   #:make-mime-part))
diff --git a/deps/rfc2388/rfc2388.asd b/deps/rfc2388/rfc2388.asd
new file mode 100644 (file)
index 0000000..920209a
--- /dev/null
@@ -0,0 +1,40 @@
+;;;; -*- mode: LISP; package: RFC2388 -*-
+;;;; Copyright (c) 2003 Janis Dzerins
+;;;;
+;;;; Redistribution and use in source and binary forms, with or without
+;;;; modification, are permitted provided that the following conditions
+;;;; are met:
+;;;; 1. Redistributions of source code must retain the above copyright
+;;;;    notice, this list of conditions and the following disclaimer.
+;;;; 2. Redistributions in binary form must reproduce the above copyright
+;;;;    notice, this list of conditions and the following disclaimer in the
+;;;;    documentation and/or other materials provided with the distribution.
+;;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+;;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+;;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+;;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+;;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+;;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+;;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+;;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+;;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (unless (find-package :rfc2388.system)
+    (defpackage :rfc2388.system
+      (:documentation "ASDF System package for rfc2388")
+      (:use :common-lisp :asdf))))
+
+(in-package :rfc2388.system)
+
+(defsystem :rfc2388
+  :author "Jānis Džeriņš <jonis@latnet.lv>"
+  :version "1.5"
+  :description "Implementation of RFC 2388"
+  :long-description "
+Contains an implementation of RFC 2388, which is used to process form data
+posted with HTTP POST method using enctype \"multipart/form-data\"."
+  :components ((:static-file "rfc2388.asd")
+               (:file "rfc2388" :depends-on ("packages"))
+               (:file "packages")))
diff --git a/deps/rfc2388/rfc2388.lisp b/deps/rfc2388/rfc2388.lisp
new file mode 100644 (file)
index 0000000..addaadc
--- /dev/null
@@ -0,0 +1,492 @@
+;;;; -*- mode: LISP; package: RFC2388 -*-
+;;;; Copyright (c) 2003 Janis Dzerins
+;;;; Modifications for TBNL Copyright (c) 2004 Michael Weber and Dr. Edmund Weitz
+;;;;
+;;;; Redistribution and use in source and binary forms, with or without
+;;;; modification, are permitted provided that the following conditions
+;;;; are met:
+;;;; 1. Redistributions of source code must retain the above copyright
+;;;;    notice, this list of conditions and the following disclaimer.
+;;;; 2. Redistributions in binary form must reproduce the above copyright
+;;;;    notice, this list of conditions and the following disclaimer in the
+;;;;    documentation and/or other materials provided with the distribution.
+;;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+;;;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+;;;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+;;;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+;;;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+;;;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+;;;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+;;;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+;;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+;;;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#+xcvb (module (:depends-on ("packages")))
+
+(in-package :rfc2388)
+
+
+\f
+;;; Utility functions
+
+
+(defun lwsp-char-p (char)
+  "Returns true if CHAR is a linear-whitespace-char (LWSP-char).  Either
+   space or tab, in short."
+  (or (char= char #\space)
+      (char= char #\tab)))
+
+
+;;; *** This actually belongs to RFC2046
+;;;
+(defun read-until-next-boundary (stream boundary &optional discard out-stream)
+  "Reads from STREAM up to the next boundary.  Returns two values: read
+   data (nil if DISCARD is true), and true if the boundary is not last
+   (i.e., there's more data)."
+  ;; Read until [CRLF]--boundary[--][transport-padding]CRLF
+  ;; States:     1 2  345        67  8                 9 10
+  ;;
+  ;; *** This will WARN like crazy on some bad input -- should only do each
+  ;; warning once.
+
+  (let ((length (length boundary)))
+    (unless (<= 1 length 70)
+      (warn "Boundary has invalid length -- must be between 1 and 70, but is: ~S" length))
+    (when (lwsp-char-p (schar boundary (1- length)))
+      (warn "Boundary has trailing whitespace: ~S" boundary)))
+
+  (flet ((run (result)
+           "This one writes everything up to a boundary to RESULT stream,
+            and returns false if the closing delimiter has been read, and
+            true otherwise."
+           (let ((state 1)
+                 (boundary-index 0)
+                 (boundary-length (length boundary))
+                 (closed nil)
+                 (queued-chars (make-string 4))
+                 (queue-index 0)
+                 char
+                 (leave-char nil))
+
+             (flet ((write-queued-chars ()
+                      (dotimes (i queue-index)
+                        (write-char (schar queued-chars i) result))
+                      (setf queue-index 0))
+
+                    (enqueue-char ()
+                      (setf (schar queued-chars queue-index) char)
+                      (incf queue-index)))
+
+               (loop
+
+                 (if leave-char
+                     (setq leave-char nil)
+                     (setq char (read-char stream nil nil)))
+
+                 (unless char
+                   (setq closed t)
+                   (return))
+
+                  #-(and)
+                  (format t "~&S:~D QI:~D BI:~2,'0D CH:~:[~;*~]~S~%"
+                          state queue-index boundary-index leave-char char)
+
+                 (case state
+                   (1 ;; optional starting CR
+                    (cond ((char= char #\return)
+                           (enqueue-char)
+                           (setq state 2))
+                          ((char= char #\-)
+                           (setq leave-char t
+                                 state 3))
+                          (t
+                           (write-char char result))))
+
+                   (2 ;; optional starting LF
+                    (cond ((char= char #\linefeed)
+                           (enqueue-char)
+                           (setq state 3))
+                          (t
+                           (write-queued-chars)
+                           (setq leave-char t
+                                 state 1))))
+
+                   (3 ;; first dash in dash-boundary
+                    (cond ((char= char #\-)
+                           (enqueue-char)
+                           (setq state 4))
+                          (t
+                           (write-queued-chars)
+                           (setq leave-char t
+                                 state 1))))
+
+                   (4 ;; second dash in dash-boundary
+                    (cond ((char= char #\-)
+                           (enqueue-char)
+                           (setq state 5))
+                          (t
+                           (write-queued-chars)
+                           (setq leave-char t
+                                 state 1))))
+
+                   (5 ;; boundary
+                    (cond ((char= char (schar boundary boundary-index))
+                           (incf boundary-index)
+                           (when (= boundary-index boundary-length)
+                             (setq state 6)))
+                          (t
+                           (write-queued-chars)
+                           (write-sequence boundary result :end boundary-index)
+                           (setq boundary-index 0
+                                 leave-char t
+                                 state 1))))
+
+                   (6 ;; first dash in close-delimiter
+                    (cond ((char= char #\-)
+                           (setq state 7))
+                          (t
+                           (setq leave-char t)
+                           (setq state 8))))
+
+                   (7 ;; second dash in close-delimiter
+                    (cond ((char= char #\-)
+                           (setq closed t
+                                 state 8))
+                          (t
+                           ;; this is a strange situation -- only two dashes, linear
+                           ;; whitespace or CR is allowed after boundary, but there was
+                           ;; a single dash...  One thing is clear -- this is not a
+                           ;; close-delimiter.  Hence this is garbage what we're looking
+                           ;; at!
+                           (warn "Garbage where expecting close-delimiter!")
+                           (setq leave-char t)
+                           (setq state 8))))
+
+                   (8 ;; transport-padding (LWSP* == [#\space #\tab]*)
+                    (cond ((lwsp-char-p char)
+                           ;; ignore these
+                           )
+                          (t
+                           (setq leave-char t)
+                           (setq state 9))))
+
+                   (9 ;; CR
+                    (cond ((char= char #\return)
+                           (setq state 10))
+                          (t
+                           (warn "Garbage where expecting CR!"))))
+
+                   (10 ;; LF
+                    (cond ((char= char #\linefeed)
+                           ;; the end
+                           (return))
+                          (t
+                           (warn "Garbage where expecting LF!")))))))
+             (not closed))))
+
+    (if discard
+        (let ((stream (make-broadcast-stream)))
+          (values nil (run stream)))
+        (let* ((stream (or out-stream (make-string-output-stream)))
+               (closed (run stream)))
+          (values (or out-stream (get-output-stream-string stream))
+                  closed)))))
+
+
+(defun make-tmp-file-name ()
+  (if (find-package :tbnl)
+      (funcall (find-symbol #.(string '#:make-tmp-file-name) :tbnl))
+      (error "WRITE-CONTENT-TO-FILE keyword argument to PARSE-MIME is supported in TBNL only at the moment.")))
+
+
+\f
+;;; Header parsing
+
+
+(defstruct (header (:type list)
+                   (:constructor make-header (name value parameters)))
+  name
+  value
+  parameters)
+
+
+(defun skip-linear-whitespace (string &key (start 0) end)
+  "Returns the position of first non-linear-whitespace character in STRING
+   bound by START and END."
+  (position-if-not #'lwsp-char-p string :start start :end end))
+
+
+(defgeneric parse-header (source &optional start-state)
+  (:documentation "Parses SOURCE and returs a single MIME header.
+
+Header is a list of the form (NAME VALUE PARAMETERS), PARAMETERS
+is a list of (NAME . VALUE)"))
+
+
+(defmethod parse-header ((source string) &optional (start-state :name))
+  (with-input-from-string (in source)
+    (parse-header in start-state)))
+
+
+;;; *** I don't like this parser -- it will have to be rewritten when I
+;;; make my state-machine parser-generator macro!
+;;;
+(defmethod parse-header ((stream stream) &optional (start-state :name))
+  "Returns a MIME part header, or NIL, if there is no header.  Header is
+   terminated by CRLF."
+  (let ((state (ecase start-state
+                 (:name 1)
+                 (:value 2)
+                 (:parameters 3)))
+        (result (make-string-output-stream))
+        char
+        (leave-char nil)
+        name
+        value
+        parameter-name
+        parameters)
+
+    (labels ((skip-lwsp (next-state)
+               (loop
+                 do (setq char (read-char stream nil nil))
+                 while (and char (lwsp-char-p char)))
+               (setq leave-char t
+                     state next-state))
+
+             (collect-parameter ()
+               (push (cons parameter-name
+                           (get-output-stream-string result))
+                     parameters)
+               (setq parameter-name nil)
+               (skip-lwsp 3))
+
+             (token-end-char-p (char)
+               (or (char= char #\;)
+                   (lwsp-char-p char))))
+
+      (loop
+
+        (if leave-char
+            (setq leave-char nil)
+            (setq char (read-char stream nil nil)))
+
+        ;; end of stream
+        (unless char
+          (return))
+
+        (when (char= #\return char)
+          (setq char (read-char stream nil nil))
+          (cond ((or (null char)
+                     (char= #\linefeed char))
+                 ;; CRLF ends the input
+                 (return))
+                (t
+                 (warn "LINEFEED without RETURN in header.")
+                 (write-char #\return result)
+                 (setq leave-char t))))
+
+        #-(and)
+        (format t "~&S:~,'0D CH:~:[~;*~]~S~%"
+                state leave-char char)
+
+        (ecase state
+          (1 ;; NAME
+           (cond ((char= char #\:)
+                  ;; end of name
+                  (setq name (get-output-stream-string result))
+                  (skip-lwsp 2))
+                 (t
+                  (write-char char result))))
+
+          (2 ;; VALUE
+           (cond ((token-end-char-p char)
+                  (setq value (get-output-stream-string result))
+                  (skip-lwsp 3))
+                 (t
+                  (write-char char result))))
+
+          (3 ;; PARAMETER name
+           (cond ((char= #\= char)
+                  (setq parameter-name (get-output-stream-string result)
+                        state 4))
+                 (t
+                  (write-char char result))))
+
+          (4 ;; PARAMETER value start
+           (cond ((char= #\" char)
+                  (setq state 5))
+                 (t
+                  (setq leave-char t
+                        state 7))))
+
+          (5 ;; Quoted PARAMETER value
+           (cond ((char= #\" char)
+                  (setq state 6))
+                 (t
+                  (write-char char result))))
+
+          (6 ;; End of quoted PARAMETER value
+           (cond ((token-end-char-p char)
+                  (collect-parameter))
+                 (t
+                  ;; no space or semicolon after quoted parameter value
+                  (setq leave-char t
+                        state 3))))
+
+          (7 ;; Unquoted PARAMETER value
+           (cond ((token-end-char-p char)
+                  (collect-parameter))
+                 (t
+                  (write-char char result))))))
+
+      (case state
+        (1
+         (setq name (get-output-stream-string result)))
+        (2
+         (setq value (get-output-stream-string result)))
+        ((3 4)
+         (let ((name (get-output-stream-string result)))
+           (unless (zerop (length name))
+             (warn "Parameter without value in header.")
+             (push (cons name nil) parameters))))
+        ((5 6 7)
+         (push (cons parameter-name (get-output-stream-string result)) parameters))))
+
+    (if (and (or (null name)
+                 (zerop (length name)))
+             (null value)
+             (null parameters))
+        nil
+        (make-header name value parameters))))
+
+
+\f
+;;; _The_ MIME parsing
+
+
+(defgeneric parse-mime (source boundary &key write-content-to-file)
+  (:documentation
+   "Parses MIME entities, returning them as a list.  Each element in the
+    list is of form: (body headers), where BODY is the contents of MIME
+    part, and HEADERS are all headers for that part.  BOUNDARY is a string
+    used to separate MIME entities."))
+
+
+(defstruct (content-type (:type list)
+                         (:constructor make-content-type (super sub)))
+  super
+  sub)
+
+
+(defun parse-content-type (string)
+  "Returns content-type which is parsed from STRING."
+  (let ((sep-offset (position #\/ string))
+        (type (array-element-type string)))
+    (if (numberp sep-offset)
+        (make-content-type (make-array sep-offset
+                                       :element-type type
+                                       :displaced-to string)
+                           (make-array (- (length string) (incf sep-offset))
+                                       :element-type type
+                                       :displaced-to string
+                                       :displaced-index-offset sep-offset))
+        (make-content-type string nil))))
+
+
+(defun unparse-content-type (ct)
+  "Returns content-type CT in string representation."
+  (let ((super (content-type-super ct))
+        (sub (content-type-sub ct)))
+    (cond ((and super sub)
+           (concatenate 'string super "/" sub))
+          (t (or super "")))))
+
+(defstruct (mime-part (:type list)
+                      (:constructor make-mime-part (contents headers)))
+  contents
+  headers)
+
+
+(defmethod parse-mime ((input string) separator &key (write-content-to-file t))
+  (with-input-from-string (stream input)
+    (parse-mime stream separator :write-content-to-file write-content-to-file)))
+
+
+(defmethod parse-mime ((input stream) boundary &key (write-content-to-file t))
+  ;; Find the first boundary.  Return immediately if it is also the last
+  ;; one.
+  (unless (nth-value 1 (read-until-next-boundary input boundary t))
+    (return-from parse-mime nil))
+
+  (let ((result ()))
+    (loop
+      (let ((headers (loop
+                      for header = (parse-header input)
+                      while header
+                      when (string-equal "CONTENT-TYPE" (header-name header))
+                      do (setf (header-value header) (parse-content-type (header-value header)))
+                      collect header)))
+        (let ((file-name (get-file-name headers)))
+          (cond ((and write-content-to-file
+                      file-name)
+                 (let ((temp-file (make-tmp-file-name)))
+                   (multiple-value-bind (text more)
+                       (with-open-file (out-file (ensure-directories-exist temp-file)
+                                                 :direction :output
+                                                 ;; external format for faithful I/O
+                                                 ;; see <http://cl-cookbook.sourceforge.net/io.html#faith>
+                                                 #+(or :sbcl :lispworks :allegro :openmcl)
+                                                 :external-format
+                                                 #+sbcl :latin-1
+                                                 #+:lispworks '(:latin-1 :eol-style :lf)
+                                                 #+:allegro (excl:crlf-base-ef :latin1)
+                                                 #+:openmcl '(:character-encoding :iso-8859-1
+                                                              :line-termination :unix))
+                         (read-until-next-boundary input boundary nil out-file))
+                     (declare (ignore text))
+                     (when (and (stringp file-name)
+                                (plusp (length file-name)))
+                       (push (make-mime-part temp-file headers) result))
+                     (when (not more)
+                       (return)))))
+                (t
+                 (multiple-value-bind (text more)
+                     (read-until-next-boundary input boundary)
+                   (push (make-mime-part text headers) result)
+                   (when (not more)
+                     (return))))))))
+    (nreverse result)))
+
+
+(defun find-header (label headers)
+  "Find header by label from set of headers."
+  (find label headers :key #'rfc2388:header-name :test #'string-equal))
+
+
+(defun find-parameter (name params)
+  "Find header parameter by name from set of parameters."
+  (assoc name params :test #'string-equal))
+
+
+(defun content-type (part &key as-string)
+  "Returns the Content-Type header of mime-part PART."
+  (let ((header (find-header "CONTENT-TYPE" (mime-part-headers part))))
+    (if header
+        (if as-string
+            (or (unparse-content-type (header-value header)) "")
+            (header-value header))
+        (when as-string ""))))
+
+
+(defun find-content-disposition-header (headers)
+  (find-if (lambda (header)
+             (and (string-equal "CONTENT-DISPOSITION"
+                                (rfc2388:header-name header))
+                  (string-equal "FORM-DATA"
+                                (rfc2388:header-value header))))
+           headers))
+
+
+(defun get-file-name (headers)
+  (cdr (find-parameter "FILENAME"
+                       (header-parameters (find-content-disposition-header headers)))))
diff --git a/deps/rfc2388/test.lisp b/deps/rfc2388/test.lisp
new file mode 100644 (file)
index 0000000..17dad9b
--- /dev/null
@@ -0,0 +1,85 @@
+(defpackage :rfc2388.test
+  (:use :common-lisp))
+
+(in-package :rfc2388.test)
+
+(defconstant +crlf+ (format nil "~C~C" #\return #\linefeed))
+
+
+(defun generate-test-strings (parts)
+  (flet ((prepend (left list)
+           (mapcar (lambda (right)
+                     (format nil "~A~A" left right))
+                   list))
+         (postpend (right list)
+           (mapcar (lambda (left)
+                     (format nil "~A~A" left right))
+                   list)))
+    (cond ((null parts)
+           nil)
+          ((null (cdr parts))
+           (list (format nil "~A" (first parts))))
+          (t
+           (list* (format nil "~A" (first parts))
+                  (nconc (prepend (first parts) (rest parts))
+                         (postpend (first parts) (rest parts))
+                         (generate-test-strings (cdr parts))))))))
+
+
+(defparameter *strings* (generate-test-strings `("X" " " "-" "--" "---" ,+crlf+ #\return #\linefeed)))
+(defparameter *boundaries* '("x" "-x" "--x"))
+
+
+(defun sanitize-test-string (string)
+  (with-output-to-string (out)
+    (loop for char across string
+       do (case char
+            (#\return   (write-string "[CR]" out))
+            (#\linefeed (write-string "[LF]" out))
+            (t (write-char char out))))))
+
+
+(defun test-string (string &optional (boundary "boundary"))
+  (with-input-from-string (stream string)
+    (handler-bind ((simple-warning (lambda (condition)
+                                     (declare (ignore condition))
+                                     (format t "~&Testing: ~S (boundary ~S)~%"
+                                             (sanitize-test-string string)
+                                             boundary))))
+      (rfc2388::read-until-next-boundary stream boundary))))
+
+
+(defun test ()
+  (declare (optimize debug))
+  (flet ((last-char (string)
+           (declare (type simple-string string))
+           (schar string (1- (length string))))
+
+         (test (test expected boundary)
+           (multiple-value-bind (result more-p)
+               (test-string test boundary)
+             (unless (or (string= result expected)
+                         more-p)
+               (format t "~%String:   ~S (Boundary: ~S)~%Expected: ~S~%Got:      ~S~%More: ~S~%"
+                       (sanitize-test-string test)
+                       boundary
+                       (sanitize-test-string expected)
+                       (sanitize-test-string result)
+                       more-p)
+          (finish-output t)))))
+
+    (dolist (string *strings*)
+      (dolist (boundary *boundaries*)
+        (dolist (trailing-separator '("--" ""))
+          (test (concatenate 'string string +crlf+ "--" boundary trailing-separator +crlf+)
+                string
+                boundary)
+          (unless (char= #\- (last-char string))
+            (test (concatenate 'string string "--" boundary trailing-separator +crlf+)
+                  (let ((end (- (length string) 2)))
+                    (if (and (<= 0 end)
+                             (string= string +crlf+ :start1 end))
+                        (subseq string 0 end)
+                        string))
+                  boundary))))))
+  t)
diff --git a/deps/trivial-backtrace/.gitignore b/deps/trivial-backtrace/.gitignore
new file mode 100644 (file)
index 0000000..391b10e
--- /dev/null
@@ -0,0 +1,15 @@
+# really this is private to my build process
+make/
+common-lisp.net
+.vcs
+GNUmakefile
+init-lisp.lisp
+website/changelog.xml
+
+
+trivial-backtrace.tar.gz
+website/output/
+test-results/
+lift-local.config
+*.dribble
+*.fasl
diff --git a/deps/trivial-backtrace/COPYING b/deps/trivial-backtrace/COPYING
new file mode 100644 (file)
index 0000000..3798a66
--- /dev/null
@@ -0,0 +1,25 @@
+Copyright (c) 2008-2008 Gary Warren King (gwking@metabang.com) 
+
+Permission is hereby granted, free of charge, to any person obtaining a 
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense, 
+and/or sell copies of the Software, and to permit persons to whom the 
+Software is furnished to do so, subject to the following conditions: 
+
+The above copyright notice and this permission notice shall be included in 
+all copies or substantial portions of the Software. 
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL 
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+DEALINGS IN THE SOFTWARE. 
+
+
+
+Copyright (c) 2005-2007 Dr. Edi Weitz 
+
+BSD style license: http://www.opensource.org/licenses/bsd-license.php
diff --git a/deps/trivial-backtrace/dev/backtrace.lisp b/deps/trivial-backtrace/dev/backtrace.lisp
new file mode 100644 (file)
index 0000000..aa3951e
--- /dev/null
@@ -0,0 +1,127 @@
+(in-package #:trivial-backtrace)
+
+(defun print-condition (condition stream)
+  "Print `condition` to `stream` using the pretty printer."
+  (format
+   stream
+   "~@<An unhandled error condition has been signalled:~3I ~a~I~:@>~%~%"
+   condition))
+  
+(defun print-backtrace (error &key (output *debug-io*)
+                       (if-exists :append)
+                       (verbose nil))
+  "Send a backtrace for the error `error` to `output`. 
+
+The keywords arguments are:
+
+ * :output - where to send the output. This can be:
+
+     * a string (which is assumed to designate a pathname)
+     * an open stream
+     * nil to indicate that the backtrace information should be 
+       returned as a string
+
+ * if-exists - what to do if output designates a pathname and 
+   the pathname already exists. Defaults to :append.
+
+ * verbose - if true, then a message about the backtrace is sent
+   to \\*terminal-io\\*. Defaults to `nil`.
+
+If the `output` is nil, the returns the backtrace output as a
+string. Otherwise, returns nil.
+"
+  (when verbose
+    (print-condition error *terminal-io*))
+  (multiple-value-bind (stream close?)
+      (typecase output
+       (null (values (make-string-output-stream) nil))
+       (string (values (open output :if-exists if-exists
+                             :if-does-not-exist :create
+                             :direction :output) t))
+       (stream (values output nil)))
+    (unwind-protect
+        (progn
+          (format stream "~&Date/time: ~a" (date-time-string))
+          (print-condition error stream)
+          (terpri stream)
+          (print-backtrace-to-stream stream)
+          (terpri stream)
+          (when (typep stream 'string-stream)
+            (get-output-stream-string stream)))
+        ;; cleanup
+        (when close?
+          (close stream)))))
+
+#+(or mcl ccl)
+(defun print-backtrace-to-stream (stream)
+  (let ((*debug-io* stream))
+    (ccl:print-call-history :detailed-p nil)))
+
+#+allegro
+(defun print-backtrace-to-stream (stream)
+  (with-standard-io-syntax
+    (let ((*print-readably* nil)
+         (*print-miser-width* 40)
+         (*print-pretty* t)
+         (tpl:*zoom-print-circle* t)
+         (tpl:*zoom-print-level* nil)
+         (tpl:*zoom-print-length* nil))
+      (cl:ignore-errors
+       (let ((*terminal-io* stream)
+            (*standard-output* stream))
+        (tpl:do-command "zoom"
+          :from-read-eval-print-loop nil
+          :count t
+          :all t))))))
+
+#+lispworks
+(defun print-backtrace-to-stream (stream)
+  (let ((dbg::*debugger-stack*
+        (dbg::grab-stack nil :how-many most-positive-fixnum))
+       (*debug-io* stream)
+       (dbg:*debug-print-level* nil)
+       (dbg:*debug-print-length* nil))
+    (dbg:bug-backtrace nil)))
+
+#+sbcl
+;; determine how we're going to access the backtrace in the next
+;; function
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (when (find-symbol "*DEBUG-PRINT-VARIABLE-ALIST*" :sb-debug)
+    (pushnew :sbcl-debug-print-variable-alist *features*)))
+
+#+sbcl
+(defun print-backtrace-to-stream (stream)
+  (let (#+:sbcl-debug-print-variable-alist
+       (sb-debug:*debug-print-variable-alist*
+        (list* '(*print-level* . nil)
+               '(*print-length* . nil)
+               sb-debug:*debug-print-variable-alist*))
+       #-:sbcl-debug-print-variable-alist
+       (sb-debug:*debug-print-level* nil)
+       #-:sbcl-debug-print-variable-alist
+       (sb-debug:*debug-print-length* nil))
+    (sb-debug:backtrace most-positive-fixnum stream)))
+
+#+clisp
+(defun print-backtrace-to-stream (stream)
+  (system::print-backtrace :out stream))
+
+#+(or cmucl scl)
+(defun print-backtrace-to-stream (stream)
+  (let ((debug:*debug-print-level* nil)
+       (debug:*debug-print-length* nil))
+    (debug:backtrace most-positive-fixnum stream)))
+
+
+;; must be after the defun above or the docstring may be wiped out
+(setf (documentation 'print-backtrace-to-stream 'function)
+  "Send a backtrace of the current error to stream. 
+
+Stream is assumed to be an open writable file stream or a
+string-output-stream. Note that `print-backtrace-to-stream`
+will print a backtrace for whatever the Lisp deems to be the 
+*current* error.
+")
+
+
diff --git a/deps/trivial-backtrace/dev/fallback.lisp b/deps/trivial-backtrace/dev/fallback.lisp
new file mode 100644 (file)
index 0000000..40a5219
--- /dev/null
@@ -0,0 +1,10 @@
+(in-package #:trivial-backtrace)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (unless (fboundp 'map-backtrace)
+    (defun map-backtrace (func)
+      (declare (ignore func))))
+
+  (unless (fboundp 'print-backtrace-to-stream)
+    (defun print-backtrace-to-stream (stream)
+      (format stream "~&backtrace output unavailable.~%"))))
diff --git a/deps/trivial-backtrace/dev/map-backtrace.lisp b/deps/trivial-backtrace/dev/map-backtrace.lisp
new file mode 100644 (file)
index 0000000..43eddda
--- /dev/null
@@ -0,0 +1,105 @@
+(in-package #:trivial-backtrace)
+
+(defstruct frame
+  func
+  source-filename
+  source-pos
+  vars)
+
+(defstruct var
+  name
+  value)
+
+(defstruct pos-form-number
+  number)
+
+(defmethod print-object ((pos-form-number pos-form-number) stream)
+  (cond 
+    (*print-readably* (call-next-method))
+    (t
+     (format stream "f~A" (pos-form-number-number pos-form-number)))))
+
+
+(defvar *trivial-backtrace-frame-print-specials*
+  '((*print-length* . 100)
+    (*print-level* . 20)
+    (*print-lines* . 5)
+    (*print-pretty* . t)
+    (*print-readably* . nil)))
+
+(defun print-frame (frame stream)
+  (format stream "~A:~@[~A:~] ~A: ~%" 
+         (or (ignore-errors (translate-logical-pathname (frame-source-filename frame))) (frame-source-filename frame) "<unknown>")
+         (frame-source-pos frame)
+         (frame-func frame))
+  (loop for var in (frame-vars frame)
+       do 
+       (format stream " ~A = ~A~%" (var-name var) 
+               (or (ignore-errors      
+                       (progv 
+                           (mapcar #'car *trivial-backtrace-frame-print-specials*)
+                           (mapcar #'cdr *trivial-backtrace-frame-print-specials*)
+                         (prin1-to-string 
+                          (var-value var))))
+                   "<error>"))))
+
+(defun map-backtrace (function)
+  (impl-map-backtrace function))
+
+(defun print-map-backtrace (&optional (stream *debug-io*) &rest args)
+  (apply 'map-backtrace 
+        (lambda (frame)
+          (print-frame frame stream)) args))
+
+(defun backtrace-string (&rest args)
+  (with-output-to-string (stream)
+    (apply 'print-map-backtrace stream args)))
+
+
+#+ccl
+(defun impl-map-backtrace (func)
+  (ccl::map-call-frames (lambda (ptr) 
+                         (multiple-value-bind (lfun pc)
+                             (ccl::cfp-lfun ptr)
+                           (let ((source-note (ccl:function-source-note lfun)))
+                             (funcall func 
+                                      (make-frame :func (ccl::lfun-name lfun)
+                                                  :source-filename (ccl:source-note-filename source-note)
+                                                  :source-pos (let ((form-number (ccl:source-note-start-pos source-note)))
+                                                                (when form-number (make-pos-form-number :number form-number)))
+                                                  :vars (loop for (name . value) in (ccl::arguments-and-locals nil ptr lfun pc)
+                                                              collect (make-var :name name :value value)))))))))
+
+#+sbcl
+(defun impl-map-backtrace (func)
+  (loop for f = (or sb-debug:*stack-top-hint* (sb-di:top-frame)) then (sb-di:frame-down f)
+       while f
+       do (funcall func 
+                   (make-frame :func 
+                               (ignore-errors
+                                 (sb-di:debug-fun-name                             
+                                  (sb-di:frame-debug-fun f)))
+                               :source-filename 
+                               (ignore-errors
+                                 (sb-di:debug-source-namestring (sb-di:code-location-debug-source (sb-di:frame-code-location f))))
+                               :source-pos
+                               (ignore-errors ;;; XXX does not work
+                                 (let ((cloc (sb-di:frame-code-location f)))
+                                   (unless (sb-di:code-location-unknown-p cloc)
+                                     (format nil "tlf~Dfn~D"
+                                             (sb-di:code-location-toplevel-form-offset cloc)
+                                             (sb-di:code-location-form-number cloc)))))
+                               :vars
+                               (remove-if 'not 
+                                          (map 'list (lambda(v)
+                                                       (ignore-errors
+                                                         (when (eq :valid
+                                                            (sb-di:debug-var-validity v (sb-di:frame-code-location f)))
+                                                           (make-var :name (sb-di:debug-var-symbol v)
+                                                                     :value (sb-di:debug-var-value v f)))))
+                                               (ignore-errors (sb-di::debug-fun-debug-vars (sb-di:frame-debug-fun f)))))))))
+
+#-(or ccl sbcl)
+(defun impl-map-backtrace (func)
+  (declare (ignore func))
+  (warn "unable to map backtrace for ~a" (lisp-implementation-type)))
\ No newline at end of file
diff --git a/deps/trivial-backtrace/dev/mucking.lisp b/deps/trivial-backtrace/dev/mucking.lisp
new file mode 100644 (file)
index 0000000..2be26a5
--- /dev/null
@@ -0,0 +1,75 @@
+(in-package #:metabang.gsn)
+
+#|
+Need to account for different kinds of links
+  in gsn-nodes-from-json, need to return pairs of node and attributes
+
+hash-table for nodes to prevent duplicates
+queue or stack for nodes to expand
+hash-table for links (triples of A link B?) to handle duplicates
+|#
+
+(defgeneric expand-node (context node)
+  )
+
+(defgeneric find-neighbors (context node)
+  )
+
+(defgeneric expand-node-p (context node)
+  )
+
+(defgeneric add-node (context node)
+  )
+
+(defgeneric add-link (context node neighbor direction)
+  )
+
+(defgeneric update-node-data (context node data)
+  )
+
+(defclass abstract-context ()
+  ())
+
+(defclass gsn-context (abstract-context)
+  ())
+
+(defparameter +gsn-root+ "http://socialgraph.apis.google.com/")
+
+(defmethod expand-node ((context abstract-context) node)
+  (bind (((to from) (find-neighbors context node)))
+    (dolist (neighbor to)
+      (add-node context neighbor)
+      (add-link context node neighbor :to))
+    (dolist (neighbor from)
+      (add-node context neighbor)
+      (add-link context node neighbor :from))))
+
+
+
+(defmethod find-neighbors ((context gsn-context) node)
+  (bind (((result headers stream)
+         (http-get 
+          (format nil "~alookup?edo=1&edi=1&pretty=1&q=~a" 
+                  +gsn-root+ node)))
+        json)
+    (unwind-protect 
+        (setf json (json:decode-json stream))
+      (close strea))
+    (update-node-data context node json)                     
+    (list (gsn-nodes-from-json json :to)
+         (gsn-nodes-from-json json :from))))
+  
+(gsn-nodes-from-json x :from)  
+
+(defun gsn-test (who)
+  (destructuring-bind (result headers stream)
+      (http-get 
+       (format nil "http://socialgraph.apis.google.com/lookup?edo=1&edi=1&pretty=1&q=~a" who))
+    (declare (ignore result headers))
+    (json:decode-json stream)))
+
+(assoc :nodes_referenced 
+       (assoc :nodes (gsn-test "TWITTER.COM/GWKING") :key #'first))
+
+
+(setf x (gsn-test "TWITTER.COM/GWKING")) 
diff --git a/deps/trivial-backtrace/dev/packages.lisp b/deps/trivial-backtrace/dev/packages.lisp
new file mode 100644 (file)
index 0000000..2da49d3
--- /dev/null
@@ -0,0 +1,13 @@
+(in-package #:common-lisp-user)
+
+(defpackage #:trivial-backtrace
+  (:use #:common-lisp)
+  (:export #:print-backtrace
+          #:print-backtrace-to-stream
+          #:print-condition
+          #:*date-time-format*
+
+
+          #:backtrace-string
+          #:map-backtrace))
+
diff --git a/deps/trivial-backtrace/dev/utilities.lisp b/deps/trivial-backtrace/dev/utilities.lisp
new file mode 100644 (file)
index 0000000..b0a2498
--- /dev/null
@@ -0,0 +1,104 @@
+(in-package #:trivial-backtrace)
+
+(defparameter *date-time-format* "%Y-%m-%d-%H:%M"
+  "The default format to use when printing dates and times.
+
+* %% - A '%' character
+* %d - Day of the month as a decimal number [01-31]
+* %e - Same as %d but does not print the leading 0 for days 1 through 9 
+     [unlike strftime[], does not print a leading space]
+* %H - Hour based on a 24-hour clock as a decimal number [00-23]
+*%I - Hour based on a 12-hour clock as a decimal number [01-12]
+* %m - Month as a decimal number [01-12]
+* %M - Minute as a decimal number [00-59]
+* %S - Second as a decimal number [00-59]
+* %w - Weekday as a decimal number [0-6], where Sunday is 0
+* %y - Year without century [00-99]
+* %Y - Year with century [such as 1990]
+
+This code is borrowed from the `format-date` function in 
+[metatilities-base][].")
+
+;; modified from metatilities-base
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defmacro generate-time-part-function (part-name position)
+    (let ((function-name 
+          (intern 
+           (concatenate 'string
+                        (symbol-name 'time) "-" (symbol-name part-name))
+           :trivial-backtrace)))
+      `(eval-when (:compile-toplevel :load-toplevel :execute)
+         (defun ,function-name
+                (&optional (universal-time (get-universal-time))
+                           (time-zone nil))
+           ,(format nil "Returns the ~(~A~) part of the given time." part-name)
+           (nth-value ,position 
+                     (apply #'decode-universal-time
+                            universal-time time-zone))))))
+
+  (generate-time-part-function second 0)
+  (generate-time-part-function minute 1)
+  (generate-time-part-function hour 2)
+  (generate-time-part-function date 3)
+  (generate-time-part-function month 4)
+  (generate-time-part-function year 5)
+  (generate-time-part-function day-of-week 6)
+  (generate-time-part-function daylight-savings-time-p 7))
+
+(defun date-time-string (&key (date/time (get-universal-time))
+                        (format *date-time-format*))
+  (format-date format date/time nil))
+
+(defun format-date (format date &optional stream time-zone)
+  (declare (ignore time-zone))
+  (let ((format-length (length format)))
+    (format 
+     stream "~{~A~}"
+     (loop for index = 0 then (1+ index) 
+       while (< index format-length) collect 
+       (let ((char (aref format index)))
+         (cond 
+           ((char= #\% char)
+            (setf char (aref format (incf index)))
+            (cond 
+              ;; %% - A '%' character
+              ((char= char #\%) #\%)
+                            
+              ;; %d - Day of the month as a decimal number [01-31]
+              ((char= char #\d) (format nil "~2,'0D" (time-date date)))
+                            
+              ;; %e - Same as %d but does not print the leading 0 for 
+              ;; days 1 through 9. Unlike strftime, does not print a 
+              ;; leading space
+              ((char= char #\e) (format nil "~D" (time-date date)))
+                            
+              ;; %H - Hour based on a 24-hour clock as a decimal number [00-23]
+              ((char= char #\H) (format nil "~2,'0D" (time-hour date)))
+                            
+              ;; %I - Hour based on a 12-hour clock as a decimal number [01-12]
+              ((char= char #\I) (format nil "~2,'0D" 
+                                        (1+ (mod (time-hour date) 12))))
+                            
+              ;; %m - Month as a decimal number [01-12]
+              ((char= char #\m) (format nil "~2,'0D" (time-month date)))
+                            
+              ;; %M - Minute as a decimal number [00-59]
+              ((char= char #\M) (format nil "~2,'0D" (time-minute date)))
+                            
+              ;; %S - Second as a decimal number [00-59]
+              ((char= char #\S) (format nil "~2,'0D" (time-second date)))
+                            
+              ;; %w - Weekday as a decimal number [0-6], where Sunday is 0
+              ((char= char #\w) (format nil "~D" (time-day-of-week date)))
+                            
+              ;; %y - Year without century [00-99]
+              ((char= char #\y) 
+               (let ((year-string (format nil "~,2A" (time-year date))))
+                 (subseq year-string (- (length year-string) 2))))
+                            
+              ;; %Y - Year with century [such as 1990]
+              ((char= char #\Y) (format nil "~D" (time-year date)))
+                            
+              (t
+               (error "Ouch - unknown formatter '%~c" char))))
+           (t char)))))))
diff --git a/deps/trivial-backtrace/lift-standard.config b/deps/trivial-backtrace/lift-standard.config
new file mode 100644 (file)
index 0000000..0f22312
--- /dev/null
@@ -0,0 +1,35 @@
+;;; configuration for LIFT tests
+
+;; settings
+(:if-dribble-exists :supersede)
+(:dribble "lift.dribble")
+(:print-length 10)
+(:print-level 5)
+(:print-test-case-names t)
+
+;; suites to run
+(trivial-backtrace-test)
+
+;; report properties
+(:report-property :title "Trivial-Backtrace | Test results")
+(:report-property :relative-to trivial-backtrace-test)
+
+(:report-property :style-sheet "test-style.css")
+(:report-property :if-exists :supersede)
+(:report-property :format :html)
+(:report-property :full-pathname "test-results/test-report.html")
+(:report-property :unique-name t)
+(:build-report)
+
+(:report-property :unique-name t)
+(:report-property :format :describe)
+(:report-property :full-pathname "test-results/test-report.txt")
+(:build-report)
+
+(:report-property :format :save)
+(:report-property :full-pathname "test-results/test-report.sav")
+(:build-report)
+
+(:report-property :format :describe)
+(:report-property :full-pathname *standard-output*)
+(:build-report)
diff --git a/deps/trivial-backtrace/test/packages.lisp b/deps/trivial-backtrace/test/packages.lisp
new file mode 100644 (file)
index 0000000..7dc3eae
--- /dev/null
@@ -0,0 +1,5 @@
+(in-package #:common-lisp-user)
+
+(defpackage #:trivial-backtrace-test
+  (:use #:common-lisp #:lift #:trivial-backtrace))
+
diff --git a/deps/trivial-backtrace/test/test-setup.lisp b/deps/trivial-backtrace/test/test-setup.lisp
new file mode 100644 (file)
index 0000000..a46b3a1
--- /dev/null
@@ -0,0 +1,4 @@
+(in-package #:trivial-backtrace-test)
+
+(deftestsuite trivial-backtrace-test ()
+  ())
diff --git a/deps/trivial-backtrace/test/tests.lisp b/deps/trivial-backtrace/test/tests.lisp
new file mode 100644 (file)
index 0000000..9b32090
--- /dev/null
@@ -0,0 +1,17 @@
+(in-package #:trivial-backtrace-test)
+
+(deftestsuite generates-backtrace (trivial-backtrace-test)
+  ())
+
+(addtest (generates-backtrace)
+  test-1
+  (let ((output nil))
+    (handler-case 
+       (let ((x 1))
+         (let ((y (- x (expt 1024 0))))
+           (declare (optimize (safety 3)))
+           (/ 2 y)))
+      (error (c)
+       (setf output (print-backtrace c :output nil))))
+    (ensure (stringp output))
+    (ensure (plusp (length output)))))
diff --git a/deps/trivial-backtrace/trivial-backtrace-test.asd b/deps/trivial-backtrace/trivial-backtrace-test.asd
new file mode 100644 (file)
index 0000000..cb08843
--- /dev/null
@@ -0,0 +1,22 @@
+(defpackage #:trivial-backtrace-test-system (:use #:asdf #:cl))
+(in-package #:trivial-backtrace-test-system)
+
+(defsystem trivial-backtrace-test
+  :author "Gary Warren King <gwking@metabang.com>"
+  :maintainer "Gary Warren King <gwking@metabang.com>"
+  :licence "MIT Style License; see file COPYING for details"
+  :components ((:module 
+               "setup"
+               :pathname "test/"
+               :components ((:file "packages")
+                            (:file "test-setup"
+                                   :depends-on ("packages"))))
+              (:module 
+               "test"
+               :pathname "test/"
+               :depends-on ("setup")
+               :components ((:file "tests"))))  
+  :depends-on (:lift :trivial-backtrace))
+
+
+
diff --git a/deps/trivial-backtrace/trivial-backtrace.asd b/deps/trivial-backtrace/trivial-backtrace.asd
new file mode 100644 (file)
index 0000000..843b6cc
--- /dev/null
@@ -0,0 +1,35 @@
+(in-package #:common-lisp-user)
+
+(defpackage #:trivial-backtrace-system (:use #:asdf #:cl))
+(in-package #:trivial-backtrace-system)
+
+(defsystem trivial-backtrace
+  :version "1.1.0"
+  :author "Gary Warren King <gwking@metabang.com> and contributors"
+  :maintainer "Gary Warren King <gwking@metabang.com> and contributors"
+  :licence "MIT Style license "
+  :description "trivial-backtrace"
+  :depends-on ()
+  :components
+  ((:static-file "COPYING")
+   (:module 
+    "setup"
+    :pathname "dev/"
+    :components ((:file "packages")))
+   (:module 
+    "dev"
+    :depends-on ("setup")
+    :components ((:file "utilities")
+                (:file "backtrace")
+                (:file "map-backtrace")
+                (:file "fallback" :depends-on ("backtrace" "map-backtrace")))))
+  :in-order-to ((test-op (load-op trivial-backtrace-test)))
+  :perform (test-op :after (op c)
+                   (funcall
+                    (intern (symbol-name '#:run-tests) :lift)
+                    :config :generic)))
+
+(defmethod operation-done-p 
+           ((o test-op)
+            (c (eql (find-system 'trivial-backtrace))))
+  (values nil))
diff --git a/deps/trivial-backtrace/website/source/index.md b/deps/trivial-backtrace/website/source/index.md
new file mode 100644 (file)
index 0000000..93a5df3
--- /dev/null
@@ -0,0 +1,88 @@
+{include resources/header.md}
+
+<div class="contents">
+<div class="system-links">
+
+  * [Mailing Lists][mailing-list]
+  * [Getting it][downloads]
+  * [Documentation][]
+  * [News][]
+  * [Test results][tr]
+  * [Changelog][]
+
+</div>
+<div class="system-description">
+
+### What it is
+
+On of the many things that didn't quite get into the Common
+Lisp standard was how to get a Lisp to output its call stack
+when something has gone wrong. As such, each Lisp has
+developed its own notion of what to display, how to display
+it, and what sort of arguments can be used to customize it.
+`trivial-backtrace` is a simple solution to generating a
+backtrace portably. As of {today}, it supports Allegro Common
+Lisp, LispWorks, ECL, MCL, SCL, SBCL and CMUCL. Its
+interface consists of three functions and one variable:
+
+ * print-backtrace
+ * print-backtrace-to-stream
+ * print-condition
+ * \*date-time-format\*
+
+You can probably already guess what they do, but they are
+described in more detail below.
+
+{anchor mailing-lists}
+
+### Mailing Lists
+
+  * [trivial-backtrace-devel][devel-list]: A list for
+    announcements, questions, patches, bug reports, and so
+    on; It's for anything and everything
+
+### API
+
+{set-property docs-package trivial-backtrace}
+{docs print-backtrace}
+{docs print-backtrace-to-stream}
+{docs print-condition}
+{docs *date-time-format*}
+
+{anchor downloads}
+
+### Where is it
+
+A [git][] repository is available using
+
+    git clone http://common-lisp.net/project/trivial-backtrace/trivial-backtrace.git
+    
+The [darcs][] repository is still around but is **not** being updated.
+The command to get it is below:
+
+    ;;; WARNING: out of date
+    darcs get http://common-lisp.net/project/trivial-backtrace/
+
+trivial-backtrace is also [ASDF installable][asdf-install].
+Its CLiki home is right [where][cliki-home] you'd expect.
+
+There's also a handy [gzipped tar file][tarball].
+
+{anchor news}
+
+### What is happening
+
+<dl>
+  <dt>14 May 2009</dt>
+  <dd>Moved to [git][]; John Fremlin adds map-backtrace
+      </dd>
+
+<dt>1 June 2008</dt>
+<dd>Release version 1.0
+    </dd>
+    </dl>
+</div>
+</div>
+
+{include resources/footer.md}
+
diff --git a/deps/trivial-backtrace/website/source/resources/footer.md b/deps/trivial-backtrace/website/source/resources/footer.md
new file mode 100644 (file)
index 0000000..c5bf3c4
--- /dev/null
@@ -0,0 +1,15 @@
+<div id="footer" class="footer">
+<div id="buttons">
+<a class="nav" href="http://validator.w3.org/check/referer" title="xhtml1.1"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/xhtml.gif" width="80" height="15" title="valid xhtml button" alt="valid xhtml" /></a>
+<a class="nav" href="http://common-lisp.net/project/cl-markdown/" title="Mark with CL-Markdown"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/cl-markdown.png" width="80" height="15" title="Made with CL-Markdown" alt="CL-Markdown" /></a>
+<a class="nav" href="http://www.catb.org/hacker-emblem/" title="hacker"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/hacker.png" width="80" height="15" title="hacker emblem" alt="hacker button" /></a>
+<a class="nav" href="http://www.lisp.org/" title="Association of Lisp Users"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/lambda-lisp.png" width="80" height="15" title="ALU emblem" alt="ALU button" /></a>
+<a class="nav" href="http://common-lisp.net/" title="Common-Lisp.net"><img src="http://common-lisp.net/project/cl-containers/shared/buttons/lisp-lizard.png" width="80" height="15" title="Common-Lisp.net" alt="Common-Lisp.net button" /></a>
+</div>
+
+### Copyright (c) 2009 - 2011 Gary Warren King (gwking@metabang.com) 
+
+trivial-backtrace has an [MIT style][mit-license] license
+
+<div id="timestamp">Last updated {today} at {now}</div>
+</div>
diff --git a/deps/trivial-backtrace/website/source/resources/header.md b/deps/trivial-backtrace/website/source/resources/header.md
new file mode 100644 (file)
index 0000000..2738c47
--- /dev/null
@@ -0,0 +1,19 @@
+{include shared-links.md}
+
+{set-property html yes}
+{set-property style-sheet "styles.css"}
+{set-property author "Gary Warren King"}
+{set-property title "trivial-backtrace | watch where you've been"}
+
+ [devel-list]: http://common-lisp.net/cgi-bin/mailman/listinfo/trivial-backtrace-devel
+ [cliki-home]: http://www.cliki.net//trivial-backtrace
+ [tarball]: http://common-lisp.net/project/trivial-backtrace/trivial-backtrace.tar.gz
+  
+<div id="header">
+       <span class="logo"><a href="http://www.metabang.com/" title="metabang.com"><img src="http://common-lisp.net/project/cl-containers/shared/metabang-2.png" title="metabang.com" width="100" alt="Metabang Logo" /></a></span>
+
+## trivial-backtrace
+
+#### watch where you've been
+
+</div>
diff --git a/deps/trivial-backtrace/website/source/resources/navigation.md b/deps/trivial-backtrace/website/source/resources/navigation.md
new file mode 100644 (file)
index 0000000..a734edf
--- /dev/null
@@ -0,0 +1,2 @@
+<div id="navigation">
+</div>
diff --git a/deps/trivial-backtrace/website/website.tmproj b/deps/trivial-backtrace/website/website.tmproj
new file mode 100644 (file)
index 0000000..01b745b
--- /dev/null
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>currentDocument</key>
+       <string>source/resources/header.md</string>
+       <key>documents</key>
+       <array>
+               <dict>
+                       <key>expanded</key>
+                       <true/>
+                       <key>name</key>
+                       <string>source</string>
+                       <key>regexFolderFilter</key>
+                       <string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
+                       <key>sourceDirectory</key>
+                       <string>source</string>
+               </dict>
+       </array>
+       <key>fileHierarchyDrawerWidth</key>
+       <integer>190</integer>
+       <key>metaData</key>
+       <dict>
+               <key>source/index.md</key>
+               <dict>
+                       <key>caret</key>
+                       <dict>
+                               <key>column</key>
+                               <integer>0</integer>
+                               <key>line</key>
+                               <integer>0</integer>
+                       </dict>
+                       <key>firstVisibleColumn</key>
+                       <integer>0</integer>
+                       <key>firstVisibleLine</key>
+                       <integer>0</integer>
+               </dict>
+               <key>source/resources/footer.md</key>
+               <dict>
+                       <key>caret</key>
+                       <dict>
+                               <key>column</key>
+                               <integer>29</integer>
+                               <key>line</key>
+                               <integer>9</integer>
+                       </dict>
+                       <key>firstVisibleColumn</key>
+                       <integer>0</integer>
+                       <key>firstVisibleLine</key>
+                       <integer>0</integer>
+               </dict>
+               <key>source/resources/header.md</key>
+               <dict>
+                       <key>caret</key>
+                       <dict>
+                               <key>column</key>
+                               <integer>27</integer>
+                               <key>line</key>
+                               <integer>3</integer>
+                       </dict>
+                       <key>firstVisibleColumn</key>
+                       <integer>0</integer>
+                       <key>firstVisibleLine</key>
+                       <integer>0</integer>
+               </dict>
+               <key>source/resources/navigation.md</key>
+               <dict>
+                       <key>caret</key>
+                       <dict>
+                               <key>column</key>
+                               <integer>0</integer>
+                               <key>line</key>
+                               <integer>1</integer>
+                       </dict>
+                       <key>firstVisibleColumn</key>
+                       <integer>0</integer>
+                       <key>firstVisibleLine</key>
+                       <integer>0</integer>
+               </dict>
+       </dict>
+       <key>openDocuments</key>
+       <array>
+               <string>source/resources/header.md</string>
+               <string>source/index.md</string>
+               <string>source/resources/navigation.md</string>
+               <string>source/resources/footer.md</string>
+       </array>
+       <key>showFileHierarchyDrawer</key>
+       <true/>
+       <key>windowFrame</key>
+       <string>{{615, 0}, {578, 778}}</string>
+</dict>
+</plist>
diff --git a/deps/trivial-gray-streams/COPYING b/deps/trivial-gray-streams/COPYING
new file mode 100644 (file)
index 0000000..fcb4fe7
--- /dev/null
@@ -0,0 +1,22 @@
+    Copyright (c) 2005 David Lichteblau
+    Copyright (c) 2013 Anton Vodonosov <avodonosov@yandex.ru>
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
diff --git a/deps/trivial-gray-streams/Makefile b/deps/trivial-gray-streams/Makefile
new file mode 100644 (file)
index 0000000..f6d297c
--- /dev/null
@@ -0,0 +1,3 @@
+.PHONY: clean
+clean:
+       rm -f *.fasl *.x86f *.fas *.ufsl *.lib *.pfsl
diff --git a/deps/trivial-gray-streams/README b/deps/trivial-gray-streams/README
new file mode 100644 (file)
index 0000000..4e49ce1
--- /dev/null
@@ -0,0 +1,40 @@
+trivial-gray-streams
+====================
+
+Gray streams is an interface proposed for inclusion with ANSI CL
+by David N. Gray in Issue STREAM-DEFINITION-BY-USER
+(http://www.nhplace.com/kent/CL/Issues/stream-definition-by-user.html).
+The proposal did not make it into ANSI CL, but most popular
+CL implementations implement this facility anyway.
+
+This system provides an extremely thin compatibility layer for gray
+streams.
+
+How to use it
+=============
+
+Use the package TRIVIAL-GRAY-STREAMS to refer Gray stream
+classes to inherit from, generic functions to implement.
+
+Extensions
+==========
+
+The Gray proposal was made before the ANCI CL standard was finalized,
+and was based on the Common Lisp The Language book.
+
+The book does not have cl:file-position, cl:read-sequence, cl:write-sequence
+functions. That's why (we think) the Gray proposal does not specify
+their counterparts: stream-file-position, stream-read-sequence, stream-write-sequence.
+
+trivial-gray-streams supports these functions:
+
+Generic function STREAM-READ-SEQUENCE (stream sequence start end &key)
+Generic function STREAM-WRITE-SEQUENCE (stream sequence start end &key)
+
+    Notice that we use two required arguments and allow additional
+    keyword arguments. Your methods on these function should have
+    compliant lambda lists:
+        (stream sequence start end &key)
+
+Generic function STREAM-FILE-POSITION (stream) => file position
+Generic function (SETF STREAM-FILE-POSITION) (position-spec stream) => successp
diff --git a/deps/trivial-gray-streams/build.xcvb b/deps/trivial-gray-streams/build.xcvb
new file mode 100644 (file)
index 0000000..abd5622
--- /dev/null
@@ -0,0 +1,7 @@
+#+xcvb
+(module
+  (:fullname "trivial-gray-streams"
+   :depends-on
+    ("package"
+     "streams")
+   :supersedes-asdf ("trivial-gray-streams")))
diff --git a/deps/trivial-gray-streams/package.lisp b/deps/trivial-gray-streams/package.lisp
new file mode 100644 (file)
index 0000000..a875b97
--- /dev/null
@@ -0,0 +1,77 @@
+#+xcvb (module ())
+
+(in-package :cl-user)
+
+#+:abcl
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (require :gray-streams))
+
+#+cmu
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (require :gray-streams))
+
+#+allegro
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (unless (fboundp 'excl:stream-write-string)
+    (require "streamc.fasl")))
+
+#+ecl
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (gray::redefine-cl-functions))
+
+(macrolet
+    ((frob ()
+       (let ((gray-class-symbols
+              '(#:fundamental-stream
+                #:fundamental-input-stream #:fundamental-output-stream
+                #:fundamental-character-stream #:fundamental-binary-stream
+                #:fundamental-character-input-stream #:fundamental-character-output-stream
+                #:fundamental-binary-input-stream #:fundamental-binary-output-stream))
+             (gray-function-symbols
+              '(#:stream-read-char
+                #:stream-unread-char #:stream-read-char-no-hang
+                #:stream-peek-char #:stream-listen #:stream-read-line
+                #:stream-clear-input #:stream-write-char #:stream-line-column
+                #:stream-start-line-p #:stream-write-string #:stream-terpri
+                #:stream-fresh-line #:stream-finish-output #:stream-force-output
+                #:stream-clear-output #:stream-advance-to-column
+                #:stream-read-byte #:stream-write-byte)))
+        `(progn
+
+            (defpackage impl-specific-gray
+              (:use :cl)
+              (:import-from
+               #+sbcl :sb-gray
+               #+allegro :excl
+               #+cmu :ext
+               #+(or clisp ecl mocl) :gray
+               #+openmcl :ccl
+               #+lispworks :stream
+               #+abcl :gray-streams
+               #-(or sbcl allegro cmu clisp openmcl lispworks ecl abcl mocl) ...
+               ,@gray-class-symbols
+               ,@gray-function-symbols)
+              (:export
+               ,@gray-class-symbols
+               ,@gray-function-symbols))
+
+            (defpackage :trivial-gray-streams
+              (:use :cl)
+              (:import-from #:impl-specific-gray
+                            ;; We import and re-export only
+                            ;; function symbols;
+                            ;; But we define our own classes
+                            ;; mirroring the gray class hierarchy
+                            ;; of the lisp implementation (this
+                            ;; is necessary to define our methods
+                            ;; for particular generic functions)
+                            ,@gray-function-symbols)
+              (:export ,@gray-class-symbols
+                       ,@gray-function-symbols
+                       ;; extension functions
+                       #:stream-read-sequence
+                       #:stream-write-sequence
+                       #:stream-file-position
+                       ;; deprecated
+                       #:trivial-gray-stream-mixin))))))
+  (frob))
diff --git a/deps/trivial-gray-streams/streams.lisp b/deps/trivial-gray-streams/streams.lisp
new file mode 100644 (file)
index 0000000..cc39277
--- /dev/null
@@ -0,0 +1,277 @@
+#+xcvb (module (:depends-on ("package")))
+
+(in-package :trivial-gray-streams)
+
+(defclass fundamental-stream (impl-specific-gray:fundamental-stream) ())
+(defclass fundamental-input-stream
+    (fundamental-stream impl-specific-gray:fundamental-input-stream) ())
+(defclass fundamental-output-stream
+    (fundamental-stream impl-specific-gray:fundamental-output-stream) ())
+(defclass fundamental-character-stream
+    (fundamental-stream impl-specific-gray:fundamental-character-stream) ())
+(defclass fundamental-binary-stream
+    (fundamental-stream impl-specific-gray:fundamental-binary-stream) ())
+(defclass fundamental-character-input-stream
+    (fundamental-input-stream fundamental-character-stream
+     impl-specific-gray:fundamental-character-input-stream) ())
+(defclass fundamental-character-output-stream
+    (fundamental-output-stream fundamental-character-stream
+     impl-specific-gray:fundamental-character-output-stream) ())
+(defclass fundamental-binary-input-stream
+    (fundamental-input-stream fundamental-binary-stream
+     impl-specific-gray:fundamental-binary-input-stream) ())
+(defclass fundamental-binary-output-stream
+    (fundamental-output-stream fundamental-binary-stream
+     impl-specific-gray:fundamental-binary-output-stream) ())
+
+(defgeneric stream-read-sequence
+    (stream sequence start end &key &allow-other-keys))
+(defgeneric stream-write-sequence
+    (stream sequence start end &key &allow-other-keys))
+
+(defgeneric stream-file-position (stream))
+(defgeneric (setf stream-file-position) (newval stream))
+
+;;; Default methods for stream-read/write-sequence.
+;;;
+;;; It would be nice to implement default methods
+;;; in trivial gray streams, maybe borrowing the code
+;;; from some of CL implementations. But now, for
+;;; simplicity we will fallback to default implementation
+;;; of the implementation-specific analogue function which calls us.
+
+(defmethod stream-read-sequence ((stream fundamental-input-stream) seq start end &key)
+  (declare (ignore seq start end))
+  'fallback)
+
+(defmethod stream-write-sequence ((stream fundamental-output-stream) seq start end &key)
+  (declare (ignore seq start end))
+  'fallback)
+
+(defmacro or-fallback (&body body)
+  `(let ((result ,@body))
+     (if (eq result (quote fallback))
+         (call-next-method)
+         result)))
+
+;; Implementations should provide this default method, I believe, but
+;; at least sbcl and allegro don't.
+(defmethod stream-terpri ((stream fundamental-output-stream))
+  (write-char #\newline stream))
+
+;; stream-file-position could be specialized to
+;; fundamental-stream, but to support backward
+;; compatibility with flexi-streams, we specialize
+;; it on T. The reason: flexi-streams calls stream-file-position
+;; for non-gray stream:
+;; https://github.com/edicl/flexi-streams/issues/4
+(defmethod stream-file-position ((stream t))
+  nil)
+
+(defmethod (setf stream-file-position) (newval (stream t))
+  (declare (ignore newval))
+  nil)
+
+#+abcl
+(progn
+  (defmethod gray-streams:stream-read-sequence 
+      ((s fundamental-input-stream) seq &optional start end)
+    (or-fallback (stream-read-sequence s seq (or start 0) (or end (length seq)))))
+  
+  (defmethod gray-streams:stream-write-sequence 
+      ((s fundamental-output-stream) seq &optional start end)
+    (or-fallback (stream-write-sequence s seq (or start 0) (or end (length seq)))))
+  
+  (defmethod gray-streams:stream-write-string 
+      ((stream xp::xp-structure) string &optional (start 0) (end (length string)))
+    (xp::write-string+ string stream start end))
+
+  #+#.(cl:if (cl:and (cl:find-package :gray-streams)
+                    (cl:find-symbol "STREAM-FILE-POSITION" :gray-streams))
+            '(:and)
+            '(:or))
+  (defmethod gray-streams:stream-file-position
+      ((s fundamental-stream) &optional position)
+    (if position
+        (setf (stream-file-position s) position)
+        (stream-file-position s))))
+
+#+allegro
+(progn
+  (defmethod excl:stream-read-sequence
+      ((s fundamental-input-stream) seq &optional start end)
+    (or-fallback (stream-read-sequence s seq (or start 0) (or end (length seq)))))
+
+  (defmethod excl:stream-write-sequence
+      ((s fundamental-output-stream) seq &optional start end)
+    (or-fallback (stream-write-sequence s seq (or start 0) (or end (length seq)))))
+
+  (defmethod excl::stream-file-position
+       ((stream fundamental-stream) &optional position)
+     (if position
+         (setf (stream-file-position stream) position)
+         (stream-file-position stream))))
+
+;; Untill 2014-08-09 CMUCL did not have stream-file-position:
+;; http://trac.common-lisp.net/cmucl/ticket/100
+#+cmu
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (when (find-symbol (string '#:stream-file-position) '#:ext)
+    (pushnew :cmu-has-stream-file-position *features*)))
+
+#+cmu
+(progn
+  (defmethod ext:stream-read-sequence
+      ((s fundamental-input-stream) seq &optional start end)
+    (or-fallback (stream-read-sequence s seq (or start 0) (or end (length seq)))))
+  (defmethod ext:stream-write-sequence
+      ((s fundamental-output-stream) seq &optional start end)
+    (or-fallback (stream-write-sequence s seq (or start 0) (or end (length seq)))))
+
+  #+cmu-has-stream-file-position
+  (defmethod ext:stream-file-position ((stream fundamental-stream))
+    (stream-file-position stream))
+
+  #+cmu-has-stream-file-position
+  (defmethod (setf ext:stream-file-position) (position (stream fundamental-stream))
+    (setf (stream-file-position stream) position)))
+
+#+lispworks
+(progn
+  (defmethod stream:stream-read-sequence
+      ((s fundamental-input-stream) seq start end)
+    (or-fallback (stream-read-sequence s seq start end)))
+  (defmethod stream:stream-write-sequence
+      ((s fundamental-output-stream) seq start end)
+    (or-fallback (stream-write-sequence s seq start end)))
+
+  (defmethod stream:stream-file-position ((stream fundamental-stream))
+    (stream-file-position stream))
+  (defmethod (setf stream:stream-file-position)
+      (newval (stream fundamental-stream))
+    (setf (stream-file-position stream) newval)))
+
+#+openmcl
+(progn
+  (defmethod ccl:stream-read-vector
+      ((s fundamental-input-stream) seq start end)
+    (or-fallback (stream-read-sequence s seq start end)))
+  (defmethod ccl:stream-write-vector
+      ((s fundamental-output-stream) seq start end)
+    (or-fallback (stream-write-sequence s seq start end)))
+
+  (defmethod ccl:stream-read-list ((s fundamental-input-stream) list count)
+    (or-fallback (stream-read-sequence s list 0 count)))
+  (defmethod ccl:stream-write-list ((s fundamental-output-stream) list count)
+    (or-fallback (stream-write-sequence s list 0 count)))
+
+  (defmethod ccl::stream-position ((stream fundamental-stream) &optional new-position)
+    (if new-position
+       (setf (stream-file-position stream) new-position)
+       (stream-file-position stream))))
+
+;; up to version 2.43 there were no
+;; stream-read-sequence, stream-write-sequence
+;; functions in CLISP
+#+clisp
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (when (find-symbol (string '#:stream-read-sequence) '#:gray)
+    (pushnew :clisp-has-stream-read/write-sequence *features*)))
+
+#+clisp
+(progn
+
+  #+clisp-has-stream-read/write-sequence
+  (defmethod gray:stream-read-sequence
+      (seq (s fundamental-input-stream) &key start end)
+    (or-fallback (stream-read-sequence s seq (or start 0) (or end (length seq)))))
+
+  #+clisp-has-stream-read/write-sequence
+  (defmethod gray:stream-write-sequence
+      (seq (s fundamental-output-stream) &key start end)
+    (or-fallback (stream-write-sequence s seq (or start 0) (or end (length seq)))))
+
+  ;;; for old CLISP
+  (defmethod gray:stream-read-byte-sequence
+      ((s fundamental-input-stream)
+       seq
+       &optional start end no-hang interactive)
+    (when no-hang
+      (error "this stream does not support the NO-HANG argument"))
+    (when interactive
+      (error "this stream does not support the INTERACTIVE argument"))
+    (or-fallback (stream-read-sequence s seq start end)))
+
+  (defmethod gray:stream-write-byte-sequence
+      ((s fundamental-output-stream)
+       seq
+       &optional start end no-hang interactive)
+    (when no-hang
+      (error "this stream does not support the NO-HANG argument"))
+    (when interactive
+      (error "this stream does not support the INTERACTIVE argument"))
+    (or-fallback (stream-write-sequence s seq start end)))
+
+  (defmethod gray:stream-read-char-sequence
+      ((s fundamental-input-stream) seq &optional start end)
+    (or-fallback (stream-read-sequence s seq start end)))
+
+  (defmethod gray:stream-write-char-sequence
+      ((s fundamental-output-stream) seq &optional start end)
+    (or-fallback (stream-write-sequence s seq start end)))
+
+  ;;; end of old CLISP read/write-sequence support
+
+  (defmethod gray:stream-position ((stream fundamental-stream) position)
+    (if position
+        (setf (stream-file-position stream) position)
+        (stream-file-position stream))))
+
+#+sbcl
+(progn
+  (defmethod sb-gray:stream-read-sequence
+      ((s fundamental-input-stream) seq &optional start end)
+    (or-fallback (stream-read-sequence s seq (or start 0) (or end (length seq)))))
+  (defmethod sb-gray:stream-write-sequence
+      ((s fundamental-output-stream) seq &optional start end)
+    (or-fallback (stream-write-sequence s seq (or start 0) (or end (length seq)))))
+  (defmethod sb-gray:stream-file-position 
+      ((stream fundamental-stream) &optional position)
+    (if position
+        (setf (stream-file-position stream) position)
+        (stream-file-position stream)))
+  ;; SBCL extension:
+  (defmethod sb-gray:stream-line-length ((stream fundamental-stream))
+    80))
+
+#+ecl
+(progn
+  (defmethod gray::stream-file-position 
+    ((stream fundamental-stream) &optional position)
+    (if position
+      (setf (stream-file-position stream) position)
+      (stream-file-position stream)))
+  (defmethod gray:stream-read-sequence
+    ((s fundamental-input-stream) seq &optional start end)
+    (or-fallback (stream-read-sequence s seq (or start 0) (or end (length seq)))))
+  (defmethod gray:stream-write-sequence
+    ((s fundamental-output-stream) seq &optional start end)
+    (or-fallback (stream-write-sequence s seq (or start 0) (or end (length seq))))))
+
+#+mocl
+(progn
+  (defmethod gray:stream-read-sequence
+      ((s fundamental-input-stream) seq &optional start end)
+    (or-fallback (stream-read-sequence s seq (or start 0) (or end (length seq)))))
+  (defmethod gray:stream-write-sequence
+      ((s fundamental-output-stream) seq &optional start end)
+    (or-fallback (stream-write-sequence s seq (or start 0) (or end (length seq)))))
+  (defmethod gray:stream-file-position
+      ((stream fundamental-stream) &optional position)
+    (if position
+       (setf (stream-file-position stream) position)
+       (stream-file-position stream))))
+
+;; deprecated
+(defclass trivial-gray-stream-mixin () ())
+
diff --git a/deps/trivial-gray-streams/test/package.lisp b/deps/trivial-gray-streams/test/package.lisp
new file mode 100644 (file)
index 0000000..e836637
--- /dev/null
@@ -0,0 +1,6 @@
+(defpackage trivial-gray-streams-test \r
+  (:use :cl #:trivial-gray-streams)\r
+  (:shadow #:method)\r
+  (:export #:run-tests\r
+           #:failed-test-names))\r
+\r
diff --git a/deps/trivial-gray-streams/test/run-on-many-lisps.lisp b/deps/trivial-gray-streams/test/run-on-many-lisps.lisp
new file mode 100644 (file)
index 0000000..9a2a9a3
--- /dev/null
@@ -0,0 +1,66 @@
+(ql:quickload :trivial-gray-streams)\r
+(ql:quickload :test-grid-agent)\r
+(ql:quickload :cl-fad)\r
+(in-package :cl-user)\r
+\r
+(defparameter *abcl* (make-instance 'lisp-exe:abcl\r
+                                    :java-exe-path "C:\\Program Files\\Java\\jdk1.6.0_26\\bin\\java"\r
+                                    :abcl-jar-path "C:\\Users\\anton\\unpacked\\abcl\\abcl-bin-1.1.0\\abcl.jar"))\r
+(defparameter *clisp* (make-instance 'lisp-exe:clisp :exe-path "clisp"))\r
+(defparameter *ccl-1.8-x86* (make-instance 'lisp-exe:ccl\r
+                                           :exe-path "C:\\Users\\anton\\unpacked\\ccl\\ccl-1.8-windows\\wx86cl.exe"))\r
+(defparameter *ccl-1.8-x86-64* (make-instance 'lisp-exe:ccl\r
+                                              :exe-path "C:\\Users\\anton\\unpacked\\ccl\\ccl-1.8-windows\\wx86cl64.exe"))\r
+(defparameter *sbcl-1.1.0.45* (make-instance 'lisp-exe:sbcl :exe-path "C:\\Program Files (x86)\\Steel Bank Common Lisp\\1.1.0.45\\run.bat"))\r
+(defparameter *sbcl-win-branch-64* (make-instance 'lisp-exe:sbcl :exe-path "C:\\Program Files\\Steel Bank Common Lisp\\1.1.0.36.mswinmt.1201-284e340\\run.bat"))\r
+(defparameter *sbcl-win-branch-32* (make-instance 'lisp-exe:sbcl :exe-path "C:\\Program Files (x86)\\Steel Bank Common Lisp\\1.1.0.36.mswinmt.1201-284e340\\run.bat"))\r
+(defparameter *ecl-bytecode* (make-instance 'lisp-exe:ecl\r
+                                            :exe-path "C:\\Users\\anton\\projects\\ecl\\bin\\ecl.exe"\r
+                                            :compiler :bytecode))\r
+(defparameter *ecl-lisp-to-c* (make-instance 'lisp-exe:ecl\r
+                                             :exe-path "C:\\Users\\anton\\projects\\ecl\\bin\\ecl.exe"\r
+                                             :compiler :lisp-to-c))\r
+(defparameter *acl* (make-instance 'lisp-exe:acl :exe-path "C:\\Program Files (x86)\\acl90express\\alisp.exe"))\r
+\r
+(defun run-on-many-lisps (run-description test-run-dir quicklisp-dir lisps)\r
+  (ensure-directories-exist test-run-dir)\r
+  (let ((fasl-root (merge-pathnames "fasl/" test-run-dir)))\r
+    (labels ((log-name (lisp)\r
+               (substitute #\- #\.\r
+                           ;; Substitute dots by hypens if our main process is CCL, it \r
+                           ;; prepends the > symbol before dots;\r
+                           ;; for example: 1.1.0.36.mswinmt.1201-284e340 => 1>.1>.0>.36>.mswinmt.1201-284e340\r
+                           ;; When we pass such a pathname to another lisps, they can't handle it.\r
+                           (string-downcase (tg-agent::implementation-identifier lisp))))\r
+             (fasl-dir (lisp)\r
+               (merge-pathnames (format nil "~A/" (log-name lisp))\r
+                                fasl-root))\r
+             (run (lisp)\r
+               (let* ((lib-result (tg-agent::proc-run-libtest lisp\r
+                                                              :trivial-gray-streams\r
+                                                              run-description\r
+                                                              (merge-pathnames (log-name lisp) test-run-dir)\r
+                                                              quicklisp-dir\r
+                                                              (fasl-dir lisp)))\r
+                      (status (getf lib-result :status)))\r
+                 (if (listp status)\r
+                     (getf status :failed-tests)\r
+                     status))))\r
+      (let ((results (mapcar (lambda (lisp)\r
+                               (list (tg-agent::implementation-identifier lisp)\r
+                                     (run lisp)))\r
+                             lisps)))\r
+        (tg-utils::write-to-file results (merge-pathnames "resutls.lisp" test-run-dir))\r
+        (cl-fad:delete-directory-and-files fasl-root)\r
+        results))))\r
+\r
+(run-on-many-lisps '(:lib-world "quicklisp 2013-02-17 + trivial-gray-streams.head"\r
+                     :contact-email "avodonosov@yandex.ru")\r
+                   "C:\\Users\\anton\\projects\\trivial-gray-streams\\test\\"\r
+                   (merge-pathnames "quicklisp/" (user-homedir-pathname))\r
+                   (list *sbcl-1.1.0.45* *sbcl-win-branch-64* *sbcl-win-branch-32*\r
+                         *abcl*\r
+                         *clisp*\r
+                         *ccl-1.8-x86* *ccl-1.8-x86-64*                         \r
+                         *ecl-bytecode* *ecl-lisp-to-c*\r
+                         *acl*))\r
diff --git a/deps/trivial-gray-streams/test/test-framework.lisp b/deps/trivial-gray-streams/test/test-framework.lisp
new file mode 100644 (file)
index 0000000..8f9717a
--- /dev/null
@@ -0,0 +1,60 @@
+(in-package :trivial-gray-streams-test)\r
+\r
+;;; test framework\r
+\r
+#|\r
+  Used like this:\r
+\r
+  (list (test (add) (assert (= 5 (+ 2 2))))\r
+        (test (mul) (assert (= 4 (* 2 2))))\r
+        (test (subst) (assert (= 3 (- 4 2)))))\r
+\r
+  => ;; list of test results, 2 failed 1 passed\r
+     (#<TEST-RESULT ADD :FAIL The assertion (= 5 (+ 2 2)) failed.>\r
+      #<TEST-RESULT MUL :OK>\r
+      #<TEST-RESULT SUBST :FAIL The assertion (= 3 (- 4 2)) failed.>)\r
+\r
+|#\r
+\r
+(defclass test-result ()\r
+  ((name :type symbol\r
+         :initarg :name\r
+         :initform (error ":name is requierd")\r
+         :accessor name)\r
+   (status :type (or (eql :ok) (eql :fail))\r
+           :initform :ok\r
+           :initarg :status\r
+           :accessor status)\r
+   (cause :type (or null condition)\r
+          :initform nil\r
+          :initarg :cause\r
+          :accessor cause)))\r
+\r
+(defun failed-p (test-result)\r
+  (eq (status test-result) :fail))\r
+\r
+(defmethod print-object ((r test-result) stream)\r
+  (print-unreadable-object (r stream :type t)\r
+    (format stream "~S ~S~@[ ~A~]" (name r) (status r) (cause r))))\r
+\r
+(defparameter *allow-debugger* nil)\r
+\r
+(defun test-impl (name body-fn)\r
+  (flet ((make-result (status &optional cause)\r
+           (make-instance 'test-result :name name :status status :cause cause)))\r
+    (handler-bind ((serious-condition\r
+                    (lambda (c)\r
+                      (unless *allow-debugger*\r
+                        (format t "FAIL: ~A~%" c)\r
+                        (let ((result (make-result :fail c)))\r
+                          (return-from test-impl result))))))\r
+      (format t "Running test ~S... " name)\r
+      (funcall body-fn)\r
+      (format t "OK~%")\r
+      (make-result :ok))))\r
+\r
+(defmacro test ((name) &body body)\r
+  "If the BODY signals a SERIOUS-CONDITION\r
+this macro returns a failed TEST-RESULT; otherwise\r
+returns a successfull TEST-RESULT."\r
+  `(test-impl (quote ,name) (lambda () ,@body)))\r
diff --git a/deps/trivial-gray-streams/test/test.lisp b/deps/trivial-gray-streams/test/test.lisp
new file mode 100644 (file)
index 0000000..8025b10
--- /dev/null
@@ -0,0 +1,212 @@
+(in-package :trivial-gray-streams-test)\r
+\r
+;;; assert-invoked - a tool to check that specified method with parameters has\r
+;;; been invoked during execution of a code body\r
+\r
+(define-condition invoked ()\r
+  ((method :type (or symbol cons) ;; cons is for (setf method)\r
+           :accessor method\r
+           :initarg :method\r
+           :initform (error ":method is required"))\r
+   (args :type list\r
+         :accessor args\r
+         :initarg :args\r
+         :initform nil)))\r
+\r
+(defun assert-invoked-impl (method args body-fn)\r
+  (let ((expected-invocation (cons method args))\r
+        (actual-invocations nil))\r
+    (handler-bind ((invoked (lambda (i)\r
+                              (let ((invocation (cons (method i) (args i))))\r
+                                (when (equalp invocation expected-invocation)\r
+                                  (return-from assert-invoked-impl nil))\r
+                                (push invocation actual-invocations)))))\r
+      (funcall body-fn))\r
+    (let ((*package* (find-package :keyword))) ; ensures package prefixes are printed\r
+      (error "expected invocation: ~(~S~) actual: ~{~(~S~)~^, ~}"\r
+             expected-invocation (reverse actual-invocations)))))\r
+\r
+(defmacro assert-invoked ((method &rest args) &body body)\r
+  "If during execution of the BODY the specified METHOD with ARGS\r
+hasn't been invoked, signals an ERROR."\r
+  `(assert-invoked-impl (quote ,method) (list ,@args) (lambda () ,@body)))\r
+\r
+(defun invoked (method &rest args)\r
+  (signal 'invoked :method method :args args))\r
+\r
+;;; The tests.\r
+\r
+#|\r
+  We will define a gray stream class, specialise \r
+  the gray generic function methods on it and test that the methods\r
+  are invoked when we call functions from common-lisp package\r
+  on that stream.\r
+\r
+  Some of the gray generic functions are only invoked by default\r
+  methods of other generic functions:\r
+\r
+      cl:format ~t or cl:pprint -> stream-advance-to-column -> stream-line-column\r
+                                                               stream-write-char\r
+      cl:fresh-line -> stream-fresh-line -> stream-start-line-p -> stream-line-column\r
+                                            stream-terpri\r
+\r
+\r
+  If we define our methods for stream-advance-to-column and stream-fresh-line,\r
+  then stream-start-line-p, stream-terpri, stram-line-column are not invoked.\r
+\r
+  Therefore we define another gray stream class. The first class is used\r
+  for all lower level functions (stream-terpri). The second class\r
+  is used to test methods for higher level functions (stream-fresh-line).\r
+|#\r
+\r
+(defclass test-stream (fundamental-binary-input-stream\r
+                       fundamental-binary-output-stream\r
+                       fundamental-character-input-stream\r
+                       fundamental-character-output-stream)\r
+  ())\r
+\r
+(defclass test-stream2 (test-stream) ())\r
+\r
+(defmethod stream-read-char ((stream test-stream))\r
+  (invoked 'stream-read-char stream))\r
+\r
+(defmethod stream-unread-char ((stream test-stream) char)\r
+  (invoked 'stream-unread-char stream char))\r
+\r
+(defmethod stream-read-char-no-hang ((stream test-stream))\r
+  (invoked 'stream-read-char-no-hang stream))\r
+\r
+(defmethod stream-peek-char ((stream test-stream))\r
+  (invoked 'stream-peek-char stream))\r
+\r
+(defmethod stream-listen ((stream test-stream))\r
+  (invoked 'stream-listen stream))\r
+\r
+(defmethod stream-read-line ((stream test-stream))\r
+  (invoked 'stream-read-line stream))\r
+\r
+(defmethod stream-clear-input ((stream test-stream))\r
+  (invoked 'stream-clear-input stream))\r
+\r
+(defmethod stream-write-char ((stream test-stream) char)\r
+  (invoked 'stream-write-char stream char))\r
+\r
+(defmethod stream-line-column ((stream test-stream))\r
+  (invoked 'stream-line-column stream))\r
+\r
+(defmethod stream-start-line-p ((stream test-stream))\r
+  (invoked 'stream-start-line-p stream))\r
+\r
+(defmethod stream-write-string ((stream test-stream) string &optional start end)\r
+  (invoked 'stream-write-string stream string start end))\r
+\r
+(defmethod stream-terpri ((stream test-stream))\r
+  (invoked 'stream-terpri stream))\r
+\r
+(defmethod stream-fresh-line ((stream test-stream2))\r
+  (invoked 'stream-fresh-line stream))\r
+\r
+(defmethod stream-finish-output ((stream test-stream))\r
+  (invoked 'stream-finish-output stream))\r
+\r
+(defmethod stream-force-output ((stream test-stream))\r
+  (invoked 'stream-force-output stream))\r
+\r
+(defmethod stream-clear-output ((stream test-stream))\r
+  (invoked 'stream-clear-output stream))\r
+\r
+(defmethod stream-advance-to-column ((stream test-stream2) column)\r
+  (invoked 'stream-advance-to-column stream column))\r
+\r
+(defmethod stream-read-byte ((stream test-stream))\r
+  (invoked 'stream-read-byte stream))\r
+\r
+(defmethod stream-write-byte ((stream test-stream) byte)\r
+  (invoked 'stream-write-byte stream byte))\r
+\r
+(defmethod stream-read-sequence ((s test-stream) seq start end &key)\r
+  (invoked 'stream-read-sequence s seq :start start :end end))\r
+\r
+(defmethod stream-write-sequence ((s test-stream) seq start end &key)\r
+  (invoked 'stream-write-sequence s seq :start start :end end))\r
+\r
+(defmethod stream-file-position ((s test-stream))\r
+  (invoked 'stream-file-position s))\r
+\r
+(defmethod (setf stream-file-position) (newval (s test-stream))\r
+  (invoked '(setf stream-file-position) newval s))\r
+\r
+;; Convinience macro, used when we want to name\r
+;; the test case with the same name as of the gray streams method we test.\r
+(defmacro test-invoked ((method &rest args) &body body)\r
+  `(test (,method)\r
+     (assert-invoked (,method ,@args)\r
+       ,@body)))\r
+\r
+(defun run-tests ()\r
+  (let ((s (make-instance 'test-stream))\r
+        (s2 (make-instance 'test-stream2)))\r
+    (list\r
+     (test-invoked (stream-read-char s)\r
+       (read-char s))\r
+     (test-invoked (stream-unread-char s #\a)\r
+       (unread-char #\a s))\r
+     (test-invoked (stream-read-char-no-hang s)\r
+       (read-char-no-hang s))\r
+     (test-invoked (stream-peek-char s)\r
+       (peek-char nil s))\r
+     (test-invoked (stream-listen s)\r
+       (listen s))\r
+     (test-invoked (stream-read-line s)\r
+       (read-line s))\r
+     (test-invoked (stream-clear-input s)\r
+       (clear-input s))\r
+     (test-invoked (stream-write-char s #\b)\r
+       (write-char #\b s))\r
+     (test-invoked (stream-line-column s)\r
+       (format s "~10,t"))\r
+     (test-invoked (stream-start-line-p s)\r
+       (fresh-line s))\r
+     (test-invoked (stream-write-string s "hello" 1 4)\r
+       (write-string "hello" s :start 1 :end 4))\r
+     (test-invoked (stream-terpri s)\r
+       (fresh-line s))\r
+     (test-invoked (stream-fresh-line s2)\r
+       (fresh-line s2))\r
+     (test-invoked (stream-finish-output s)\r
+       (finish-output s))\r
+     (test-invoked (stream-force-output s)\r
+       (force-output s))\r
+     (test-invoked (stream-clear-output s)\r
+       (clear-output s))\r
+     (test-invoked (stream-advance-to-column s2 10)\r
+        (format s2 "~10,t"))\r
+     (test-invoked (stream-read-byte s)\r
+       (read-byte s))\r
+     (test-invoked (stream-write-byte s 1)\r
+       (write-byte 1 s))\r
+     ;;; extensions\r
+     (test-invoked (stream-read-sequence s #(1 2) :start 0 :end 1)\r
+       (read-sequence #(1 2) s :start 0 :end 1))\r
+     (test-invoked (stream-write-sequence s #(1 2) :start 0 :end 1)\r
+       (write-sequence #(1 2) s :start 0 :end 1))\r
+     (test-invoked (stream-file-position s)\r
+       (file-position s))\r
+     (test (setf-stream-file-position)\r
+       (assert-invoked ((setf stream-file-position) 9 s)\r
+         (file-position s 9))))))\r
+\r
+(defun failed-tests (results)\r
+  (remove-if-not #'failed-p results))\r
+\r
+(defun failed-test-names (results)\r
+  (mapcar (lambda (result)\r
+            (string-downcase (name result)))\r
+          (failed-tests results)))\r
+               \r
+#|\r
+(failed-test-names (run-tests))\r
+\r
+(setf *allow-debugger* nil))\r
+\r
+|#
\ No newline at end of file
diff --git a/deps/trivial-gray-streams/trivial-gray-streams-test.asd b/deps/trivial-gray-streams/trivial-gray-streams-test.asd
new file mode 100644 (file)
index 0000000..9eb6b74
--- /dev/null
@@ -0,0 +1,10 @@
+;;; -*- mode: lisp -*-
+
+(defsystem :trivial-gray-streams-test
+  :version "2.0"
+  :depends-on (:trivial-gray-streams)
+  :pathname #P"test/"
+  :serial t
+  :components ((:file "package")
+               (:file "test-framework")
+               (:file "test")))
diff --git a/deps/trivial-gray-streams/trivial-gray-streams.asd b/deps/trivial-gray-streams/trivial-gray-streams.asd
new file mode 100644 (file)
index 0000000..d57afff
--- /dev/null
@@ -0,0 +1,10 @@
+;;; -*- mode: lisp -*-
+
+(defsystem :trivial-gray-streams
+  :description "Compatibility layer for Gray Streams (see http://www.cliki.net/Gray%20streams)."
+  :license "MIT"
+  :author "David Lichteblau"
+  :maintainer "Anton Vodonosov <avodonosov@yandex.ru>"
+  :version "2.0"
+  :serial t
+  :components ((:file "package") (:file "streams")))
diff --git a/deps/usocket/CHANGES b/deps/usocket/CHANGES
new file mode 100644 (file)
index 0000000..a91adcd
--- /dev/null
@@ -0,0 +1,99 @@
+0.6.4:
+
+* New feature: [SBCL] IPv6 support (patch from Guillaume LE VAILLANT, #15)
+* New feature: [API] SOCKET-SHUTDOWN added (patch from Thayne McCombs #9)
+* New feature: [Corman] minimal initial support of this platform
+* Bugfix: [SBCL/win32] wait-for-input nil-timeout bug (patch from Michal Herda, #13)
+* Bugfix: [ECL] included unistd.h for gethostname() (patch from Daniel Kochmanski, #7)
+* Bugfix: [LispWorks] SOCKET-RECEIVE now updates %READ-P (patch from Frank James)
+
+0.6.3:
+
+* Bugfix: [CCL] Further fixed CCL-1.11 compatibility and a typo in SOCKET-CONNECT for CCL-1.10.
+* Bugfix: [ECL] Fixed build in some versions.
+* Bugfix: [LispWorks] SOCKET-SEND and SOCKET-RECEIVE now throw conditions if something goes wrong.
+
+0.6.2:
+
+* Bugfix: [CCL] Fixed CCL-1.11 compatibility.
+* Bugfix: [ECL] Fixed compatibility on recent versions.
+* Bugfix: [LispWorks] Added support address-in-use-error condition on LW/Win32. (patch from Sergey Katrevich).
+
+0.6.1:
+
+* New feature: [MOCL] Initial MOCL support (TCP only, no W-F-I, patch from github.com/Wukix/usocket).
+* New feature: [MCL] Initial UDP support for Macintosh Common Lisp (MCL/RMCL).
+* New feature: Added TCP-NO-DELAY (TCP_NODELAY) support in SOCKET-OPTION, for TCP client
+* Bugfix: [CCL] Added (:external-format ccl:*default-external-format*) to SOCKET-CONNECT, to prevent it fallback to ISO-8859-1 on NIL. (Patch from Vsevolod Dyomkin)
+* Bugfix: [CCL] Performance improved WAIT-FOR-INPUT and other fixes. (patch from Faré <fahree@gmail.com>)
+
+0.6.0:
+
+* New feature: SOCKET-OPTION and (setf SOCKET-OPTION) for seting and geting various socket options.
+* New feature: SOCKET-SEND now support an CCL-like OFFSET keyword for sending only parts of the whole buffer.
+* New feature: [ECL] Added support for ECL DFFI mode on Windows. (no need for C compilers now)
+* Bugfix: [ECL] ECL now list sb-bsd-sockets as a dependency but relies on REQUIRE. (patched by Juanjo)
+* Bugfix: [ABCL] Make USOCKET compile warning-free on ABCL again: MAKE-IMMEDIATE-OBJECT was deprecated a while ago in favor of 2 predefined constants.
+* Bugfix: [LispWorks] remove redundant call to hcl:flag-special-free-action. (reported by Kamil Shakirov)
+* Bugfix: [CLISP] improved HANDLE-CONDITION for more CLISP environments.
+
+0.5.5:
+
+* Enhancement: SOCKET-CONNECT argument :nodelay can now set to :if-supported (patch from Anton Vodonosov).
+* Enhancement: [Server] adding *remote-host* *remote-port* to socket-server stream handler functions (suggested by Matthew Curry)
+* Bugfix: [LispWorks] Fixed UDP support for LispWorks 6.1 (patch from Camille Troillard by Martin Simmons).
+* Bugfix: [LispWorks] Stop using hcl:add-special-free-action for reclaiming unused UDP socket fds to improve multi-threading stablity (suggested by Camille Troillard).
+* Bugfix: [LispWorks] Fixed SOCKET-CONNECT on Windows, now LOCAL-PORT never have *auto-port* (0) as default value.
+
+0.5.4:
+
+* Bugfix: [ECL] Fixed for ECL's MAKE-BUILD by removing some unecessary code (reported by Juan Jose Garcia-Ripoll, the ECL maintainer)
+* Bugfix: [ACL] Fixed for Allegro CL modern mode.
+* Bugfix: [SBCL] SOCKET-CONNECT on TCP won't call bind() when keyword arguments LOCAL-HOST or LOCAL-PORT is not set. (reported by Robert Brown)
+
+0.5.3:
+
+* Bugfix: [MCL] Fixed SOCKET-LISTEN on vector addresses like #(0 0 0 0)
+* Bugfix: [MCL] Fixed WAIT-FOR-INPUT on passive sockets (stream-server-usocket)
+* Bugfix: [LispWorks] Fixed using OPEN-UDP-SOCKET in delivered applications (thanks to Camille Troillard and Martin Simmons, this fix is from LispWorks-UDP project).
+* Bugfix: [SBCL] Fixed for "SBCL data flush problem", reported by Robert Brown and confirmed by Nikodemus Siivola.
+
+0.5.2:
+
+* General: [SBCL] SOCKET-CONNECT's TIMEOUT argument was limited on non-Windows platforms.
+* Bugfix: [CLISP] WAIT-FOR-INPUT now functions right (with/without READY-ONLY), this made Hunchentoot working on CLISP. (Thanks to Anton Vodonosov <avodonosov@yandex.ru>)
+* Bugfix: [ABCL] Fix SOCKET-ACCEPT to follow the documented API so that when called without an :ELEMENT-TYPE argument. (Thanks to Mark Evenson, the ABCL developer)
+* Bugfix: [LispWorks] Fixed SOCKET-ACCEPT (Windows only) on WAIT-FOR-INPUTed sockets.
+* Bugfix: [SBCL, ECL] Fixed wrongly STATE set/unset for WAIT-FOR-INPUT on Windows (report by Elliott Slaughter)
+* Enhancement: Additional NAME keyword argument for SOCKET-SERVER for setting the server thread name.
+* Enhancement: [ABCL] GET-ADDRESS now works with underlying IP6 addresses.
+* Enhancement: [CLISP] missing GET-LOCAL-* methods for STREAM-SERVER-USOCKET was now added.
+
+0.5.1:
+
+* New feature: [CLISP] UDP (Datagram) support based on FFI (Win/Mac/Linux), no RAWSOCK needed.
+* Enhancement: SOCKET-SERVER return a second value (socket) when calling in new-thread mode.
+* Enhancement: [CLISP] Full support of DNS helper functions (GET-HOST-BY-NAME, ...) added.
+* Enhancement: [CLISP] Better network error type detection based on OS error code.
+* Enhancement: [LispWorks] Better network error type detection based on OS error code.
+* Bugfix: Fixed wrong macro expansions of {IP|PORT}-{FROM|TO}-OCTET-BUFFER functions (since 0.4.0)
+* Bugfix: SOCKET-CONNECT didn't set CONNECTED-P for datagram usockets on most backends.
+* Bugfix: [SBCL] Fixes for "SBCL/Win32: finalizer problem, etc", by Anton Kovalenko <anton@sw4me.com>
+* Bugfix: [SBCL] Fixed SOCKET-SERVER (UDP) on SBCL due to a issue in SOCKET-CONNECT when HOST is NIL.
+* Bugfix: [SBCL] SOCKET-CONNECT's TIMEOUT argument now works as a "connection timeout".
+* Bugfix: [CMUCL] Fixed SOCKET-SEND on unconnected usockets under Unicode version of CMUCL.
+* Bugfix: [CLISP] Fixed and confirmed UDP (Datagram) support (RAWSOCK version).
+
+0.5.0:
+
+* New supported platform: Macintosh Common Lisp (5.0 and up, plus RMCL)
+* Support for UDP (datagram-usocket) was added (for all supported platform except MCL)
+* Add WAIT-FOR-INPUT support for SBCL and ECL on win32.
+* Simple TCP and UDP server API: SOCKET-SERVER
+* Completely rewritten full-feature ABCL backends using latest Java interfaces
+* Lots of bug fixed since 0.4.1
+
+[TODO]
+
+* New feature: CLISP support some advanced TCP features which CLISP's SOCKET interface not provide
+* New feature: SOCKET-SHUTDOWN for TCP and UDP sockets.
diff --git a/deps/usocket/LICENSE b/deps/usocket/LICENSE
new file mode 100644 (file)
index 0000000..86cefd8
--- /dev/null
@@ -0,0 +1,24 @@
+(This is the MIT / X Consortium license as taken from 
+ http://www.opensource.org/licenses/mit-license.html)
+
+Copyright (c) 2003 Erik Enge
+Copyright (c) 2006-2007 Erik Huelsmann
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/deps/usocket/README.md b/deps/usocket/README.md
new file mode 100644 (file)
index 0000000..135feb7
--- /dev/null
@@ -0,0 +1,142 @@
+# Introduction
+
+This is the usocket Common Lisp sockets library: a library to bring
+sockets access to the broadest of common lisp implementations as possible.
+
+# The library currently supports:
+
+ - SBCL
+ - CMUCL
+ - ArmedBear Common Lisp
+ - GNU CLISP
+ - Allegro Common Lisp
+ - LispWorks
+ - Clozure CL
+ - ECL
+ - Scieneer Common Lisp
+ - Macintosh Common Lisp
+ - MOCL
+
+If your favorite common lisp misses in the list above, please contact
+usocket-devel@common-lisp.net and submit a request.  Please include
+references to available sockets functions in your lisp implementation.
+
+The library has been ASDF (http://cliki.net/ASDF) enabled, meaning
+that you can tar up a checkout and use that to ASDF-INSTALL:INSTALL
+the package in your system package site.  (Or use your usual ASDF
+tricks to use the checkout directly.)
+
+# Remarks on licensing
+
+Even though the source code has an MIT style license attached to it,
+when compiling this code with some of the supported lisp implementations
+you may not end up with an MIT style binary version due to the licensing
+of the implementations themselves.  ECL is such an example and - when
+it will become supported - GCL is like that too.
+
+# Non-support of :external-format
+
+Because of its definition in the hyperspec, there's no common
+external-format between lisp implementations: every vendor has chosen
+a different way to solve the problem of newline translation or
+character set recoding.
+
+Because there's no way to avoid platform specific code in the application
+when using external-format, the purpose of a portability layer gets
+defeated.  So, for now, usocket doesn't support external-format.
+
+The workaround to get reasonably portable external-format support is to
+layer a flexi-stream (from flexi-streams) on top of a usocket stream.
+
+# API definition
+
+ - usocket (class)
+ - stream-usocket (class; usocket derivative)
+ - stream-server-usocket (class; usocket derivative)
+ - socket-connect (function) [ to create an active/connected socket ]
+    socket-connect host port &key element-type
+      where `host' is a vectorized ip
+                      or a string representation of a dotted ip address
+                      or a hostname for lookup in the DNS system
+ - socket-listen (function) [ to create a passive/listening socket ]
+     socket-listen host port &key reuseaddress backlog element-type
+       where `host' has the same definition as above
+ - socket-accept (method) [ to create an active/connected socket ]
+     socket-accept socket &key element-type
+       returns (server side) a connected socket derived from a
+       listening/passive socket.
+ - socket-close (method)
+    socket-close socket
+      where socket a previously returned socket
+ - socket (usocket slot accessor),
+      the internal/implementation defined socket representation
+ - socket-stream (usocket slot accessor),
+    socket-stream socket
+      the return value of which satisfies the normal stream interface
+ - socket-shutdown
+
+## Errors:
+ - address-in-use-error
+ - address-not-available-error
+ - bad-file-descriptor-error
+ - connection-refused-error
+ - connection-aborted-error
+ - connection-reset-error
+ - invalid-argument-error
+ - no-buffers-error
+ - operation-not-supported-error
+ - operation-not-permitted-error
+ - protocol-not-supported-error
+ - socket-type-not-supported-error
+ - network-unreachable-error
+ - network-down-error
+ - network-reset-error
+ - host-down-error
+ - host-unreachable-error
+ - shutdown-error
+ - timeout-error
+ - unkown-error
+
+## Non-fatal conditions:
+ - interrupted-condition
+ - unkown-condition
+
+(for a description of the API methods and functions see
+  http://common-lisp.net/project/usocket/api-docs.shtml.)
+
+# Test suite
+
+The test suite unfortunately isn't mature enough yet to run without
+some manual configuration.  Several elements are required which are
+hard to programatically detect.  Please adjust the test file before
+running the tests, for these variables:
+
+- +non-existing-host+: The stringified IP address of a host on the
+     same subnet.  No physical host may be present.
+- +unused-local-port+: A port number of a port not in use on the
+     machine the tests run on.
+- +common-lisp-net+: A vector with 4 integer elements which make up
+     an IP address. This must be the IP "common-lisp.net" resolves to.
+
+# Known problems
+
+- CMUCL error reporting wrt sockets raises only simple-errors
+  meaning there's no way to tell different error conditions apart.
+  All errors are mapped to unknown-error on CMUCL.
+
+- The ArmedBear backend doesn't do any error mapping (yet). Java
+  defines exceptions at the wrong level (IMO), since the exception
+  reported bares a relation to the function failing, not the actual
+  error that occurred: for example 'Address already in use' (when
+  creating a passive socket) is reported as a BindException with
+  an error text of 'Address already in use'. There's no way to sanely
+  map 'BindException' to a meaningfull error in usocket. [This does not
+  mean the backend should not at least map to 'unknown-error'!]
+
+- When using the library with ECL, you need the C compiler installed
+  to be able to compile and load the Foreign Function Interface.
+  Not all ECL targets support DFFI yet, so on some targets this would
+  be the case anyway.  By depending on this technique, usocket can
+  reuse the FFI code on all platforms (including Windows).  This benefit
+  currently outweighs the additional requirement. (hey, it's *Embeddable*
+  Common Lisp, so, you probably wanted to embed it all along, right?)
diff --git a/deps/usocket/TODO b/deps/usocket/TODO
new file mode 100644 (file)
index 0000000..e631798
--- /dev/null
@@ -0,0 +1,5 @@
+- Fix condition systems (making all implementation generate same error)
+- Add INET6 support.
+- IOlib backend
+
+For more TODO items, see http://trac.common-lisp.net/usocket/report.
diff --git a/deps/usocket/backend/abcl.lisp b/deps/usocket/backend/abcl.lisp
new file mode 100644 (file)
index 0000000..657708f
--- /dev/null
@@ -0,0 +1,441 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; New ABCL networking support (replacement to old armedbear.lisp)
+;;;; Author: Chun Tian (binghe)
+
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+;;; Java Classes ($*...)
+(defvar $*boolean (jclass "boolean"))
+(defvar $*byte (jclass "byte"))
+(defvar $*byte[] (jclass "[B"))
+(defvar $*int (jclass "int"))
+(defvar $*long (jclass "long"))
+(defvar $*|Byte| (jclass "java.lang.Byte"))
+(defvar $*DatagramChannel (jclass "java.nio.channels.DatagramChannel"))
+(defvar $*DatagramPacket (jclass "java.net.DatagramPacket"))
+(defvar $*DatagramSocket (jclass "java.net.DatagramSocket"))
+(defvar $*Inet4Address (jclass "java.net.Inet4Address"))
+(defvar $*InetAddress (jclass "java.net.InetAddress"))
+(defvar $*InetSocketAddress (jclass "java.net.InetSocketAddress"))
+(defvar $*Iterator (jclass "java.util.Iterator"))
+(defvar $*SelectableChannel (jclass "java.nio.channels.SelectableChannel"))
+(defvar $*SelectionKey (jclass "java.nio.channels.SelectionKey"))
+(defvar $*Selector (jclass "java.nio.channels.Selector"))
+(defvar $*ServerSocket (jclass "java.net.ServerSocket"))
+(defvar $*ServerSocketChannel (jclass "java.nio.channels.ServerSocketChannel"))
+(defvar $*Set (jclass "java.util.Set"))
+(defvar $*Socket (jclass "java.net.Socket"))
+(defvar $*SocketAddress (jclass "java.net.SocketAddress"))
+(defvar $*SocketChannel (jclass "java.nio.channels.SocketChannel"))
+(defvar $*String (jclass "java.lang.String"))
+
+;;; Java Constructor ($%.../n)
+(defvar $%Byte/0 (jconstructor $*|Byte| $*byte))
+(defvar $%DatagramPacket/3 (jconstructor $*DatagramPacket $*byte[] $*int $*int))
+(defvar $%DatagramPacket/5 (jconstructor $*DatagramPacket $*byte[] $*int $*int $*InetAddress $*int))
+(defvar $%DatagramSocket/0 (jconstructor $*DatagramSocket))
+(defvar $%DatagramSocket/1 (jconstructor $*DatagramSocket $*int))
+(defvar $%DatagramSocket/2 (jconstructor $*DatagramSocket $*int $*InetAddress))
+(defvar $%InetSocketAddress/1 (jconstructor $*InetSocketAddress $*int))
+(defvar $%InetSocketAddress/2 (jconstructor $*InetSocketAddress $*InetAddress $*int))
+(defvar $%ServerSocket/0 (jconstructor $*ServerSocket))
+(defvar $%ServerSocket/1 (jconstructor $*ServerSocket $*int))
+(defvar $%ServerSocket/2 (jconstructor $*ServerSocket $*int $*int))
+(defvar $%ServerSocket/3 (jconstructor $*ServerSocket $*int $*int $*InetAddress))
+(defvar $%Socket/0 (jconstructor $*Socket))
+(defvar $%Socket/2 (jconstructor $*Socket $*InetAddress $*int))
+(defvar $%Socket/4 (jconstructor $*Socket $*InetAddress $*int $*InetAddress $*int))
+
+;;; Java Methods ($@...[/Class]/n)
+(defvar $@accept/0 (jmethod $*ServerSocket "accept"))
+(defvar $@bind/DatagramSocket/1 (jmethod $*DatagramSocket "bind" $*SocketAddress))
+(defvar $@bind/ServerSocket/1 (jmethod $*ServerSocket "bind" $*SocketAddress))
+(defvar $@bind/ServerSocket/2 (jmethod $*ServerSocket "bind" $*SocketAddress $*int))
+(defvar $@bind/Socket/1 (jmethod $*Socket "bind" $*SocketAddress))
+(defvar $@byteValue/0 (jmethod $*|Byte| "byteValue"))
+(defvar $@channel/0 (jmethod $*SelectionKey "channel"))
+(defvar $@close/DatagramSocket/0 (jmethod $*DatagramSocket "close"))
+(defvar $@close/Selector/0 (jmethod $*Selector "close"))
+(defvar $@close/ServerSocket/0 (jmethod $*ServerSocket "close"))
+(defvar $@close/Socket/0 (jmethod $*Socket "close"))
+(defvar $@shutdownInput/Socket/0 (jmethod $*Socket "shutdownInput"))
+(defvar $@shutdownOutput/Socket/0 (jmethod $*Socket "shutdownOutput"))
+(defvar $@configureBlocking/1 (jmethod $*SelectableChannel "configureBlocking" $*boolean))
+(defvar $@connect/DatagramChannel/1 (jmethod $*DatagramChannel "connect" $*SocketAddress))
+(defvar $@connect/Socket/1 (jmethod $*Socket "connect" $*SocketAddress))
+(defvar $@connect/Socket/2 (jmethod $*Socket "connect" $*SocketAddress $*int))
+(defvar $@connect/SocketChannel/1 (jmethod $*SocketChannel "connect" $*SocketAddress))
+(defvar $@getAddress/0 (jmethod $*InetAddress "getAddress"))
+(defvar $@getAllByName/1 (jmethod $*InetAddress "getAllByName" $*String))
+(defvar $@getByName/1 (jmethod $*InetAddress "getByName" $*String))
+(defvar $@getChannel/DatagramSocket/0 (jmethod $*DatagramSocket "getChannel"))
+(defvar $@getChannel/ServerSocket/0 (jmethod $*ServerSocket "getChannel"))
+(defvar $@getChannel/Socket/0 (jmethod $*Socket "getChannel"))
+(defvar $@getAddress/DatagramPacket/0 (jmethod $*DatagramPacket "getAddress"))
+(defvar $@getHostName/0 (jmethod $*InetAddress "getHostName"))
+(defvar $@getInetAddress/DatagramSocket/0 (jmethod $*DatagramSocket "getInetAddress"))
+(defvar $@getInetAddress/ServerSocket/0 (jmethod $*ServerSocket "getInetAddress"))
+(defvar $@getInetAddress/Socket/0 (jmethod $*Socket "getInetAddress"))
+(defvar $@getLength/DatagramPacket/0 (jmethod $*DatagramPacket "getLength"))
+(defvar $@getLocalAddress/DatagramSocket/0 (jmethod $*DatagramSocket "getLocalAddress"))
+(defvar $@getLocalAddress/Socket/0 (jmethod $*Socket "getLocalAddress"))
+(defvar $@getLocalPort/DatagramSocket/0 (jmethod $*DatagramSocket "getLocalPort"))
+(defvar $@getLocalPort/ServerSocket/0 (jmethod $*ServerSocket "getLocalPort"))
+(defvar $@getLocalPort/Socket/0 (jmethod $*Socket "getLocalPort"))
+(defvar $@getOffset/DatagramPacket/0 (jmethod $*DatagramPacket "getOffset"))
+(defvar $@getPort/DatagramPacket/0 (jmethod $*DatagramPacket "getPort"))
+(defvar $@getPort/DatagramSocket/0 (jmethod $*DatagramSocket "getPort"))
+(defvar $@getPort/Socket/0 (jmethod $*Socket "getPort"))
+(defvar $@hasNext/0 (jmethod $*Iterator "hasNext"))
+(defvar $@iterator/0 (jmethod $*Set "iterator"))
+(defvar $@next/0 (jmethod $*Iterator "next"))
+(defvar $@open/DatagramChannel/0 (jmethod $*DatagramChannel "open"))
+(defvar $@open/Selector/0 (jmethod $*Selector "open"))
+(defvar $@open/ServerSocketChannel/0 (jmethod $*ServerSocketChannel "open"))
+(defvar $@open/SocketChannel/0 (jmethod $*SocketChannel "open"))
+(defvar $@receive/1 (jmethod $*DatagramSocket "receive" $*DatagramPacket))
+(defvar $@register/2 (jmethod $*SelectableChannel "register" $*Selector $*int))
+(defvar $@select/0 (jmethod $*Selector "select"))
+(defvar $@select/1 (jmethod $*Selector "select" $*long))
+(defvar $@selectedKeys/0 (jmethod $*Selector "selectedKeys"))
+(defvar $@send/1 (jmethod $*DatagramSocket "send" $*DatagramPacket))
+(defvar $@setReuseAddress/1 (jmethod $*ServerSocket "setReuseAddress" $*boolean))
+(defvar $@setSoTimeout/DatagramSocket/1 (jmethod $*DatagramSocket "setSoTimeout" $*int))
+(defvar $@setSoTimeout/Socket/1 (jmethod $*Socket "setSoTimeout" $*int))
+(defvar $@setTcpNoDelay/1 (jmethod $*Socket "setTcpNoDelay" $*boolean))
+(defvar $@socket/DatagramChannel/0 (jmethod $*DatagramChannel "socket"))
+(defvar $@socket/ServerSocketChannel/0 (jmethod $*ServerSocketChannel "socket"))
+(defvar $@socket/SocketChannel/0 (jmethod $*SocketChannel "socket"))
+(defvar $@validOps/0 (jmethod $*SelectableChannel "validOps"))
+
+;;; Java Field Variables ($+...)
+(defvar $+op-accept (jfield $*SelectionKey "OP_ACCEPT"))
+(defvar $+op-connect (jfield $*SelectionKey "OP_CONNECT"))
+(defvar $+op-read (jfield $*SelectionKey "OP_READ"))
+(defvar $+op-write (jfield $*SelectionKey "OP_WRITE"))
+
+
+;;; Wrapper functions (return-type: java-object)
+(defun %get-address (address)
+  (jcall $@getAddress/0 address))
+(defun %get-all-by-name (string) ; return a simple vector
+  (jstatic $@getAllByName/1 $*InetAddress string))
+(defun %get-by-name (string)
+  (jstatic $@getByName/1 $*InetAddress string))
+
+(defun host-to-inet4 (host)
+  "USOCKET host formats to Java Inet4Address, used internally."
+  (%get-by-name (host-to-hostname host)))
+
+;;; HANDLE-CONTITION
+
+(defparameter +abcl-error-map+
+  `(("java.net.BindException" . operation-not-permitted-error)
+    ("java.net.ConnectException" . connection-refused-error)
+    ("java.net.NoRouteToHostException" . network-unreachable-error) ; untested
+    ("java.net.PortUnreachableException" . protocol-not-supported-error) ; untested
+    ("java.net.ProtocolException" . protocol-not-supported-error) ; untested
+    ("java.net.SocketException" . socket-type-not-supported-error) ; untested
+    ("java.net.SocketTimeoutException" . timeout-error)))
+
+(defparameter +abcl-nameserver-error-map+
+  `(("java.net.UnknownHostException" . ns-host-not-found-error)))
+
+(defun handle-condition (condition &optional (socket nil))
+  (typecase condition
+    (java-exception
+     (let ((java-cause (java-exception-cause condition)))
+       (let* ((usock-error (cdr (assoc (jclass-of java-cause) +abcl-error-map+
+                                      :test #'string=)))
+             (usock-error (if (functionp usock-error)
+                              (funcall usock-error condition)
+                              usock-error))
+             (nameserver-error (cdr (assoc (jclass-of java-cause) +abcl-nameserver-error-map+
+                                           :test #'string=))))
+        (if nameserver-error
+            (error nameserver-error :host-or-ip nil)
+            (when usock-error
+              (error usock-error :socket socket))))))))
+
+;;; GET-HOSTS-BY-NAME
+
+(defun get-address (address)
+  (when address
+    (let* ((array (%get-address address))
+          (length (jarray-length array)))
+      (labels ((jbyte (n)
+                (let ((byte (jarray-ref array n)))
+                  (if (minusp byte) (+ 256 byte) byte))))
+       (cond  
+          ((= 4 length)
+           (vector (jbyte 0) (jbyte 1) (jbyte 2) (jbyte 3)))
+          ((= 16 length)
+           (vector (jbyte 0) (jbyte 1) (jbyte 2) (jbyte 3) 
+                   (jbyte 4) (jbyte 5) (jbyte 6) (jbyte 7)))
+          (t nil)))))) ; neither a IPv4 nor IPv6 address?!
+
+(defun get-hosts-by-name (name)
+  (with-mapped-conditions ()
+    (map 'list #'get-address (%get-all-by-name name))))
+
+;;; GET-HOST-BY-ADDRESS
+
+(defun get-host-by-address (host)
+  (let ((inet4 (host-to-inet4 host)))
+    (with-mapped-conditions ()
+      (jcall $@getHostName/0 inet4))))
+
+;;; SOCKET-CONNECT
+
+(defun socket-connect (host port &key (protocol :stream) (element-type 'character)
+                       timeout deadline (nodelay t nodelay-supplied-p)
+                       local-host local-port)
+  (when deadline (unsupported 'deadline 'socket-connect))
+  (let (socket stream usocket)
+    (ecase protocol
+      (:stream ; TCP
+       (let ((channel (jstatic $@open/SocketChannel/0 $*SocketChannel))
+            (address (jnew $%InetSocketAddress/2 (host-to-inet4 host) port)))
+        (setq socket (jcall $@socket/SocketChannel/0 channel))
+        ;; bind to local address if needed
+        (when (or local-host local-port)
+          (let ((local-address (jnew $%InetSocketAddress/2 (host-to-inet4 local-host) (or local-port 0))))
+            (with-mapped-conditions ()
+              (jcall $@bind/Socket/1 socket local-address))))
+        ;; connect to dest address
+        (with-mapped-conditions ()
+          (jcall $@connect/SocketChannel/1 channel address))
+        (setq stream (ext:get-socket-stream socket :element-type element-type)
+              usocket (make-stream-socket :stream stream :socket socket))
+        (when nodelay-supplied-p
+          (jcall $@setTcpNoDelay/1 socket (if nodelay ;; both t and :if-supported mean java:+true+
+                                           java:+true+ java:+false+)))
+        (when timeout
+          (jcall $@setSoTimeout/Socket/1 socket (truncate (* 1000 timeout))))))
+      (:datagram ; UDP
+       (let ((channel (jstatic $@open/DatagramChannel/0 $*DatagramChannel)))
+        (setq socket (jcall $@socket/DatagramChannel/0 channel))
+        ;; bind to local address if needed
+        (when (or local-host local-port)
+          (let ((local-address (jnew $%InetSocketAddress/2 (host-to-inet4 local-host) (or local-port 0))))
+            (with-mapped-conditions ()
+              (jcall $@bind/DatagramSocket/1 socket local-address))))
+        ;; connect to dest address if needed
+        (when (and host port)
+          (let ((address (jnew $%InetSocketAddress/2 (host-to-inet4 host) port)))
+            (with-mapped-conditions ()
+              (jcall $@connect/DatagramChannel/1 channel address))))
+        (setq usocket (make-datagram-socket socket :connected-p (if (and host port) t nil)))
+        (when timeout
+          (jcall $@setSoTimeout/DatagramSocket/1 socket (truncate (* 1000 timeout)))))))
+    usocket))
+
+;;; SOCKET-LISTEN
+
+(defun socket-listen (host port &key reuseaddress
+                      (reuse-address nil reuse-address-supplied-p)
+                     (backlog 5 backlog-supplied-p)
+                     (element-type 'character))
+  (declare (type boolean reuse-address))
+  (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
+        (channel (jstatic $@open/ServerSocketChannel/0 $*ServerSocketChannel))
+        (socket (jcall $@socket/ServerSocketChannel/0 channel))
+        (endpoint (jnew $%InetSocketAddress/2 (host-to-inet4 host) (or port 0))))
+    (jcall $@setReuseAddress/1 socket (if reuseaddress java:+true+ java:+false+))
+    (with-mapped-conditions (socket)
+      (if backlog-supplied-p
+         (jcall $@bind/ServerSocket/2 socket endpoint backlog)
+         (jcall $@bind/ServerSocket/1 socket endpoint)))
+    (make-stream-server-socket socket :element-type element-type)))
+
+;;; SOCKET-ACCEPT
+
+(defmethod socket-accept ((usocket stream-server-usocket) 
+                          &key (element-type 'character element-type-p))
+  (with-mapped-conditions (usocket)
+    (let* ((client-socket (jcall $@accept/0 (socket usocket)))
+           (element-type (if element-type-p 
+                             element-type
+                             (element-type usocket)))
+          (stream (ext:get-socket-stream client-socket :element-type element-type)))
+      (make-stream-socket :stream stream :socket client-socket))))
+
+;;; SOCKET-CLOSE
+
+(defmethod socket-close :before ((usocket usocket))
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket)))
+
+(defmethod socket-close ((usocket stream-server-usocket))
+  (with-mapped-conditions (usocket)
+    (jcall $@close/ServerSocket/0 (socket usocket))))
+
+(defmethod socket-close ((usocket stream-usocket))
+  (with-mapped-conditions (usocket)
+    (close (socket-stream usocket))
+    (jcall $@close/Socket/0 (socket usocket))))
+
+(defmethod socket-close ((usocket datagram-usocket))
+  (with-mapped-conditions (usocket)
+    (jcall $@close/DatagramSocket/0 (socket usocket))))
+
+(defmethod socket-shutdown ((usocket stream-usocket) direction)
+  (with-mapped-conditions (usocket)
+    (ecase direction
+      (:input
+       (jcall $@shutdownInput/Socket/0 (socket usocket)))
+      (:output
+       (jcall $@shutdownOutput/Socket/0 (socket usocket))))))
+
+;;; GET-LOCAL/PEER-NAME/ADDRESS/PORT
+
+(defmethod get-local-name ((usocket usocket))
+  (values (get-local-address usocket)
+         (get-local-port usocket)))
+
+(defmethod get-peer-name ((usocket usocket))
+  (values (get-peer-address usocket)
+         (get-peer-port usocket)))
+
+(defmethod get-local-address ((usocket stream-usocket))
+  (get-address (jcall $@getLocalAddress/Socket/0 (socket usocket))))
+
+(defmethod get-local-address ((usocket stream-server-usocket))
+  (get-address (jcall $@getInetAddress/ServerSocket/0 (socket usocket))))
+
+(defmethod get-local-address ((usocket datagram-usocket))
+  (get-address (jcall $@getLocalAddress/DatagramSocket/0 (socket usocket))))
+
+(defmethod get-peer-address ((usocket stream-usocket))
+  (get-address (jcall $@getInetAddress/Socket/0 (socket usocket))))
+
+(defmethod get-peer-address ((usocket datagram-usocket))
+  (get-address (jcall $@getInetAddress/DatagramSocket/0 (socket usocket))))
+
+(defmethod get-local-port ((usocket stream-usocket))
+  (jcall $@getLocalPort/Socket/0 (socket usocket)))
+
+(defmethod get-local-port ((usocket stream-server-usocket))
+  (jcall $@getLocalPort/ServerSocket/0 (socket usocket)))
+
+(defmethod get-local-port ((usocket datagram-usocket))
+  (jcall $@getLocalPort/DatagramSocket/0 (socket usocket)))
+
+(defmethod get-peer-port ((usocket stream-usocket))
+  (jcall $@getPort/Socket/0 (socket usocket)))
+
+(defmethod get-peer-port ((usocket datagram-usocket))
+  (jcall $@getPort/DatagramSocket/0 (socket usocket)))
+
+;;; SOCKET-SEND & SOCKET-RECEIVE
+
+(defun *->byte (data)
+  (declare (type (unsigned-byte 8) data)) ; required by SOCKET-SEND
+  (jnew $%Byte/0 (if (> data 127) (- data 256) data)))
+
+(defun byte->* (byte &optional (element-type '(unsigned-byte 8)))
+  (let* ((ub8 (if (minusp byte) (+ 256 byte) byte)))
+    (if (eq element-type 'character)
+       (code-char ub8)
+       ub8)))
+
+(defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
+  (let* ((socket (socket usocket))
+        (byte-array (jnew-array $*byte size))
+        (packet (if (and host port)
+                    (jnew $%DatagramPacket/5 byte-array 0 size (host-to-inet4 host) port)
+                    (jnew $%DatagramPacket/3 byte-array 0 size))))
+    ;; prepare sending data
+    (loop for i from offset below (+ size offset)
+       do (setf (jarray-ref byte-array i) (*->byte (aref buffer i))))
+    (with-mapped-conditions (usocket)
+      (jcall $@send/1 socket packet))))
+
+;;; TODO: return-host and return-port cannot be get ...
+(defmethod socket-receive ((usocket datagram-usocket) buffer length
+                          &key (element-type '(unsigned-byte 8)))
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+                  (integer 0)                          ; size
+                  (unsigned-byte 32)                   ; host
+                  (unsigned-byte 16)))                 ; port
+  (let* ((socket (socket usocket))
+        (real-length (or length +max-datagram-packet-size+))
+        (byte-array (jnew-array $*byte real-length))
+        (packet (jnew $%DatagramPacket/3 byte-array 0 real-length)))
+    (with-mapped-conditions (usocket)
+      (jcall $@receive/1 socket packet))
+    (let* ((receive-length (jcall $@getLength/DatagramPacket/0 packet))
+          (return-buffer (or buffer (make-array receive-length :element-type element-type))))
+      (loop for i from 0 below receive-length
+        do (setf (aref return-buffer i)
+                 (byte->* (jarray-ref byte-array i) element-type)))
+      (let ((return-host (if (connected-p usocket)
+                            (get-peer-address usocket)
+                            (get-address (jcall $@getAddress/DatagramPacket/0 packet))))
+           (return-port (if (connected-p usocket)
+                            (get-peer-port usocket)
+                            (jcall $@getPort/DatagramPacket/0 packet))))
+       (values return-buffer
+               receive-length
+               return-host
+               return-port)))))
+
+;;; WAIT-FOR-INPUT
+
+(defun socket-channel-class (usocket)
+  (cond ((stream-usocket-p usocket) $*SocketChannel)
+       ((stream-server-usocket-p usocket) $*ServerSocketChannel)
+       ((datagram-usocket-p usocket) $*DatagramChannel)))
+
+(defun get-socket-channel (usocket)
+  (let ((method (cond ((stream-usocket-p usocket) $@getChannel/Socket/0)
+                     ((stream-server-usocket-p usocket) $@getChannel/ServerSocket/0)
+                     ((datagram-usocket-p usocket) $@getChannel/DatagramSocket/0))))
+    (jcall method (socket usocket))))
+
+(defun wait-for-input-internal (wait-list &key timeout)
+  (let* ((sockets (wait-list-waiters wait-list))
+        (ops (logior $+op-read $+op-accept))
+        (selector (jstatic $@open/Selector/0 $*Selector))
+        (channels (mapcar #'get-socket-channel sockets)))
+    (unwind-protect
+        (with-mapped-conditions ()
+          (dolist (channel channels)
+            (jcall $@configureBlocking/1 channel java:+false+)
+            (jcall $@register/2 channel selector (logand ops (jcall $@validOps/0 channel))))
+          (let ((ready-count (if timeout
+                                 (jcall $@select/1 selector (truncate (* timeout 1000)))
+                                 (jcall $@select/0 selector))))
+            (when (plusp ready-count)
+              (let* ((keys (jcall $@selectedKeys/0 selector))
+                     (iterator (jcall $@iterator/0 keys))
+                     (%wait (wait-list-%wait wait-list)))
+                (loop while (jcall $@hasNext/0 iterator)
+                      do (let* ((key (jcall $@next/0 iterator))
+                                (channel (jcall $@channel/0 key)))
+                           (setf (state (gethash channel %wait)) :read)))))))
+      (jcall $@close/Selector/0 selector)
+      (dolist (channel channels)
+       (jcall $@configureBlocking/1 channel java:+true+)))))
+
+;;; WAIT-LIST
+
+;;; NOTE from original worker (Erik):
+;;; Note that even though Java has the concept of the Selector class, which
+;;; remotely looks like a wait-list, it requires the sockets to be non-blocking.
+;;; usocket however doesn't make any such guarantees and is therefore unable to
+;;; use the concept outside of the waiting routine itself (blergh!).
+
+(defun %setup-wait-list (wl)
+  (setf (wait-list-%wait wl)
+        (make-hash-table :test #'equal :rehash-size 1.3d0)))
+
+(defun %add-waiter (wl w)
+  (setf (gethash (get-socket-channel w) (wait-list-%wait wl)) w))
+
+(defun %remove-waiter (wl w)
+  (remhash (get-socket-channel w) (wait-list-%wait wl)))
diff --git a/deps/usocket/backend/allegro.lisp b/deps/usocket/backend/allegro.lisp
new file mode 100644 (file)
index 0000000..15aa446
--- /dev/null
@@ -0,0 +1,225 @@
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+#+cormanlisp
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (require :acl-socket))
+
+#+allegro
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (require :sock)
+  ;; for wait-for-input:
+  (require :process)
+  ;; note: the line below requires ACL 6.2+
+  (require :osi))
+
+(defun get-host-name ()
+  ;; note: the line below requires ACL 7.0+ to actually *work* on windows
+  #+allegro (excl.osi:gethostname)
+  #+cormanlisp "")
+
+(defparameter +allegro-identifier-error-map+
+  '((:address-in-use . address-in-use-error)
+    (:address-not-available . address-not-available-error)
+    (:network-down . network-down-error)
+    (:network-reset . network-reset-error)
+    (:network-unreachable . network-unreachable-error)
+    (:connection-aborted . connection-aborted-error)
+    (:connection-reset . connection-reset-error)
+    (:no-buffer-space . no-buffers-error)
+    (:shutdown . shutdown-error)
+    (:connection-timed-out . timeout-error)
+    (:connection-refused . connection-refused-error)
+    (:host-down . host-down-error)
+    (:host-unreachable . host-unreachable-error)))
+
+(defun handle-condition (condition &optional (socket nil))
+  "Dispatch correct usocket condition."
+  (typecase condition
+    #+allegro
+    (excl:socket-error
+     (let ((usock-err
+            (cdr (assoc (excl:stream-error-identifier condition)
+                        +allegro-identifier-error-map+))))
+       (if usock-err
+           (error usock-err :socket socket)
+         (error 'unknown-error
+                :real-error condition
+                :socket socket))))))
+
+(defun to-format (element-type)
+  (if (subtypep element-type 'character)
+      :text
+    :binary))
+
+(defun socket-connect (host port &key (protocol :stream) (element-type 'character)
+                       timeout deadline
+                       (nodelay t) ;; nodelay == t is the ACL default
+                       local-host local-port)
+  (when timeout (unsupported 'timeout 'socket-connect))
+  (when deadline (unsupported 'deadline 'socket-connect))
+  (when (eq nodelay :if-supported)
+    (setf nodelay t))
+
+  (let ((socket))
+    (setf socket
+          (with-mapped-conditions (socket)
+            (ecase protocol
+              (:stream
+              (labels ((make-socket ()
+                         (socket:make-socket :remote-host (host-to-hostname host)
+                                             :remote-port port
+                                             :local-host (when local-host
+                                                           (host-to-hostname local-host))
+                                             :local-port local-port
+                                             :format (to-format element-type)
+                                             :nodelay nodelay)))
+                 #+allegro
+                (if timeout
+                    (mp:with-timeout (timeout nil)
+                      (make-socket))
+                    (make-socket))
+                 #+cormanlisp (make-socket)))
+              (:datagram
+              (apply #'socket:make-socket
+                     (nconc (list :type protocol
+                                  :address-family :internet
+                                  :local-host (when local-host
+                                                (host-to-hostname local-host))
+                                  :local-port local-port
+                                  :format (to-format element-type))
+                            (if (and host port)
+                                (list :connect :active
+                                      :remote-host (host-to-hostname host)
+                                      :remote-port port)
+                                (list :connect :passive))))))))
+    (ecase protocol
+      (:stream
+       (make-stream-socket :socket socket :stream socket))
+      (:datagram
+       (make-datagram-socket socket :connected-p (and host port t))))))
+
+;; One socket close method is sufficient,
+;; because socket-streams are also sockets.
+(defmethod socket-close ((usocket usocket))
+  "Close socket."
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+    (close (socket usocket))))
+
+(defmethod socket-shutdown ((usocket stream-usocket) direction)
+  (with-mapped-conditions (usocket)
+    (socket:shutdown (socket usocket) :direction direction)))
+
+(defun socket-listen (host port
+                           &key reuseaddress
+                           (reuse-address nil reuse-address-supplied-p)
+                           (backlog 5)
+                           (element-type 'character))
+  ;; Allegro and OpenMCL socket interfaces bear very strong resemblence
+  ;; whatever you change here, change it also for OpenMCL
+  (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
+         (sock (with-mapped-conditions ()
+                 (apply #'socket:make-socket
+                        (append (list :connect :passive
+                                      :reuse-address reuseaddress
+                                      :local-port port
+                                      :backlog backlog
+                                      :format (to-format element-type)
+                                      ;; allegro now ignores :format
+                                      )
+                                (when (ip/= host *wildcard-host*)
+                                  (list :local-host host)))))))
+    (make-stream-server-socket sock :element-type element-type)))
+
+(defmethod socket-accept ((socket stream-server-usocket) &key element-type)
+  (declare (ignore element-type)) ;; allegro streams are multivalent
+  (let ((stream-sock
+         (with-mapped-conditions (socket)
+            (socket:accept-connection (socket socket)))))
+    (make-stream-socket :socket stream-sock :stream stream-sock)))
+
+(defmethod get-local-address ((usocket usocket))
+  (hbo-to-vector-quad (socket:local-host (socket usocket))))
+
+(defmethod get-peer-address ((usocket stream-usocket))
+  (hbo-to-vector-quad (socket:remote-host (socket usocket))))
+
+(defmethod get-local-port ((usocket usocket))
+  (socket:local-port (socket usocket)))
+
+(defmethod get-peer-port ((usocket stream-usocket))
+  #+allegro
+  (socket:remote-port (socket usocket)))
+
+(defmethod get-local-name ((usocket usocket))
+  (values (get-local-address usocket)
+          (get-local-port usocket)))
+
+(defmethod get-peer-name ((usocket stream-usocket))
+  (values (get-peer-address usocket)
+          (get-peer-port usocket)))
+
+#+allegro
+(defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
+  (with-mapped-conditions (usocket)
+    (let ((s (socket usocket)))
+      (socket:send-to s
+                     (if (zerop offset)
+                         buffer
+                         (subseq buffer offset (+ offset size)))
+                     size
+                     :remote-host host
+                     :remote-port port))))
+
+#+allegro
+(defmethod socket-receive ((socket datagram-usocket) buffer length &key)
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+                  (integer 0)                          ; size
+                  (unsigned-byte 32)                   ; host
+                  (unsigned-byte 16)))                 ; port
+  (with-mapped-conditions (socket)
+    (let ((s (socket socket)))
+      (socket:receive-from s length :buffer buffer :extract t))))
+
+(defun get-host-by-address (address)
+  (with-mapped-conditions ()
+    (socket:ipaddr-to-hostname (host-to-hbo address))))
+
+(defun get-hosts-by-name (name)
+  ;;###FIXME: ACL has the acldns module which returns all A records
+  ;; only problem: it doesn't fall back to tcp (from udp) if the returned
+  ;; structure is too long.
+  (with-mapped-conditions ()
+    (list (hbo-to-vector-quad (socket:lookup-hostname
+                               (host-to-hostname name))))))
+
+(defun %setup-wait-list (wait-list)
+  (declare (ignore wait-list)))
+
+(defun %add-waiter (wait-list waiter)
+  (push (socket waiter) (wait-list-%wait wait-list)))
+
+(defun %remove-waiter (wait-list waiter)
+  (setf (wait-list-%wait wait-list)
+        (remove (socket waiter) (wait-list-%wait wait-list))))
+
+#+allegro
+(defun wait-for-input-internal (wait-list &key timeout)
+  (with-mapped-conditions ()
+    (let ((active-internal-sockets
+           (if timeout
+               (mp:wait-for-input-available (wait-list-%wait wait-list)
+                                            :timeout timeout)
+             (mp:wait-for-input-available (wait-list-%wait wait-list)))))
+      ;; this is quadratic, but hey, the active-internal-sockets
+      ;; list is very short and it's only quadratic in the length of that one.
+      ;; When I have more time I could recode it to something of linear
+      ;; complexity.
+      ;; [Same code is also used in openmcl.lisp]
+      (dolist (x active-internal-sockets)
+        (setf (state (gethash x (wait-list-map wait-list)))
+              :read))
+      wait-list)))
diff --git a/deps/usocket/backend/clisp.lisp b/deps/usocket/backend/clisp.lisp
new file mode 100644 (file)
index 0000000..c4f2000
--- /dev/null
@@ -0,0 +1,703 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  #-ffi
+  (warn "This image doesn't contain FFI package, GET-HOST-NAME won't work.")
+  #-(or ffi rawsock)
+  (warn "This image doesn't contain either FFI or RAWSOCK package, no UDP support."))
+
+;; utility routine for looking up the current host name
+#+ffi
+(ffi:def-call-out get-host-name-internal
+         (:name "gethostname")
+         (:arguments (name (FFI:C-PTR (FFI:C-ARRAY-MAX ffi:character 256))
+                           :OUT :ALLOCA)
+                     (len ffi:int))
+         #+win32 (:library "WS2_32")
+        #-win32 (:library :default)
+         (:language #-win32 :stdc
+                    #+win32 :stdc-stdcall)
+         (:return-type ffi:int))
+
+(defun get-host-name ()
+  #+ffi
+  (multiple-value-bind (retcode name)
+      (get-host-name-internal 256)
+    (when (= retcode 0)
+      name))
+  #-ffi
+  "localhost")
+
+(defun get-host-by-address (address)
+  (with-mapped-conditions ()
+    (let ((hostent (posix:resolve-host-ipaddr (host-to-hostname address))))
+      (posix:hostent-name hostent))))
+
+(defun get-hosts-by-name (name)
+  (with-mapped-conditions ()
+    (let ((hostent (posix:resolve-host-ipaddr name)))
+      (mapcar #'host-to-vector-quad
+              (posix:hostent-addr-list hostent)))))
+
+;; Format: ((UNIX Windows) . CONDITION)
+(defparameter +clisp-error-map+
+  #-win32
+  `((:EADDRINUSE . address-in-use-error)
+    (:EADDRNOTAVAIL . address-not-available-error)
+    (:EBADF . bad-file-descriptor-error)
+    (:ECONNREFUSED  . connection-refused-error)
+    (:ECONNRESET . connection-reset-error)
+    (:ECONNABORTED . connection-aborted-error)
+    (:EINVAL . invalid-argument-error)
+    (:ENOBUFS . no-buffers-error)
+    (:ENOMEM . out-of-memory-error)
+    (:ENOTSUP . operation-not-supported-error)
+    (:EPERM . operation-not-permitted-error)
+    (:EPROTONOSUPPORT . protocol-not-supported-error)
+    (:ESOCKTNOSUPPORT . socket-type-not-supported-error)
+    (:ENETUNREACH . network-unreachable-error)
+    (:ENETDOWN . network-down-error)
+    (:ENETRESET . network-reset-error)
+    (:ESHUTDOWN . already-shutdown-error)
+    (:ETIMEDOUT . timeout-error)
+    (:EHOSTDOWN . host-down-error)
+    (:EHOSTUNREACH . host-unreachable-error))
+  #+win32
+  `((:WSAEADDRINUSE . address-in-use-error)
+    (:WSAEADDRNOTAVAIL . address-not-available-error)
+    (:WSAEBADF . bad-file-descriptor-error)
+    (:WSAECONNREFUSED  . connection-refused-error)
+    (:WSAECONNRESET . connection-reset-error)
+    (:WSAECONNABORTED . connection-aborted-error)
+    (:WSAEINVAL . invalid-argument-error)
+    (:WSAENOBUFS . no-buffers-error)
+    (:WSAENOMEM . out-of-memory-error)
+    (:WSAENOTSUP . operation-not-supported-error)
+    (:WSAEPERM . operation-not-permitted-error)
+    (:WSAEPROTONOSUPPORT . protocol-not-supported-error)
+    (:WSAESOCKTNOSUPPORT . socket-type-not-supported-error)
+    (:WSAENETUNREACH . network-unreachable-error)
+    (:WSAENETDOWN . network-down-error)
+    (:WSAENETRESET . network-reset-error)
+    (:WSAESHUTDOWN . already-shutdown-error)
+    (:WSAETIMEDOUT . timeout-error)
+    (:WSAEHOSTDOWN . host-down-error)
+    (:WSAEHOSTUNREACH . host-unreachable-error)))
+
+(defun handle-condition (condition &optional (socket nil))
+  "Dispatch correct usocket condition."
+  (let (error-keyword error-string)
+    (typecase condition
+      (ext:os-error
+       (let ((errno (car (simple-condition-format-arguments condition))))
+        #+ffi
+        (setq error-keyword (os:errno errno)
+              error-string (os:strerror errno))))
+      (simple-error
+       (let ((keyword
+             (car (simple-condition-format-arguments condition))))
+        (setq error-keyword keyword)
+        #+ffi
+        (setq error-string (os:strerror keyword))))
+      (error (error 'unknown-error :real-error condition))
+      (condition (signal 'unknown-condition :real-condition condition)))
+    (when error-keyword
+      (let ((usocket-error
+            (cdr (assoc error-keyword +clisp-error-map+ :test #'eq))))
+       (if usocket-error
+           (if (subtypep usocket-error 'error)
+               (error usocket-error :socket socket)
+               (signal usocket-error :socket socket))
+           (error "Unknown OS error: ~A (~A)" error-string error-keyword))))))
+
+(defun socket-connect (host port &key (protocol :stream) (element-type 'character)
+                       timeout deadline (nodelay t nodelay-specified)
+                       local-host local-port)
+  (declare (ignorable timeout local-host local-port))
+  (when deadline (unsupported 'deadline 'socket-connect))
+  (when (and nodelay-specified 
+             (not (eq nodelay :if-supported)))
+    (unsupported 'nodelay 'socket-connect))
+  (case protocol
+    (:stream
+     (let ((socket)
+          (hostname (host-to-hostname host)))
+       (with-mapped-conditions (socket)
+        (setf socket
+              (if timeout
+                  (socket:socket-connect port hostname
+                                         :element-type element-type
+                                         :buffered t
+                                         :timeout timeout)
+                  (socket:socket-connect port hostname
+                                         :element-type element-type
+                                         :buffered t))))
+       (make-stream-socket :socket socket
+                          :stream socket))) ;; the socket is a stream too
+    (:datagram
+     #+(or rawsock ffi)
+     (socket-create-datagram (or local-port *auto-port*)
+                            :local-host (or local-host *wildcard-host*)
+                            :remote-host (and host (host-to-vector-quad host))
+                            :remote-port port)
+     #-(or rawsock ffi)
+     (unsupported '(protocol :datagram) 'socket-connect))))
+
+(defun socket-listen (host port
+                           &key reuseaddress
+                           (reuse-address nil reuse-address-supplied-p)
+                           (backlog 5)
+                           (element-type 'character))
+  ;; clisp 2.39 sets SO_REUSEADDRESS to 1 by default; no need to
+  ;; to explicitly turn it on; unfortunately, there's no way to turn it off...
+  (declare (ignore reuseaddress reuse-address reuse-address-supplied-p))
+  (let ((sock (apply #'socket:socket-server
+                     (append (list port
+                                   :backlog backlog)
+                             (when (ip/= host *wildcard-host*)
+                               (list :interface host))))))
+    (with-mapped-conditions ()
+        (make-stream-server-socket sock :element-type element-type))))
+
+(defmethod socket-accept ((socket stream-server-usocket) &key element-type)
+  (let ((stream
+         (with-mapped-conditions (socket)
+           (socket:socket-accept (socket socket)
+                                 :element-type (or element-type
+                                                   (element-type socket))))))
+    (make-stream-socket :socket stream
+                        :stream stream)))
+
+;; Only one close method required:
+;; sockets and their associated streams
+;; are the same object
+(defmethod socket-close ((usocket usocket))
+  "Close socket."
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+    (close (socket usocket))))
+
+(defmethod socket-close ((usocket stream-server-usocket))
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (socket:socket-server-close (socket usocket)))
+
+(defmethod socket-shutdown ((usocket stream-usocket) direction)
+  (with-mapped-conditions (usocket)
+    (socket:socket-stream-shutdown (socket usocket) direction)))
+
+(defmethod get-local-name ((usocket stream-usocket))
+  (multiple-value-bind
+      (address port)
+      (socket:socket-stream-local (socket usocket) t)
+    (values (dotted-quad-to-vector-quad address) port)))
+
+(defmethod get-local-name ((usocket stream-server-usocket))
+  (values (get-local-address usocket)
+          (get-local-port usocket)))
+
+(defmethod get-peer-name ((usocket stream-usocket))
+  (multiple-value-bind
+      (address port)
+      (socket:socket-stream-peer (socket usocket) t)
+    (values (dotted-quad-to-vector-quad address) port)))
+
+(defmethod get-local-address ((usocket usocket))
+  (nth-value 0 (get-local-name usocket)))
+
+(defmethod get-local-address ((usocket stream-server-usocket))
+  (dotted-quad-to-vector-quad
+   (socket:socket-server-host (socket usocket))))
+
+(defmethod get-peer-address ((usocket usocket))
+  (nth-value 0 (get-peer-name usocket)))
+
+(defmethod get-local-port ((usocket usocket))
+  (nth-value 1 (get-local-name usocket)))
+
+(defmethod get-local-port ((usocket stream-server-usocket))
+  (socket:socket-server-port (socket usocket)))
+
+(defmethod get-peer-port ((usocket usocket))
+  (nth-value 1 (get-peer-name usocket)))
+
+(defun %setup-wait-list (wait-list)
+  (declare (ignore wait-list)))
+
+(defun %add-waiter (wait-list waiter)
+  (push (cons (socket waiter) NIL) (wait-list-%wait wait-list)))
+
+(defun %remove-waiter (wait-list waiter)
+  (setf (wait-list-%wait wait-list)
+        (remove (socket waiter) (wait-list-%wait wait-list) :key #'car)))
+
+(defmethod wait-for-input-internal (wait-list &key timeout)
+  (with-mapped-conditions ()
+    (multiple-value-bind
+        (secs musecs)
+        (split-timeout (or timeout 1))
+      (dolist (x (wait-list-%wait wait-list))
+        (setf (cdr x) :INPUT))
+      (let* ((request-list (wait-list-%wait wait-list))
+             (status-list (if timeout
+                              (socket:socket-status request-list secs musecs)
+                            (socket:socket-status request-list)))
+             (sockets (wait-list-waiters wait-list)))
+        (do* ((x (pop sockets) (pop sockets))
+              (y (cdr (pop status-list)) (cdr (pop status-list))))
+             ((null x))
+          (when (member y '(T :INPUT))
+            (setf (state x) :READ)))
+        wait-list))))
+
+;;;
+;;; UDP/Datagram sockets (RAWSOCK version)
+;;;
+
+#+rawsock
+(progn
+  (defun make-sockaddr_in ()
+    (make-array 16 :element-type '(unsigned-byte 8) :initial-element 0))
+
+  (declaim (inline fill-sockaddr_in))
+  (defun fill-sockaddr_in (sockaddr_in ip port)
+    (port-to-octet-buffer port sockaddr_in)
+    (ip-to-octet-buffer ip sockaddr_in :start 2)
+    sockaddr_in)
+
+  (defun socket-create-datagram (local-port
+                                 &key (local-host *wildcard-host*)
+                                      remote-host
+                                      remote-port)
+    (let ((sock (rawsock:socket :inet :dgram 0))
+          (lsock_addr (fill-sockaddr_in (make-sockaddr_in)
+                                        local-host local-port))
+          (rsock_addr (when remote-host
+                        (fill-sockaddr_in (make-sockaddr_in)
+                                          remote-host (or remote-port
+                                                          local-port)))))
+      (rawsock:bind sock (rawsock:make-sockaddr :inet lsock_addr))
+      (when rsock_addr
+        (rawsock:connect sock (rawsock:make-sockaddr :inet rsock_addr)))
+      (make-datagram-socket sock :connected-p (if rsock_addr t nil))))
+
+  (defmethod socket-receive ((socket datagram-usocket) buffer length &key)
+    "Returns the buffer, the number of octets copied into the buffer (received)
+and the address of the sender as values."
+    (let* ((sock (socket socket))
+           (sockaddr (rawsock:make-sockaddr :inet))
+           (real-length (or length +max-datagram-packet-size+))
+           (real-buffer (or buffer
+                            (make-array real-length
+                                        :element-type '(unsigned-byte 8)))))
+      (let ((rv (rawsock:recvfrom sock real-buffer sockaddr
+                                 :start 0 :end real-length))
+            (host 0) (port 0))
+        (unless (connected-p socket)
+          (let ((data (rawsock:sockaddr-data sockaddr)))
+            (setq host (ip-from-octet-buffer data :start 4)
+                  port (port-from-octet-buffer data :start 2))))
+        (values (if buffer real-buffer (subseq real-buffer 0 rv))
+                rv
+                host
+                port))))
+
+  (defmethod socket-send ((socket datagram-usocket) buffer size &key host port (offset 0))
+    "Returns the number of octets sent."
+    (let* ((sock (socket socket))
+           (sockaddr (when (and host port)
+                       (rawsock:make-sockaddr :inet
+                                              (fill-sockaddr_in
+                                               (make-sockaddr_in)
+                                               (host-byte-order host)
+                                               port))))
+           (real-size (min size +max-datagram-packet-size+))
+           (real-buffer (if (typep buffer '(simple-array (unsigned-byte 8) (*)))
+                            buffer
+                          (make-array real-size
+                                      :element-type '(unsigned-byte 8)
+                                      :initial-contents (subseq buffer 0 real-size))))
+           (rv (if (and host port)
+                   (rawsock:sendto sock real-buffer sockaddr
+                                   :start offset
+                                   :end (+ offset real-size))
+                   (rawsock:send sock real-buffer
+                                 :start offset
+                                 :end (+ offset real-size)))))
+      rv))
+
+  (defmethod socket-close ((usocket datagram-usocket))
+    (when (wait-list usocket)
+       (remove-waiter (wait-list usocket) usocket))
+    (rawsock:sock-close (socket usocket)))
+
+  (declaim (inline get-socket-name))
+  (defun get-socket-name (socket function)
+    (let ((sockaddr (rawsock:make-sockaddr :inet (make-sockaddr_in))))
+      (funcall function socket sockaddr)
+      (let ((data (rawsock:sockaddr-data sockaddr)))
+        (values (hbo-to-vector-quad (ip-from-octet-buffer data :start 2))
+                (port-from-octet-buffer data :start 0)))))
+
+  (defmethod get-local-name ((usocket datagram-usocket))
+    (get-socket-name (socket usocket) 'rawsock:getsockname))
+
+  (defmethod get-peer-name ((usocket datagram-usocket))
+    (get-socket-name (socket usocket) 'rawsock:getpeername))
+
+) ; progn
+
+;;;
+;;; UDP/Datagram sockets (FFI version)
+;;;
+
+#+(and ffi (not rawsock))
+(progn
+  ;; C primitive types
+  (ffi:def-c-type socklen_t ffi:uint32)
+
+  ;; C structures
+  (ffi:def-c-struct sockaddr
+    #+macos (sa_len ffi:uint8)
+    (sa_family  #-macos ffi:ushort
+               #+macos ffi:uint8)
+    (sa_data    (ffi:c-array ffi:char 14)))
+
+  (ffi:def-c-struct sockaddr_in
+    #+macos (sin_len ffi:uint8)
+    (sin_family #-macos ffi:short
+               #+macos ffi:uint8)
+    (sin_port   #-macos ffi:ushort
+               #+macos ffi:uint16)
+    (sin_addr   ffi:uint32)
+    (sin_zero   (ffi:c-array ffi:char 8)))
+
+  (ffi:def-c-struct timeval
+    (tv_sec     ffi:long)
+    (tv_usec    ffi:long))
+
+  ;; foreign functions
+  (ffi:def-call-out %sendto (:name "sendto")
+    (:arguments (socket ffi:int)
+               (buffer ffi:c-pointer)
+               (length ffi:int)
+               (flags ffi:int)
+               (address (ffi:c-ptr sockaddr))
+               (address-len ffi:int))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %send (:name "send")
+    (:arguments (socket ffi:int)
+               (buffer ffi:c-pointer)
+               (length ffi:int)
+               (flags ffi:int))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %recvfrom (:name "recvfrom")
+    (:arguments (socket ffi:int)
+               (buffer ffi:c-pointer)
+               (length ffi:int)
+               (flags ffi:int)
+               (address (ffi:c-ptr sockaddr) :in-out)
+               (address-len (ffi:c-ptr ffi:int) :in-out))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %socket (:name "socket")
+    (:arguments (family ffi:int)
+               (type ffi:int)
+               (protocol ffi:int))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %connect (:name "connect")
+    (:arguments (socket ffi:int)
+               (address (ffi:c-ptr sockaddr) :in)
+               (address_len socklen_t))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %bind (:name "bind")
+    (:arguments (socket ffi:int)
+               (address (ffi:c-ptr sockaddr) :in)
+               (address_len socklen_t))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %close (:name #-win32 "close" #+win32 "closesocket")
+    (:arguments (socket ffi:int))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %getsockopt (:name "getsockopt")
+    (:arguments (sockfd ffi:int)
+               (level ffi:int)
+               (optname ffi:int)
+               (optval ffi:c-pointer)
+               (optlen (ffi:c-ptr socklen_t) :out))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %setsockopt (:name "setsockopt")
+    (:arguments (sockfd ffi:int)
+               (level ffi:int)
+               (optname ffi:int)
+               (optval ffi:c-pointer)
+               (optlen socklen_t))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %htonl (:name "htonl")
+    (:arguments (hostlong ffi:uint32))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:uint32))
+
+  (ffi:def-call-out %htons (:name "htons")
+    (:arguments (hostshort ffi:uint16))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:uint16))
+
+  (ffi:def-call-out %ntohl (:name "ntohl")
+    (:arguments (netlong ffi:uint32))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:uint32))
+
+  (ffi:def-call-out %ntohs (:name "ntohs")
+    (:arguments (netshort ffi:uint16))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:uint16))
+
+  (ffi:def-call-out %getsockname (:name "getsockname")
+    (:arguments (sockfd ffi:int)
+               (localaddr (ffi:c-ptr sockaddr) :in-out)
+               (addrlen (ffi:c-ptr socklen_t) :in-out))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  (ffi:def-call-out %getpeername (:name "getpeername")
+    (:arguments (sockfd ffi:int)
+               (peeraddr (ffi:c-ptr sockaddr) :in-out)
+               (addrlen (ffi:c-ptr socklen_t) :in-out))
+    #+win32 (:library "WS2_32")
+    #-win32 (:library :default)
+    (:language #-win32 :stdc
+              #+win32 :stdc-stdcall)
+    (:return-type ffi:int))
+
+  ;; socket constants
+  (defconstant +socket-af-inet+ 2)
+  (defconstant +socket-sock-dgram+ 2)
+  (defconstant +socket-ip-proto-udp+ 17)
+
+  (defconstant +sockopt-so-rcvtimeo+ #-linux #x1006 #+linux 20 "Socket receive timeout")
+
+  (defparameter *length-of-sockaddr_in* (ffi:sizeof 'sockaddr_in))
+
+  (declaim (inline fill-sockaddr_in))
+  (defun fill-sockaddr_in (sockaddr host port)
+    (let ((hbo (host-to-hbo host)))
+      (ffi:with-c-place (place sockaddr)
+       #+macos
+       (setf (ffi:slot place 'sin_len) *length-of-sockaddr_in*)
+       (setf (ffi:slot place 'sin_family) +socket-af-inet+
+             (ffi:slot place 'sin_port) (%htons port)
+             (ffi:slot place 'sin_addr) (%htonl hbo)))
+      sockaddr))
+
+  (defun socket-create-datagram (local-port
+                                &key (local-host *wildcard-host*)
+                                     remote-host
+                                     remote-port)
+    (let ((sock (%socket +socket-af-inet+ +socket-sock-dgram+ +socket-ip-proto-udp+))
+         (lsock_addr (fill-sockaddr_in (ffi:allocate-shallow 'sockaddr_in)
+                                       local-host local-port))
+         (rsock_addr (when remote-host
+                       (fill-sockaddr_in (ffi:allocate-shallow 'sockaddr_in)
+                                         remote-host (or remote-port local-port)))))
+      (unless (plusp sock)
+       (error "SOCKET-CREATE-DATAGRAM ERROR (socket): ~A" (os:errno)))
+      (unwind-protect
+          (let ((rv (%bind sock (ffi:cast (ffi:foreign-value lsock_addr) 'sockaddr)
+                           *length-of-sockaddr_in*)))
+            (unless (zerop rv)
+              (error "SOCKET-CREATE-DATAGRAM ERROR (bind): ~A" (os:errno)))
+            (when rsock_addr
+              (let ((rv (%connect sock
+                                  (ffi:cast (ffi:foreign-value rsock_addr) 'sockaddr)
+                                  *length-of-sockaddr_in*)))
+                (unless (zerop rv)
+                  (error "SOCKET-CREATE-DATAGRAM ERROR (connect): ~A" (os:errno))))))
+       (ffi:foreign-free lsock_addr)
+       (when remote-host
+         (ffi:foreign-free rsock_addr)))
+      (make-datagram-socket sock :connected-p (if rsock_addr t nil))))
+
+  (defun finalize-datagram-usocket (object)
+    (when (datagram-usocket-p object)
+      (socket-close object)))
+
+  (defmethod initialize-instance :after ((usocket datagram-usocket) &key)
+    (setf (slot-value usocket 'recv-buffer)
+         (ffi:allocate-shallow 'ffi:uint8 :count +max-datagram-packet-size+))
+    ;; finalize the object
+    (ext:finalize usocket 'finalize-datagram-usocket))
+
+  (defmethod socket-close ((usocket datagram-usocket))
+    (when (wait-list usocket)
+      (remove-waiter (wait-list usocket) usocket))
+    (with-slots (recv-buffer socket) usocket
+      (ffi:foreign-free recv-buffer)
+      (zerop (%close socket))))
+
+  (defmethod socket-receive ((usocket datagram-usocket) buffer length &key)
+    (let ((remote-address (ffi:allocate-shallow 'sockaddr_in))
+         (remote-address-length (ffi:allocate-shallow 'ffi:int))
+         nbytes (host 0) (port 0))
+      (setf (ffi:foreign-value remote-address-length)
+           *length-of-sockaddr_in*)
+      (unwind-protect
+          (multiple-value-bind (n address address-length)
+              (%recvfrom (socket usocket)
+                         (ffi:foreign-address (slot-value usocket 'recv-buffer))
+                         +max-datagram-packet-size+
+                         0 ; flags
+                         (ffi:cast (ffi:foreign-value remote-address) 'sockaddr)
+                         (ffi:foreign-value remote-address-length))
+            (when (minusp n)
+              (error "SOCKET-RECEIVE ERROR: ~A" (os:errno)))
+            (setq nbytes n)
+            (when (= address-length *length-of-sockaddr_in*)
+              (let ((data (sockaddr-sa_data address)))
+                (setq host (ip-from-octet-buffer data :start 2)
+                      port (port-from-octet-buffer data))))
+            (cond ((plusp n)
+                   (let ((return-buffer (ffi:foreign-value (slot-value usocket 'recv-buffer))))
+                     (if buffer ; replace exist buffer of create new return buffer
+                         (let ((end-1 (min (or length (length buffer)) +max-datagram-packet-size+))
+                               (end-2 (min n +max-datagram-packet-size+)))
+                           (replace buffer return-buffer :end1 end-1 :end2 end-2))
+                         (setq buffer (subseq return-buffer 0 (min n +max-datagram-packet-size+))))))
+                  ((zerop n))))
+       (ffi:foreign-free remote-address)
+       (ffi:foreign-free remote-address-length))
+      (values buffer nbytes host port)))
+
+  ;; implementation note: different from socket-receive, we know how many bytes we want to send everytime,
+  ;; so, a send buffer will not needed, and if there is a buffer, it's hard to fill its content like those
+  ;; in LispWorks. So, we allocate new foreign buffer for holding data (unknown sequence subtype) every time.
+  ;; 
+  ;; I don't know if anyone is watching my coding work, but I think this design is reasonable for CLISP.
+  (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
+    (declare (type sequence buffer)
+            (type (integer 0 *) size offset))
+    (let ((remote-address
+          (when (and host port)
+            (fill-sockaddr_in (ffi:allocate-shallow 'sockaddr_in) host port)))
+         (send-buffer
+          (ffi:allocate-deep 'ffi:uint8
+                             (if (zerop offset)
+                                 buffer
+                                 (subseq buffer offset (+ offset size)))
+                             :count size :read-only t))
+         (real-size (min size +max-datagram-packet-size+))
+         (nbytes 0))
+      (unwind-protect
+          (let ((n (if remote-address
+                       (%sendto (socket usocket)
+                                (ffi:foreign-address send-buffer)
+                                real-size
+                                0 ; flags
+                                (ffi:cast (ffi:foreign-value remote-address) 'sockaddr)
+                                *length-of-sockaddr_in*)
+                       (%send (socket usocket)
+                              (ffi:foreign-address send-buffer)
+                              real-size
+                              0))))
+            (cond ((plusp n)
+                   (setq nbytes n))
+                  ((zerop n)
+                   (setq nbytes n))
+                  (t (error "SOCKET-SEND ERROR: ~A" (os:errno)))))
+       (ffi:foreign-free send-buffer)
+       (when remote-address
+         (ffi:foreign-free remote-address))
+       nbytes)))
+
+  (declaim (inline get-socket-name))
+  (defun get-socket-name (socket function)
+    (let ((address (ffi:allocate-shallow 'sockaddr_in))
+         (address-length (ffi:allocate-shallow 'ffi:int))
+         (host 0) (port 0))
+      (setf (ffi:foreign-value address-length) *length-of-sockaddr_in*)
+      (unwind-protect
+          (multiple-value-bind (rv return-address return-address-length)
+              (funcall function socket
+                       (ffi:cast (ffi:foreign-value address) 'sockaddr)
+                       (ffi:foreign-value address-length))
+            (declare (ignore return-address-length))
+            (if (zerop rv)
+                (let ((data (sockaddr-sa_data return-address)))
+                  (setq host (ip-from-octet-buffer data :start 2)
+                        port (port-from-octet-buffer data)))
+                (error "GET-SOCKET-NAME ERROR: ~A" (os:errno))))
+       (ffi:foreign-free address)
+       (ffi:foreign-free address-length))
+      (values (hbo-to-vector-quad host) port)))
+
+  (defmethod get-local-name ((usocket datagram-usocket))
+    (get-socket-name (socket usocket) '%getsockname))
+
+  (defmethod get-peer-name ((usocket datagram-usocket))
+    (get-socket-name (socket usocket) '%getpeername))
+
+) ; progn
diff --git a/deps/usocket/backend/clozure.lisp b/deps/usocket/backend/clozure.lisp
new file mode 100644 (file)
index 0000000..04d6840
--- /dev/null
@@ -0,0 +1,73 @@
+;;;; See LICENSE for licensing information.
+
+;;;; Functions for CCL 1.11 (IPv6) only, see openmcl.lisp for rest of functions.
+
+(in-package :usocket)
+
+#+ipv6
+(defun socket-connect (host port &key (protocol :stream) element-type
+                                   timeout deadline nodelay
+                                   local-host local-port)
+  (when (eq nodelay :if-supported)
+    (setf nodelay t))
+  (with-mapped-conditions ()
+    (let* ((remote (when (and host port)
+                    (openmcl-socket:resolve-address :host (host-to-hostname host)
+                                                    :port port
+                                                    :socket-type protocol)))
+          (local  (when (and local-host local-port)
+                    (openmcl-socket:resolve-address :host (host-to-hostname local-host)
+                                                    :port local-port
+                                                    :socket-type protocol)))
+          (mcl-sock (apply #'openmcl-socket:make-socket
+                           `(:type ,protocol
+                             ,@(when (or remote local)
+                                 `(:address-family ,(openmcl-socket:socket-address-family (or remote local))))
+                             ,@(when remote
+                                 `(:remote-address ,remote))
+                             ,@(when local
+                                 `(:local-address ,local))
+                             :format ,(to-format element-type protocol)
+                             :external-format ,ccl:*default-external-format*
+                             :deadline ,deadline
+                             :nodelay ,nodelay
+                             :connect-timeout ,timeout
+                             :input-timeout ,timeout))))
+      (ecase protocol
+        (:stream
+         (make-stream-socket :stream mcl-sock :socket mcl-sock))
+        (:datagram
+         (make-datagram-socket mcl-sock :connected-p (and remote t)))))))
+
+#+ipv6
+(defun socket-listen (host port
+                      &key
+                        (reuse-address nil reuse-address-supplied-p)
+                        (reuseaddress (when reuse-address-supplied-p reuse-address))
+                        (backlog 5)
+                        (element-type 'character))
+  (let ((local-address (openmcl-socket:resolve-address :host (host-to-hostname host)
+                                                      :port port :connect :passive)))
+    (with-mapped-conditions ()
+      (make-stream-server-socket
+        (openmcl-socket:make-socket :connect :passive
+                                   :address-family (openmcl-socket:socket-address-family local-address)
+                                   :local-address local-address
+                                   :reuse-address reuseaddress
+                                   :backlog backlog
+                                   :format (to-format element-type :stream))
+       :element-type element-type))))
+
+#+ipv6
+(defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
+  (let* ((ccl-socket (socket usocket))
+        (socket-keys (ccl::socket-keys ccl-socket)))
+    (with-mapped-conditions (usocket)
+      (if (and host port)
+         (openmcl-socket:send-to ccl-socket buffer size
+                                 :remote-host (host-to-hostname host)
+                                 :remote-port port
+                                 :offset offset)
+         (openmcl-socket:send-to ccl-socket buffer size
+                                 :remote-address (getf socket-keys :remote-address)
+                                 :offset offset)))))
diff --git a/deps/usocket/backend/cmucl.lisp b/deps/usocket/backend/cmucl.lisp
new file mode 100644 (file)
index 0000000..69c94a1
--- /dev/null
@@ -0,0 +1,298 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+#+win32
+(defun remap-for-win32 (z)
+  (mapcar #'(lambda (x)
+              (cons (mapcar #'(lambda (y)
+                                (+ 10000 y))
+                            (car x))
+                    (cdr x)))
+          z))
+
+(defparameter +cmucl-error-map+
+  #+win32
+  (append (remap-for-win32 +unix-errno-condition-map+)
+          (remap-for-win32 +unix-errno-error-map+))
+  #-win32
+  (append +unix-errno-condition-map+
+          +unix-errno-error-map+))
+
+(defun cmucl-map-socket-error (err &key condition socket)
+  (let ((usock-err
+         (cdr (assoc err +cmucl-error-map+ :test #'member))))
+    (if usock-err
+        (if (subtypep usock-err 'error)
+            (error usock-err :socket socket)
+          (signal usock-err :socket socket))
+      (error 'unknown-error
+             :socket socket
+             :real-error condition))))
+
+;; CMUCL error handling is brain-dead: it doesn't preserve any
+;; information other than the OS error string from which the
+;; error can be determined. The OS error string isn't good enough
+;; given that it may have been localized (l10n).
+;;
+;; The above applies to versions pre 19b; 19d and newer are expected to
+;; contain even better error reporting.
+;;
+;;
+;; Just catch the errors and encapsulate them in an unknown-error
+(defun handle-condition (condition &optional (socket nil))
+  "Dispatch correct usocket condition."
+  (typecase condition
+    (ext::socket-error (cmucl-map-socket-error (ext::socket-errno condition)
+                                               :socket socket
+                                               :condition condition))))
+
+(defun socket-connect (host port &key (protocol :stream) (element-type 'character)
+                       timeout deadline (nodelay t nodelay-specified)
+                      (local-host nil local-host-p)
+                      (local-port nil local-port-p)
+                      &aux
+                      (local-bind-p (fboundp 'ext::bind-inet-socket)))
+  (when timeout (unsupported 'timeout 'socket-connect))
+  (when deadline (unsupported 'deadline 'socket-connect))
+  (when (and nodelay-specified 
+             (not (eq nodelay :if-supported)))
+    (unsupported 'nodelay 'socket-connect))
+  (when (and local-host-p (not local-bind-p))
+     (unsupported 'local-host 'socket-connect :minimum "Snapshot 2008-08 (19E)"))
+  (when (and local-port-p (not local-bind-p))
+     (unsupported 'local-port 'socket-connect :minimum "Snapshot 2008-08 (19E)"))
+
+  (let ((socket))
+    (ecase protocol
+      (:stream
+       (setf socket
+            (let ((args (list (host-to-hbo host) port protocol)))
+              (when (and local-bind-p (or local-host-p local-port-p))
+                (nconc args (list :local-host (when local-host
+                                                (host-to-hbo local-host))
+                                  :local-port local-port)))
+              (with-mapped-conditions (socket)
+                (apply #'ext:connect-to-inet-socket args))))
+       (if socket
+          (let* ((stream (sys:make-fd-stream socket :input t :output t
+                                             :element-type element-type
+                                             :buffering :full))
+                 ;;###FIXME the above line probably needs an :external-format
+                 (usocket (make-stream-socket :socket socket
+                                              :stream stream)))
+            usocket)
+          (let ((err (unix:unix-errno)))
+            (when err (cmucl-map-socket-error err)))))
+      (:datagram
+       (setf socket
+            (if (and host port)
+                (let ((args (list (host-to-hbo host) port protocol)))
+                  (when (and local-bind-p (or local-host-p local-port-p))
+                    (nconc args (list :local-host (when local-host
+                                                    (host-to-hbo local-host))
+                                      :local-port local-port)))
+                  (with-mapped-conditions (socket)
+                    (apply #'ext:connect-to-inet-socket args)))
+                (if (or local-host-p local-port-p)
+                    (with-mapped-conditions (socket)
+                      (apply #'ext:create-inet-listener
+                             (nconc (list (or local-port 0) protocol)
+                                    (when (and local-host-p
+                                               (ip/= local-host *wildcard-host*))
+                                      (list :host (host-to-hbo local-host))))))
+                    (with-mapped-conditions (socket)
+                      (ext:create-inet-socket protocol)))))
+       (if socket
+          (let ((usocket (make-datagram-socket socket :connected-p (and host port t))))
+            (ext:finalize usocket #'(lambda () (when (%open-p usocket)
+                                                 (ext:close-socket socket))))
+            usocket)
+          (let ((err (unix:unix-errno)))
+            (when err (cmucl-map-socket-error err))))))))
+
+(defun socket-listen (host port
+                           &key reuseaddress
+                           (reuse-address nil reuse-address-supplied-p)
+                           (backlog 5)
+                           (element-type 'character))
+ (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
+        (server-sock
+         (with-mapped-conditions ()
+           (apply #'ext:create-inet-listener
+                  (nconc  (list port :stream
+                                :backlog backlog
+                                :reuse-address reuseaddress)
+                          (when (ip/= host *wildcard-host*)
+                            (list :host
+                                  (host-to-hbo host))))))))
+   (make-stream-server-socket server-sock :element-type element-type)))
+
+(defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
+  (with-mapped-conditions (usocket)
+    (let* ((sock (ext:accept-tcp-connection (socket usocket)))
+           (stream (sys:make-fd-stream sock :input t :output t
+                                       :element-type (or element-type
+                                                         (element-type usocket))
+                                       :buffering :full)))
+      (make-stream-socket :socket sock :stream stream))))
+
+;; Sockets and socket streams are represented
+;; by different objects. Be sure to close the
+;; socket stream when closing a stream socket.
+(defmethod socket-close ((usocket stream-usocket))
+  "Close socket."
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+    (close (socket-stream usocket))))
+
+(defmethod socket-close ((usocket usocket))
+  "Close socket."
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+    (ext:close-socket (socket usocket))))
+
+(defmethod socket-close :after ((socket datagram-usocket))
+  (setf (%open-p socket) nil))
+
+#+unicode
+(defun %unix-send (fd buffer length flags)
+  (alien:alien-funcall
+   (alien:extern-alien "send"
+                      (function c-call:int
+                                c-call:int
+                                system:system-area-pointer
+                                c-call:int
+                                c-call:int))
+   fd
+   (system:vector-sap buffer)
+   length
+   flags))
+
+(defmethod socket-shutdown ((usocket usocket) direction)
+  (with-mapped-conditions (usocket)
+    (ext:inet-shutdown (socket usocket) (ecase direction
+                                          (:input ext:shut-rd)
+                                          (:output ext:shut-wr)))))
+
+(defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0)
+                       &aux (real-buffer (if (zerop offset)
+                                             buffer
+                                             (subseq buffer offset (+ offset size)))))
+  (with-mapped-conditions (usocket)
+    (if (and host port)
+       (ext:inet-sendto (socket usocket) real-buffer size (host-to-hbo host) port)
+       #-unicode
+       (unix:unix-send (socket usocket) real-buffer size 0)
+       #+unicode
+       (%unix-send (socket usocket) real-buffer size 0))))
+
+(defmethod socket-receive ((usocket datagram-usocket) buffer length &key)
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+                  (integer 0)                          ; size
+                  (unsigned-byte 32)                   ; host
+                  (unsigned-byte 16)))                 ; port
+  (let ((real-buffer (or buffer
+                         (make-array length :element-type '(unsigned-byte 8))))
+        (real-length (or length
+                         (length buffer))))
+    (multiple-value-bind (nbytes remote-host remote-port)
+        (with-mapped-conditions (usocket)
+          (ext:inet-recvfrom (socket usocket) real-buffer real-length))
+      (values real-buffer nbytes remote-host remote-port))))
+
+(defmethod get-local-name ((usocket usocket))
+  (multiple-value-bind
+      (address port)
+      (ext:get-socket-host-and-port (socket usocket))
+    (values (hbo-to-vector-quad address) port)))
+
+(defmethod get-peer-name ((usocket stream-usocket))
+  (multiple-value-bind
+      (address port)
+      (ext:get-peer-host-and-port (socket usocket))
+    (values (hbo-to-vector-quad address) port)))
+
+(defmethod get-local-address ((usocket usocket))
+  (nth-value 0 (get-local-name usocket)))
+
+(defmethod get-peer-address ((usocket stream-usocket))
+  (nth-value 0 (get-peer-name usocket)))
+
+(defmethod get-local-port ((usocket usocket))
+  (nth-value 1 (get-local-name usocket)))
+
+(defmethod get-peer-port ((usocket stream-usocket))
+  (nth-value 1 (get-peer-name usocket)))
+
+
+(defun lookup-host-entry (host)
+  (multiple-value-bind
+      (entry errno)
+      (ext:lookup-host-entry host)
+    (if entry
+        entry
+      ;;###The constants below work on *most* OSes, but are defined as the
+      ;; constants mentioned in C
+      (let ((exception
+             (second (assoc errno
+                            '((1 ns-host-not-found-error)     ;; HOST_NOT_FOUND
+                              (2 ns-no-recovery-error)        ;; NO_DATA
+                              (3 ns-no-recovery-error)        ;; NO_RECOVERY
+                              (4 ns-try-again-condition)))))) ;; TRY_AGAIN
+        (when exception
+          (error exception))))))
+
+
+(defun get-host-by-address (address)
+  (handler-case (ext:host-entry-name
+                 (lookup-host-entry (host-byte-order address)))
+    (condition (condition) (handle-condition condition))))
+
+(defun get-hosts-by-name (name)
+  (handler-case (mapcar #'hbo-to-vector-quad
+                        (ext:host-entry-addr-list
+                         (lookup-host-entry name)))
+    (condition (condition) (handle-condition condition))))
+
+(defun get-host-name ()
+  (unix:unix-gethostname))
+
+(defun %setup-wait-list (wait-list)
+  (declare (ignore wait-list)))
+
+(defun %add-waiter (wait-list waiter)
+  (push (socket waiter) (wait-list-%wait wait-list)))
+
+(defun %remove-waiter (wait-list waiter)
+  (setf (wait-list-%wait wait-list)
+        (remove (socket waiter) (wait-list-%wait wait-list))))
+
+(defun wait-for-input-internal (wait-list &key timeout)
+  (with-mapped-conditions ()
+    (alien:with-alien ((rfds (alien:struct unix:fd-set)))
+       (unix:fd-zero rfds)
+       (dolist (socket (wait-list-%wait wait-list))
+         (unix:fd-set socket rfds))
+       (multiple-value-bind
+           (secs musecs)
+           (split-timeout (or timeout 1))
+         (multiple-value-bind (count err)
+            (unix:unix-fast-select (1+ (reduce #'max
+                                               (wait-list-%wait wait-list)))
+                                   (alien:addr rfds) nil nil
+                                   (when timeout secs) musecs)
+          (declare (ignore err))
+           (if (<= 0 count)
+               ;; process the result...
+               (dolist (x (wait-list-waiters wait-list))
+                 (when (unix:fd-isset (socket x) rfds)
+                   (setf (state x) :READ)))
+              (progn
+                ;;###FIXME generate an error, except for EINTR
+                )))))))
diff --git a/deps/usocket/backend/ecl.lisp b/deps/usocket/backend/ecl.lisp
new file mode 100644 (file)
index 0000000..a4c80b1
--- /dev/null
@@ -0,0 +1,154 @@
+;;;; -*- Mode: Lisp -*-
+;;;; $Id$
+;;;; $URL$
+
+;;;; Foreign functions defined by ECL's DFFI, used for #+ecl-bytecmp only.
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+#+(and ecl-bytecmp windows)
+(eval-when (:load-toplevel :execute)
+  (ffi:load-foreign-library "ws2_32.dll" :module "ws2_32"))
+
+#+(and ecl-bytecmp windows)
+(progn
+  (ffi:def-function ("gethostname" c-gethostname)
+    ((name (* :unsigned-char))
+     (len :int))
+    :returning :int
+    :module "ws2_32")
+
+  (defun get-host-name ()
+    "Returns the hostname"
+    (ffi:with-foreign-object (name '(:array :unsigned-char 256))
+      (when (zerop (c-gethostname (ffi:char-array-to-pointer name) 256))
+        (ffi:convert-from-foreign-string name))))
+
+  (ffi:def-foreign-type ws-socket :unsigned-int)
+  (ffi:def-foreign-type ws-dword :unsigned-long)
+  (ffi:def-foreign-type ws-event :unsigned-int)
+
+  (ffi:def-struct wsa-network-events
+    (network-events :long)
+    (error-code (:array :int 10)))
+
+  (ffi:def-function ("WSACreateEvent" wsa-event-create)
+    ()
+    :returning ws-event
+    :module "ws2_32")
+
+  (ffi:def-function ("WSACloseEvent" c-wsa-event-close)
+    ((event-object ws-event))
+    :returning :int
+    :module "ws2_32")
+
+  (defun wsa-event-close (ws-event)
+    (not (zerop (c-wsa-event-close ws-event))))
+
+  (ffi:def-function ("WSAEnumNetworkEvents" wsa-enum-network-events)
+    ((socket ws-socket)
+     (event-object ws-event)
+     (network-events (* wsa-network-events)))
+    :returning :int
+    :module "ws2_32")
+
+  (ffi:def-function ("WSAEventSelect" wsa-event-select)
+    ((socket ws-socket)
+     (event-object ws-event)
+     (network-events :long))
+    :returning :int
+    :module "ws2_32")
+
+  (ffi:def-function ("WSAWaitForMultipleEvents" c-wsa-wait-for-multiple-events)
+    ((number-of-events ws-dword)
+     (events (* ws-event))
+     (wait-all-p :int)
+     (timeout ws-dword)
+     (alertable-p :int))
+    :returning ws-dword
+    :module "ws2_32")
+
+  (defun wsa-wait-for-multiple-events (number-of-events events wait-all-p timeout alertable-p)
+    (c-wsa-wait-for-multiple-events number-of-events
+                                    events
+                                    (if wait-all-p -1 0)
+                                    timeout
+                                    (if alertable-p -1 0)))
+
+  (ffi:def-function ("ioctlsocket" wsa-ioctlsocket)
+    ((socket ws-socket)
+     (cmd :long)
+     (argp (* :unsigned-long)))
+    :returning :int
+    :module "ws2_32")
+
+  (ffi:def-function ("WSAGetLastError" wsa-get-last-error)
+    ()
+    :returning :int
+    :module "ws2_32")
+
+  (defun maybe-wsa-error (rv &optional socket)
+    (unless (zerop rv)
+      (raise-usock-err (wsa-get-last-error) socket)))
+
+  (defun bytes-available-for-read (socket)
+    (ffi:with-foreign-object (int-ptr :unsigned-long)
+      (maybe-wsa-error (wsa-ioctlsocket (socket-handle socket) fionread int-ptr)
+                       socket)
+      (let ((int (ffi:deref-pointer int-ptr :unsigned-long)))
+        (prog1 int
+          (when (plusp int)
+            (setf (state socket) :read))))))
+
+  (defun map-network-events (func network-events)
+    (let ((event-map (ffi:get-slot-value network-events 'wsa-network-events 'network-events))
+          (error-array (ffi:get-slot-pointer network-events 'wsa-network-events 'error-code)))
+      (unless (zerop event-map)
+        (dotimes (i fd-max-events)
+          (unless (zerop (ldb (byte 1 i) event-map))
+            (funcall func (ffi:deref-array error-array '(:array :int 10) i)))))))
+
+  (defun update-ready-and-state-slots (sockets)
+    (dolist (socket sockets)
+      (if (%ready-p socket)
+          (progn
+            (setf (state socket) :READ))
+        (ffi:with-foreign-object (network-events 'wsa-network-events)
+          (let ((rv (wsa-enum-network-events (socket-handle socket) 0 network-events)))
+            (if (zerop rv)
+                (map-network-events
+                 #'(lambda (err-code)
+                     (if (zerop err-code)
+                         (progn
+                           (setf (state socket) :READ)
+                           (when (stream-server-usocket-p socket)
+                             (setf (%ready-p socket) t)))
+                       (raise-usock-err err-code socket)))
+                 network-events)
+              (maybe-wsa-error rv socket)))))))
+
+  (defun os-wait-list-%wait (wait-list)
+    (ffi:deref-pointer (wait-list-%wait wait-list) 'ws-event))
+
+  (defun (setf os-wait-list-%wait) (value wait-list)
+    (setf (ffi:deref-pointer (wait-list-%wait wait-list) 'ws-event) value))
+
+  (defun free-wait-list (wl)
+    (when (wait-list-p wl)
+      (unless (null (wait-list-%wait wl))
+        (wsa-event-close (os-wait-list-%wait wl))
+        (ffi:free-foreign-object (wait-list-%wait wl))
+        (setf (wait-list-%wait wl) nil))))
+
+  (defun %setup-wait-list (wait-list)
+    (setf (wait-list-%wait wait-list)
+          (ffi:allocate-foreign-object 'ws-event))
+    (setf (os-wait-list-%wait wait-list)
+          (wsa-event-create))
+    (ext:set-finalizer wait-list #'free-wait-list))
+
+  (defun os-socket-handle (usocket)
+    (socket-handle usocket))
+
+) ; #+(and ecl-bytecmp windows)
diff --git a/deps/usocket/backend/lispworks.lisp b/deps/usocket/backend/lispworks.lisp
new file mode 100644 (file)
index 0000000..8e8e21a
--- /dev/null
@@ -0,0 +1,838 @@
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (require "comm")
+
+  #+lispworks3
+  (error "LispWorks 3 is not supported by USOCKET any more."))
+
+;;; ---------------------------------------------------------------------------
+;;;  Warn if multiprocessing is not running on Lispworks
+
+(defun check-for-multiprocessing-started (&optional errorp)
+  (unless mp:*current-process*
+    (funcall (if errorp 'error 'warn)
+             "You must start multiprocessing on Lispworks by calling~
+              ~%~3t(~s)~
+              ~%for ~s function properly."
+             'mp:initialize-multiprocessing
+             'wait-for-input)))
+
+(eval-when (:load-toplevel :execute)
+  (check-for-multiprocessing-started))
+
+#+win32
+(eval-when (:load-toplevel :execute)
+  (fli:register-module "ws2_32"))
+
+(fli:define-foreign-function (get-host-name-internal "gethostname" :source)
+      ((return-string (:reference-return (:ef-mb-string :limit 257)))
+       (namelen :int))
+      :lambda-list (&aux (namelen 256) return-string)
+      :result-type :int
+      #+win32 :module
+      #+win32 "ws2_32")
+
+(defun get-host-name ()
+  (multiple-value-bind (return-code name)
+      (get-host-name-internal)
+    (when (zerop return-code)
+      name)))
+
+#+win32
+(defun remap-maybe-for-win32 (z)
+  (mapcar #'(lambda (x)
+              (cons (mapcar #'(lambda (y) (+ 10000 y)) (car x))
+                    (cdr x)))
+          z))
+
+(defparameter +lispworks-error-map+
+  #+win32
+  (append (remap-maybe-for-win32 +unix-errno-condition-map+)
+          (remap-maybe-for-win32 +unix-errno-error-map+))
+  #-win32
+  (append +unix-errno-condition-map+
+          +unix-errno-error-map+))
+
+(defun raise-usock-err (errno socket &optional condition)
+  (let ((usock-err
+         (cdr (assoc errno +lispworks-error-map+ :test #'member))))
+    (if usock-err
+        (if (subtypep usock-err 'error)
+            (error usock-err :socket socket)
+          (signal usock-err))
+      (error 'unknown-error
+             :socket socket
+             :real-error condition
+             :errno errno))))
+
+(defun handle-condition (condition &optional (socket nil))
+  "Dispatch correct usocket condition."
+  (typecase condition
+    (condition (let ((errno #-win32 (lw:errno-value)
+                            #+win32 (wsa-get-last-error)))
+                 (unless (zerop errno)
+                   (raise-usock-err errno socket condition))))))
+
+(defconstant *socket_sock_dgram* 2
+  "Connectionless, unreliable datagrams of fixed maximum length.")
+
+(defconstant *socket_ip_proto_udp* 17)
+
+(defconstant *sockopt_so_rcvtimeo*
+  #-linux #x1006
+  #+linux 20
+  "Socket receive timeout")
+
+(fli:define-c-struct timeval
+  (tv-sec :long)
+  (tv-usec :long))
+
+;;; ssize_t
+;;; recvfrom(int socket, void *restrict buffer, size_t length, int flags,
+;;;          struct sockaddr *restrict address, socklen_t *restrict address_len);
+(fli:define-foreign-function (%recvfrom "recvfrom" :source)
+    ((socket :int)
+     (buffer (:pointer (:unsigned :byte)))
+     (length :int)
+     (flags :int)
+     (address (:pointer (:struct comm::sockaddr)))
+     (address-len (:pointer :int)))
+  :result-type :int
+  #+win32 :module
+  #+win32 "ws2_32")
+
+;;; ssize_t
+;;; sendto(int socket, const void *buffer, size_t length, int flags,
+;;;        const struct sockaddr *dest_addr, socklen_t dest_len);
+(fli:define-foreign-function (%sendto "sendto" :source)
+    ((socket :int)
+     (buffer (:pointer (:unsigned :byte)))
+     (length :int)
+     (flags :int)
+     (address (:pointer (:struct comm::sockaddr)))
+     (address-len :int))
+  :result-type :int
+  #+win32 :module
+  #+win32 "ws2_32")
+
+#-win32
+(defun set-socket-receive-timeout (socket-fd seconds)
+  "Set socket option: RCVTIMEO, argument seconds can be a float number"
+  (declare (type integer socket-fd)
+           (type number seconds))
+  (multiple-value-bind (sec usec) (truncate seconds)
+    (fli:with-dynamic-foreign-objects ((timeout (:struct timeval)))
+      (fli:with-foreign-slots (tv-sec tv-usec) timeout
+        (setf tv-sec sec
+              tv-usec (truncate (* 1000000 usec)))
+        (if (zerop (comm::setsockopt socket-fd
+                               comm::*sockopt_sol_socket*
+                               *sockopt_so_rcvtimeo*
+                               (fli:copy-pointer timeout
+                                                 :type '(:pointer :void))
+                               (fli:size-of '(:struct timeval))))
+            seconds)))))
+
+#+win32
+(defun set-socket-receive-timeout (socket-fd seconds)
+  "Set socket option: RCVTIMEO, argument seconds can be a float number.
+   On win32, you must bind the socket before use this function."
+  (declare (type integer socket-fd)
+           (type number seconds))
+  (fli:with-dynamic-foreign-objects ((timeout :int))
+    (setf (fli:dereference timeout)
+          (truncate (* 1000 seconds)))
+    (if (zerop (comm::setsockopt socket-fd
+                           comm::*sockopt_sol_socket*
+                           *sockopt_so_rcvtimeo*
+                           (fli:copy-pointer timeout
+                                             :type '(:pointer :char))
+                           (fli:size-of :int)))
+        seconds)))
+
+#-win32
+(defun get-socket-receive-timeout (socket-fd)
+  "Get socket option: RCVTIMEO, return value is a float number"
+  (declare (type integer socket-fd))
+  (fli:with-dynamic-foreign-objects ((timeout (:struct timeval))
+                                     (len :int))
+    (comm::getsockopt socket-fd
+                comm::*sockopt_sol_socket*
+                *sockopt_so_rcvtimeo*
+                (fli:copy-pointer timeout
+                                  :type '(:pointer :void))
+                len)
+    (fli:with-foreign-slots (tv-sec tv-usec) timeout
+      (float (+ tv-sec (/ tv-usec 1000000))))))
+
+#+win32
+(defun get-socket-receive-timeout (socket-fd)
+  "Get socket option: RCVTIMEO, return value is a float number"
+  (declare (type integer socket-fd))
+  (fli:with-dynamic-foreign-objects ((timeout :int)
+                                     (len :int))
+    (comm::getsockopt socket-fd
+                comm::*sockopt_sol_socket*
+                *sockopt_so_rcvtimeo*
+                (fli:copy-pointer timeout
+                                  :type '(:pointer :void))
+                len)
+    (float (/ (fli:dereference timeout) 1000))))
+
+(defun initialize-dynamic-sockaddr (hostname service protocol &aux (original-hostname hostname))
+  (declare (ignorable original-hostname))
+  #+(or lispworks4 lispworks5 lispworks6.0)
+  (let ((server-addr (fli:allocate-dynamic-foreign-object
+                      :type '(:struct comm::sockaddr_in))))
+    (values (comm::initialize-sockaddr_in 
+             server-addr 
+             comm::*socket_af_inet*
+             hostname
+             service protocol)
+            comm::*socket_af_inet*
+            server-addr
+            (fli:pointer-element-size server-addr)))
+  #-(or lispworks4 lispworks5 lispworks6.0)
+  (progn
+    (when (stringp hostname)
+      (setq hostname (comm:string-ip-address hostname))
+      (unless hostname
+        (let ((resolved-hostname (comm:get-host-entry original-hostname :fields '(:address))))
+          (unless resolved-hostname
+            (return-from initialize-dynamic-sockaddr :unknown-host))
+          (setq hostname resolved-hostname))))
+    (if (or (null hostname)
+            (integerp hostname)
+            (comm:ipv6-address-p hostname))
+        (let ((server-addr (fli:allocate-dynamic-foreign-object
+                            :type '(:struct comm::lw-sockaddr))))
+          (multiple-value-bind (error family)
+              (comm::initialize-sockaddr_in 
+               server-addr 
+               hostname
+               service protocol)
+            (values error family
+                    server-addr
+                    (if (eql family comm::*socket_af_inet*)
+                        (fli:size-of '(:struct comm::sockaddr_in))
+                      (fli:size-of '(:struct comm::sockaddr_in6))))))
+      :bad-host)))
+
+(defun open-udp-socket (&key local-address local-port read-timeout
+                             (address-family comm::*socket_af_inet*))
+  "Open a unconnected UDP socket.
+   For binding on address ANY(*), just not set LOCAL-ADDRESS (NIL),
+   for binding on random free unused port, set LOCAL-PORT to 0."
+
+  ;; Note: move (ensure-sockets) here to make sure delivered applications
+  ;; correctly have networking support initialized.
+  ;;
+  ;; Following words was from Martin Simmons, forwarded by Camille Troillard:
+
+  ;; Calling comm::ensure-sockets at load time looks like a bug in Lispworks-udp
+  ;; (it is too early and also unnecessary).
+
+  ;; The LispWorks comm package calls comm::ensure-sockets when it is needed, so I
+  ;; think open-udp-socket should probably do it too.  Calling it more than once is
+  ;; safe and it will be very fast after the first time.
+  #+win32 (comm::ensure-sockets)
+
+  (let ((socket-fd (comm::socket address-family *socket_sock_dgram* *socket_ip_proto_udp*)))
+    (if socket-fd
+        (progn
+          (when read-timeout (set-socket-receive-timeout socket-fd read-timeout))
+          (if local-port
+              (fli:with-dynamic-foreign-objects ()
+                (multiple-value-bind (error local-address-family
+                                            client-addr client-addr-length)
+                    (initialize-dynamic-sockaddr local-address local-port "udp")
+                  (if (or error (not (eql address-family local-address-family)))
+                      (progn
+                        (comm::close-socket socket-fd)
+                        (error "cannot resolve hostname ~S, service ~S: ~A"
+                               local-address local-port (or error "address family mismatch")))
+                    (if (comm::bind socket-fd client-addr client-addr-length)
+                        ;; success, return socket fd
+                        socket-fd
+                      (progn
+                        (comm::close-socket socket-fd)
+                        (error "cannot bind"))))))
+           socket-fd))
+      (error "cannot create socket"))))
+
+(defun connect-to-udp-server (hostname service
+                                       &key local-address local-port read-timeout)
+  "Something like CONNECT-TO-TCP-SERVER"
+  (fli:with-dynamic-foreign-objects ()
+    (multiple-value-bind (error address-family server-addr server-addr-length)
+        (initialize-dynamic-sockaddr hostname service "udp")
+      (when error
+        (error "cannot resolve hostname ~S, service ~S: ~A"
+               hostname service error))
+      (let ((socket-fd (open-udp-socket :local-address local-address
+                                        :local-port local-port
+                                        :read-timeout read-timeout
+                                        :address-family address-family)))
+        (if socket-fd
+            (if (comm::connect socket-fd server-addr server-addr-length)
+                ;; success, return socket fd
+                socket-fd
+              ;; fail, close socket and return nil
+              (progn
+                (comm::close-socket socket-fd)
+                (error "cannot connect")))
+          (error "cannot create socket"))))))
+
+(defun socket-connect (host port &key (protocol :stream) (element-type 'base-char)
+                       timeout deadline (nodelay t nodelay-specified)
+                       local-host local-port)
+
+  ;; What's the meaning of this keyword?
+  (when deadline
+    (unimplemented 'deadline 'socket-connect))
+
+  #+(and lispworks4 (not lispworks4.4)) ; < 4.4.5
+  (when timeout
+    (unsupported 'timeout 'socket-connect :minimum "LispWorks 4.4.5"))
+
+  #+(or lispworks4 lispworks5.0) ; < 5.1
+  (when (and nodelay-specified 
+             (not (eq nodelay :if-supported)))
+    (unsupported 'nodelay 'socket-connect :minimum "LispWorks 5.1"))
+
+  #+lispworks4
+  (when local-host
+     (unsupported 'local-host 'socket-connect :minimum "LispWorks 5.0"))
+  #+lispworks4
+  (when local-port
+     (unsupported 'local-port 'socket-connect :minimum "LispWorks 5.0"))
+
+  (ecase protocol
+    (:stream
+     (let ((hostname (host-to-hostname host))
+           (stream))
+       (setf stream
+             (with-mapped-conditions ()
+               (comm:open-tcp-stream hostname port
+                                     :element-type element-type
+                                     #-(and lispworks4 (not lispworks4.4)) ; >= 4.4.5
+                                     #-(and lispworks4 (not lispworks4.4))
+                                     :timeout timeout
+                                     #-lispworks4 #-lispworks4
+                                     #-lispworks4 #-lispworks4
+                                     :local-address (when local-host (host-to-hostname local-host))
+                                     :local-port local-port
+                                     #-(or lispworks4 lispworks5.0) ; >= 5.1
+                                     #-(or lispworks4 lispworks5.0)
+                                     :nodelay nodelay)))
+       (if stream
+           (make-stream-socket :socket (comm:socket-stream-socket stream)
+                               :stream stream)
+         ;; if no other error catched by above with-mapped-conditions and still fails, then it's a timeout
+         (error 'timeout-error))))
+    (:datagram
+     (let ((usocket (make-datagram-socket
+                     (if (and host port)
+                         (with-mapped-conditions ()
+                           (connect-to-udp-server (host-to-hostname host) port
+                                                  :local-address (and local-host (host-to-hostname local-host))
+                                                  :local-port local-port
+                                                  :read-timeout timeout))
+                         (with-mapped-conditions ()
+                           (open-udp-socket       :local-address (and local-host (host-to-hostname local-host))
+                                                  :local-port local-port
+                                                  :read-timeout timeout)))
+                     :connected-p (and host port t))))
+       usocket))))
+
+(defun socket-listen (host port
+                           &key reuseaddress
+                           (reuse-address nil reuse-address-supplied-p)
+                           (backlog 5)
+                           (element-type 'base-char))
+  #+lispworks4.1
+  (unsupported 'host 'socket-listen :minimum "LispWorks 4.0 or newer than 4.1")
+  #+lispworks4.1
+  (unsupported 'backlog 'socket-listen :minimum "LispWorks 4.0 or newer than 4.1")
+
+  (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
+         (comm::*use_so_reuseaddr* reuseaddress)
+         (hostname (host-to-hostname host))
+         (socket-res-list (with-mapped-conditions ()
+                            (multiple-value-list
+                             #-lispworks4.1 (comm::create-tcp-socket-for-service
+                                             port :address hostname :backlog backlog)
+                             #+lispworks4.1 (comm::create-tcp-socket-for-service port))))
+         (sock (if (not (or (second socket-res-list) (third socket-res-list)))
+                   (first socket-res-list)
+                 (when (eq (second socket-res-list) :bind)
+                   (error 'address-in-use-error)))))
+    (make-stream-server-socket sock :element-type element-type)))
+
+;; Note: COMM::GET-FD-FROM-SOCKET contains addition socket wait operations, which
+;; should NOT be applied on socket FDs who have already been called on W-F-I,
+;; so we have to check the %READY-P slot to decide if this waiting is necessary,
+;; or SOCKET-ACCEPT will just hang. -- Chun Tian (binghe), May 1, 2011
+
+(defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
+  (let* ((socket (with-mapped-conditions (usocket)
+                   #+win32
+                   (if (%ready-p usocket)
+                       (comm::accept-connection-to-socket (socket usocket))
+                     (comm::get-fd-from-socket (socket usocket)))
+                   #-win32
+                   (comm::get-fd-from-socket (socket usocket))))
+         (stream (make-instance 'comm:socket-stream
+                                :socket socket
+                                :direction :io
+                                :element-type (or element-type
+                                                  (element-type usocket)))))
+    #+win32
+    (when socket
+      (setf (%ready-p usocket) nil))
+    (make-stream-socket :socket socket :stream stream)))
+
+;; Sockets and their streams are different objects
+;; close the stream in order to make sure buffers
+;; are correctly flushed and the socket closed.
+(defmethod socket-close ((usocket stream-usocket))
+  "Close socket."
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (close (socket-stream usocket)))
+
+(defmethod socket-close ((usocket usocket))
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+     (comm::close-socket (socket usocket))))
+
+(defmethod socket-close :after ((socket datagram-usocket))
+  "Additional socket-close method for datagram-usocket"
+  (setf (%open-p socket) nil))
+
+(defmethod socket-shutdown ((usocket stream-usocket) direction)
+  (with-mapped-conditions (usocket)
+    (comm:socket-stream-shutdown (socket usocket) direction)))
+
+(defmethod initialize-instance :after ((socket datagram-usocket) &key)
+  (setf (slot-value socket 'send-buffer)
+        (make-array +max-datagram-packet-size+
+                    :element-type '(unsigned-byte 8)
+                    :allocation :static))
+  (setf (slot-value socket 'recv-buffer)
+        (make-array +max-datagram-packet-size+
+                    :element-type '(unsigned-byte 8)
+                    :allocation :static)))
+
+(defvar *length-of-sockaddr_in*
+  (fli:size-of '(:struct comm::sockaddr_in)))
+
+(defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0)
+                        &aux (socket-fd (socket usocket))
+                             (message (slot-value usocket 'send-buffer))) ; TODO: multiple threads send together?
+  "Send message to a socket, using sendto()/send()"
+  (declare (type integer socket-fd)
+           (type sequence buffer))
+  (when host (setq host (host-to-hostname host)))
+  (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte))
+    (replace message buffer :start2 offset :end2 (+ offset size))
+    (let ((n (if (and host port)
+                 (fli:with-dynamic-foreign-objects ()
+                   (multiple-value-bind (error family client-addr client-addr-length)
+                       (initialize-dynamic-sockaddr host port "udp")
+                     (declare (ignore family))
+                     (when error
+                       (error "cannot resolve hostname ~S, port ~S: ~A"
+                              host port error))
+                     (%sendto socket-fd ptr (min size +max-datagram-packet-size+) 0
+                              (fli:copy-pointer client-addr :type '(:struct comm::sockaddr))
+                              client-addr-length)))
+               (comm::%send socket-fd ptr (min size +max-datagram-packet-size+) 0))))
+      (declare (type fixnum n))
+      (if (plusp n)
+          n
+        (let ((errno #-win32 (lw:errno-value)
+                     #+win32 (wsa-get-last-error)))
+          (if (zerop errno)
+              n
+            (raise-usock-err errno socket-fd)))))))
+
+(defmethod socket-receive ((socket datagram-usocket) buffer length &key timeout (max-buffer-size +max-datagram-packet-size+))
+  "Receive message from socket, read-timeout is a float number in seconds.
+
+   This function will return 4 values:
+   1. receive buffer
+   2. number of receive bytes
+   3. remote address
+   4. remote port"
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+                   (integer 0)                          ; size
+                   (unsigned-byte 32)                   ; host
+                   (unsigned-byte 16))                  ; port
+          (type sequence buffer))
+  (let ((socket-fd (socket socket))
+        (message (slot-value socket 'recv-buffer)) ; TODO: how multiple threads do this in parallel?
+        (read-timeout timeout)
+        old-timeout)
+    (declare (type integer socket-fd))
+    (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in))
+                                       (len :int
+                                            #-(or lispworks4 lispworks5.0) ; <= 5.0
+                                            :initial-element *length-of-sockaddr_in*))
+      #+(or lispworks4 lispworks5.0) ; <= 5.0
+      (setf (fli:dereference len) *length-of-sockaddr_in*)
+      (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte))
+        ;; setup new read timeout
+        (when read-timeout
+          (setf old-timeout (get-socket-receive-timeout socket-fd))
+          (set-socket-receive-timeout socket-fd read-timeout))
+        (let ((n (%recvfrom socket-fd ptr max-buffer-size 0
+                            (fli:copy-pointer client-addr :type '(:struct comm::sockaddr))
+                            len)))
+          (declare (type fixnum n))
+          ;; restore old read timeout
+          (when (and read-timeout (/= old-timeout read-timeout))
+            (set-socket-receive-timeout socket-fd old-timeout))
+          ;; Frank James' patch: reset the %read-p for WAIT-FOR-INPUT
+          #+win32 (setf (%ready-p socket) nil)
+          (if (plusp n)
+              (values (if buffer
+                          (replace buffer message
+                                   :end1 (min length max-buffer-size)
+                                   :end2 (min n max-buffer-size))
+                          (subseq message 0 (min n max-buffer-size)))
+                      (min n max-buffer-size)
+                      (comm::ntohl (fli:foreign-slot-value
+                                    (fli:foreign-slot-value client-addr
+                                                            'comm::sin_addr
+                                                            :object-type '(:struct comm::sockaddr_in)
+                                                            :type '(:struct comm::in_addr)
+                                                            :copy-foreign-object nil)
+                                    'comm::s_addr
+                                    :object-type '(:struct comm::in_addr)))
+                      (comm::ntohs (fli:foreign-slot-value client-addr
+                                                           'comm::sin_port
+                                                           :object-type '(:struct comm::sockaddr_in)
+                                                           :type '(:unsigned :short)
+                                                           :copy-foreign-object nil)))
+            (let ((errno #-win32 (lw:errno-value)
+                         #+win32 (wsa-get-last-error)))
+              (if (zerop errno)
+                  (values nil n 0 0)
+                (raise-usock-err errno socket-fd)))))))))
+
+(defmethod get-local-name ((usocket usocket))
+  (multiple-value-bind
+      (address port)
+      (comm:get-socket-address (socket usocket))
+    (values (hbo-to-vector-quad address) port)))
+
+(defmethod get-peer-name ((usocket stream-usocket))
+  (multiple-value-bind
+      (address port)
+      (comm:get-socket-peer-address (socket usocket))
+    (values (hbo-to-vector-quad address) port)))
+
+(defmethod get-local-address ((usocket usocket))
+  (nth-value 0 (get-local-name usocket)))
+
+(defmethod get-peer-address ((usocket stream-usocket))
+  (nth-value 0 (get-peer-name usocket)))
+
+(defmethod get-local-port ((usocket usocket))
+  (nth-value 1 (get-local-name usocket)))
+
+(defmethod get-peer-port ((usocket stream-usocket))
+  (nth-value 1 (get-peer-name usocket)))
+
+(defun lw-hbo-to-vector-quad (hbo)
+  (if (comm:ipv6-address-p hbo)
+      (ipv6-host-to-vector (comm:ipv6-address-string hbo))
+    (hbo-to-vector-quad hbo)))
+
+(defun get-hosts-by-name (name)
+  (with-mapped-conditions ()
+     (mapcar #'lw-hbo-to-vector-quad
+             (comm:get-host-entry name :fields '(:addresses)))))
+
+(defun os-socket-handle (usocket)
+  (socket usocket))
+
+(defun usocket-listen (usocket)
+  (if (stream-usocket-p usocket)
+      (when (listen (socket-stream usocket))
+        usocket)
+    (when (comm::socket-listen (socket usocket))
+      usocket)))
+
+;;;
+;;; Non Windows implementation
+;;;   The Windows implementation needs to resort to the Windows API in order
+;;;   to achieve what we want (what we want is waiting without busy-looping)
+;;;
+
+#-win32
+(progn
+
+  (defun %setup-wait-list (wait-list)
+    (declare (ignore wait-list)))
+
+  (defun %add-waiter (wait-list waiter)
+    (declare (ignore wait-list waiter)))
+
+  (defun %remove-waiter (wait-list waiter)
+    (declare (ignore wait-list waiter)))
+
+  (defun wait-for-input-internal (wait-list &key timeout)
+    (with-mapped-conditions ()
+      ;; unfortunately, it's impossible to share code between
+      ;; non-win32 and win32 platforms...
+      ;; Can we have a sane -pref. complete [UDP!?]- API next time, please?
+      (dolist (x (wait-list-waiters wait-list))
+        (mp:notice-fd (os-socket-handle x)))
+      (labels ((wait-function (socks)
+                (let (rv)
+                  (dolist (x socks rv)
+                    (when (usocket-listen x)
+                      (setf (state x) :READ
+                            rv t))))))
+       (if timeout
+           (mp:process-wait-with-timeout "Waiting for a socket to become active"
+                                       (truncate timeout)
+                                       #'wait-function
+                                       (wait-list-waiters wait-list))
+           (mp:process-wait "Waiting for a socket to become active"
+                            #'wait-function
+                            (wait-list-waiters wait-list))))
+      (dolist (x (wait-list-waiters wait-list))
+        (mp:unnotice-fd (os-socket-handle x)))
+      wait-list))
+
+) ; end of block
+
+
+;;;
+;;;  The Windows side of the story
+;;;    We want to wait without busy looping
+;;;    This code only works in threads which don't have (hidden)
+;;;    windows which need to receive messages. There are workarounds in the Windows API
+;;;    but are those available to 'us'.
+;;;
+
+
+#+win32
+(progn
+
+  ;; LispWorks doesn't provide an interface to wait for a socket
+  ;; to become ready (under Win32, that is) meaning that we need
+  ;; to resort to system calls to achieve the same thing.
+  ;; Luckily, it provides us access to the raw socket handles (as we 
+  ;; wrote the code above.
+
+  (defconstant fd-read 1)
+  (defconstant fd-read-bit 0)
+  (defconstant fd-write 2)
+  (defconstant fd-write-bit 1)
+  (defconstant fd-oob 4)
+  (defconstant fd-oob-bit 2)
+  (defconstant fd-accept 8)
+  (defconstant fd-accept-bit 3)
+  (defconstant fd-connect 16)
+  (defconstant fd-connect-bit 4)
+  (defconstant fd-close 32)
+  (defconstant fd-close-bit 5)
+  (defconstant fd-qos 64)
+  (defconstant fd-qos-bit 6)
+  (defconstant fd-group-qos 128)
+  (defconstant fd-group-qos-bit 7)
+  (defconstant fd-routing-interface 256)
+  (defconstant fd-routing-interface-bit 8)
+  (defconstant fd-address-list-change 512)
+  (defconstant fd-address-list-change-bit 9)
+  
+  (defconstant fd-max-events 10)
+
+  (defconstant fionread 1074030207)
+
+
+  ;; Note:
+  ;;
+  ;;  If special finalization has to occur for a given
+  ;;  system resource (handle), an associated object should
+  ;;  be created.  A special cleanup action should be added
+  ;;  to the system and a special cleanup action should
+  ;;  be flagged on all objects created for resources like it
+  ;;
+  ;;  We have 2 functions to do so:
+  ;;   * hcl:add-special-free-action (function-symbol)
+  ;;   * hcl:flag-special-free-action (object)
+  ;;
+  ;;  Note that the special free action will be called on all
+  ;;  objects which have been flagged for special free, so be
+  ;;  sure to check for the right argument type!
+  
+  (fli:define-foreign-type ws-socket () '(:unsigned :int))
+  (fli:define-foreign-type win32-handle () '(:unsigned :int))
+  (fli:define-c-struct wsa-network-events
+    (network-events :long)
+    (error-code (:c-array :int 10)))
+
+  (fli:define-foreign-function (wsa-event-create "WSACreateEvent" :source)
+      ()
+      :lambda-list nil
+    :result-type :int
+    :module "ws2_32")
+
+  (fli:define-foreign-function (wsa-event-close "WSACloseEvent" :source)
+      ((event-object win32-handle))
+    :result-type :int
+    :module "ws2_32")
+
+  (fli:define-foreign-function (wsa-enum-network-events "WSAEnumNetworkEvents" :source)
+      ((socket ws-socket)
+       (event-object win32-handle)
+       (network-events (:reference-return wsa-network-events)))
+    :result-type :int
+    :module "ws2_32")
+  
+  (fli:define-foreign-function (wsa-event-select "WSAEventSelect" :source)
+      ((socket ws-socket)
+       (event-object win32-handle)
+       (network-events :long))
+    :result-type :int
+    :module "ws2_32")
+
+  (fli:define-foreign-function (wsa-get-last-error "WSAGetLastError" :source)
+      ()
+    :result-type :int
+    :module "ws2_32")
+
+  (fli:define-foreign-function (wsa-ioctlsocket "ioctlsocket" :source)
+      ((socket :long) (cmd :long) (argp (:ptr :long)))
+    :result-type :int
+    :module "ws2_32")
+
+
+  ;; The Windows system 
+
+
+  ;; Now that we have access to the system calls, this is the plan:
+
+  ;; 1. Receive a wait-list with associated sockets to wait for
+  ;; 2. Add all those sockets to an event handle
+  ;; 3. Listen for an event on that handle (we have a LispWorks system:: internal for that)
+  ;; 4. After listening, detect if there are errors
+  ;;    (this step is different from Unix, where we can have only one error)
+  ;; 5. If so, raise one of them
+  ;; 6. If not so, return the sockets which have input waiting for them
+
+
+  (defun maybe-wsa-error (rv &optional socket)
+    (unless (zerop rv)
+      (raise-usock-err (wsa-get-last-error) socket)))
+
+  (defun bytes-available-for-read (socket)
+    (fli:with-dynamic-foreign-objects ((int-ptr :long))
+      (let ((rv (wsa-ioctlsocket (os-socket-handle socket) fionread int-ptr)))
+        (if (= 0 rv)
+            (fli:dereference int-ptr)
+          0))))
+
+  (defun socket-ready-p (socket)
+    (if (typep socket 'stream-usocket)
+        (< 0 (bytes-available-for-read socket))
+      (%ready-p socket)))
+
+  (defun waiting-required (sockets)
+    (notany #'socket-ready-p sockets))
+
+  (defun wait-for-input-internal (wait-list &key timeout)
+    (when (waiting-required (wait-list-waiters wait-list))
+      (system:wait-for-single-object (wait-list-%wait wait-list)
+                                     "Waiting for socket activity" timeout))
+    (update-ready-and-state-slots (wait-list-waiters wait-list)))
+  
+  (defun map-network-events (func network-events)
+    (let ((event-map (fli:foreign-slot-value network-events 'network-events))
+          (error-array (fli:foreign-slot-pointer network-events 'error-code)))
+      (unless (zerop event-map)
+        (dotimes (i fd-max-events)
+          (unless (zerop (ldb (byte 1 i) event-map)) ;;### could be faster with ash and logand?
+            (funcall func (fli:foreign-aref error-array i)))))))
+
+  (defun update-ready-and-state-slots (sockets)
+    (dolist (socket sockets)
+      (if (or (and (stream-usocket-p socket)
+                   (listen (socket-stream socket)))
+              (%ready-p socket))
+          (setf (state socket) :READ)
+        (multiple-value-bind
+            (rv network-events)
+            (wsa-enum-network-events (os-socket-handle socket) 0 t)
+          (if (zerop rv)
+              (map-network-events #'(lambda (err-code)
+                                      (if (zerop err-code)
+                                          (setf (%ready-p socket) t
+                                                (state socket) :READ)
+                                        (raise-usock-err err-code socket)))
+                                  network-events)
+            (maybe-wsa-error rv socket))))))
+
+  ;; The wait-list part
+
+  (defun free-wait-list (wl)
+    (when (wait-list-p wl)
+      (unless (null (wait-list-%wait wl))
+        (wsa-event-close (wait-list-%wait wl))
+        (setf (wait-list-%wait wl) nil))))
+  
+  (eval-when (:load-toplevel :execute)
+    (hcl:add-special-free-action 'free-wait-list))
+  
+  (defun %setup-wait-list (wait-list)
+    (hcl:flag-special-free-action wait-list)
+    (setf (wait-list-%wait wait-list) (wsa-event-create)))
+
+  (defun %add-waiter (wait-list waiter)
+    (let ((events (etypecase waiter
+                    (stream-server-usocket (logior fd-connect fd-accept fd-close))
+                    (stream-usocket (logior fd-connect fd-read fd-oob fd-close))
+                    (datagram-usocket (logior fd-read)))))
+      (maybe-wsa-error
+       (wsa-event-select (os-socket-handle waiter) (wait-list-%wait wait-list) events)
+       waiter)))
+
+  (defun %remove-waiter (wait-list waiter)
+    (maybe-wsa-error
+     (wsa-event-select (os-socket-handle waiter) (wait-list-%wait wait-list) 0)
+     waiter))
+  
+) ; end of WIN32-block
+
+(defun set-socket-reuse-address (socket-fd reuse-address-p)
+  (declare (type integer socket-fd)
+           (type boolean reuse-address-p))
+  (fli:with-dynamic-foreign-objects ((value :int))
+    (setf (fli:dereference value) (if reuse-address-p 1 0))
+    (if (zerop (comm::setsockopt socket-fd
+                                 comm::*sockopt_sol_socket*
+                                 comm::*sockopt_so_reuseaddr*
+                                 (fli:copy-pointer value
+                                                   :type '(:pointer :void))
+                                 (fli:size-of :int)))
+        reuse-address-p)))
+
+(defun get-socket-reuse-address (socket-fd)
+  (declare (type integer socket-fd))
+  (fli:with-dynamic-foreign-objects ((value :int) (len :int))
+    (if (zerop (comm::getsockopt socket-fd
+                                 comm::*sockopt_sol_socket*
+                                 comm::*sockopt_so_reuseaddr*
+                                 (fli:copy-pointer value
+                                                   :type '(:pointer :void))
+                                 len))
+        (= 1 (fli:dereference value)))))
diff --git a/deps/usocket/backend/mcl.lisp b/deps/usocket/backend/mcl.lisp
new file mode 100644 (file)
index 0000000..5ef29c8
--- /dev/null
@@ -0,0 +1,272 @@
+;;;; $Id$
+;;;; $URL$
+
+;; MCL backend for USOCKET 0.4.1
+;; Terje Norderhaug <terje@in-progress.com>, January 1, 2009
+
+(in-package :usocket)
+
+(defun handle-condition (condition &optional socket)
+  ; incomplete, needs to handle additional conditions
+  (flet ((raise-error (&optional socket-condition)
+           (if socket-condition
+           (error socket-condition :socket socket)
+           (error  'unknown-error :socket socket :real-error condition))))
+    (typecase condition
+      (ccl:host-stopped-responding
+       (raise-error 'host-down-error))
+      (ccl:host-not-responding
+       (raise-error 'host-unreachable-error))
+      (ccl:connection-reset 
+       (raise-error 'connection-reset-error))
+      (ccl:connection-timed-out
+       (raise-error 'timeout-error))
+      (ccl:opentransport-protocol-error
+       (raise-error 'protocol-not-supported-error))       
+      (otherwise
+       (raise-error)))))
+
+(defun socket-connect (host port &key (element-type 'character) timeout deadline nodelay 
+                            local-host local-port (protocol :stream))
+  (when (eq nodelay :if-supported)
+    (setf nodelay t))
+  (ecase protocol
+    (:stream
+     (with-mapped-conditions ()
+       (let* ((socket
+               (make-instance 'active-socket
+                :remote-host (when host (host-to-hostname host)) 
+                 :remote-port port
+                 :local-host (when local-host (host-to-hostname local-host)) 
+                 :local-port local-port
+                 :deadline deadline
+                 :nodelay nodelay
+                 :connect-timeout (and timeout (round (* timeout 60)))
+                 :element-type element-type))
+              (stream (socket-open-stream socket)))
+         (make-stream-socket :socket socket :stream stream))))
+    (:datagram
+     (with-mapped-conditions ()
+       (make-datagram-socket
+        (ccl::open-udp-socket :local-address (and local-host (host-to-hbo local-host))
+                              :local-port local-port))))))
+
+(defun socket-listen (host port
+                           &key reuseaddress
+                           (reuse-address nil reuse-address-supplied-p)
+                           (backlog 5)
+                           (element-type 'character))
+  (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
+        (socket (with-mapped-conditions ()
+                  (make-instance 'passive-socket 
+                                 :local-port port
+                                 :local-host (host-to-hbo host)
+                                 :reuse-address reuseaddress
+                                 :backlog backlog))))
+    (make-stream-server-socket socket :element-type element-type)))
+
+(defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
+  (let* ((socket (socket usocket))
+         (stream (with-mapped-conditions (usocket)
+                   (socket-accept socket :element-type element-type))))
+    (make-stream-socket :socket socket :stream stream)))
+
+(defmethod socket-close ((usocket usocket))
+  (with-mapped-conditions (usocket)
+    (socket-close (socket usocket))))
+
+(defmethod socket-shutdown ((usocket usocket) direction)
+  (declare (ignore usocket direction))
+  ;; As far as I can tell there isn't a way to shutdown a socket in mcl.
+  (unsupported "shutdown" 'socket-shutdown))
+
+(defmethod ccl::stream-close ((usocket usocket))
+  (socket-close usocket))
+
+(defun get-hosts-by-name (name)
+  (with-mapped-conditions ()
+    (list (hbo-to-vector-quad (ccl::get-host-address
+                               (host-to-hostname name))))))
+
+(defun get-host-by-address (address)
+  (with-mapped-conditions ()
+    (ccl::inet-host-name (host-to-hbo address))))
+
+(defmethod get-local-name ((usocket usocket))
+  (values (get-local-address usocket)
+          (get-local-port usocket)))
+
+(defmethod get-peer-name ((usocket stream-usocket))
+  (values (get-peer-address usocket)
+          (get-peer-port usocket)))
+
+(defmethod get-local-address ((usocket usocket))
+  (hbo-to-vector-quad (ccl::get-host-address (or (local-host (socket usocket)) ""))))
+
+(defmethod get-local-port ((usocket usocket))
+  (local-port (socket usocket)))
+
+(defmethod get-peer-address ((usocket stream-usocket))
+  (hbo-to-vector-quad (ccl::get-host-address (remote-host (socket usocket)))))
+
+(defmethod get-peer-port ((usocket stream-usocket))
+  (remote-port (socket usocket)))
+
+
+(defun %setup-wait-list (wait-list)
+  (declare (ignore wait-list)))
+
+(defun %add-waiter (wait-list waiter)
+  (declare (ignore wait-list waiter)))
+
+(defun %remove-waiter (wait-list waiter)
+  (declare (ignore wait-list waiter)))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; BASIC MCL SOCKET IMPLEMENTATION
+
+(defclass socket ()
+  ((local-port :reader local-port :initarg :local-port)
+   (local-host :reader local-host :initarg :local-host)
+   (element-type :reader element-type :initform 'ccl::base-character :initarg :element-type)))
+
+(defclass active-socket (socket)
+  ((remote-host :reader remote-host :initarg :remote-host)
+   (remote-port :reader remote-port :initarg :remote-port)
+   (deadline :initarg :deadline)
+   (nodelay :initarg :nodelay)
+   (connect-timeout :reader connect-timeout :initform NIL :initarg :connect-timeout
+                    :type (or null fixnum) :documentation "ticks (60th of a second)")))
+
+(defmethod socket-open-stream ((socket active-socket))
+  (ccl::open-tcp-stream (or (remote-host socket)(ccl::local-interface-ip-address)) (remote-port socket)
+   :element-type (if (subtypep (element-type socket) 'character) 'ccl::base-character 'unsigned-byte)
+   :connect-timeout (connect-timeout socket)))
+
+(defmethod socket-close ((socket active-socket))
+  NIL)
+
+(defclass passive-socket (socket)
+  ((streams :accessor socket-streams :type list :initform NIL
+           :documentation "Circular list of streams with first element the next to open")
+   (reuse-address :reader reuse-address :initarg :reuse-address)
+   (lock :reader socket-lock :initform (ccl:make-lock "Socket"))))
+
+(defmethod initialize-instance :after ((socket passive-socket) &key backlog)
+  (loop repeat backlog
+        collect (socket-open-listener socket) into streams
+        finally (setf (socket-streams socket)
+                      (cdr (rplacd (last streams) streams))))
+  (when (zerop (local-port socket))
+    (setf (slot-value socket 'local-port)
+          (or (ccl::process-wait-with-timeout "binding port" (* 10 60) 
+               #'ccl::stream-local-port (car (socket-streams socket)))
+              (error "timeout")))))
+
+(defmethod socket-accept ((socket passive-socket) &key element-type &aux (lock (socket-lock socket)))
+  (flet ((connection-established-p (stream)
+          (ccl::with-io-buffer-locked ((ccl::stream-io-buffer stream nil))
+            (let ((state (ccl::opentransport-stream-connection-state stream)))
+              (not (eq :unbnd state))))))
+    (with-mapped-conditions ()
+      (ccl:with-lock-grabbed (lock nil "Socket Lock")
+       (let ((connection (shiftf (car (socket-streams socket))
+                                 (socket-open-listener socket element-type))))
+         (pop (socket-streams socket))
+         (ccl:process-wait "Accepting" #'connection-established-p connection)
+         connection)))))
+
+(defmethod socket-close ((socket passive-socket))
+  (loop
+    with streams = (socket-streams socket)
+    for (stream tail) on streams
+    do (close stream :abort T)
+    until (eq tail streams)
+    finally (setf (socket-streams socket) NIL)))
+
+(defmethod socket-open-listener (socket &optional element-type)
+  ; see http://code.google.com/p/mcl/issues/detail?id=28
+  (let* ((ccl::*passive-interface-address* (local-host socket))
+         (new (ccl::open-tcp-stream NIL (or (local-port socket) #$kOTAnyInetAddress) 
+                                    :reuse-local-port-p (reuse-address socket) 
+                                    :element-type (if (subtypep (or element-type (element-type socket))
+                                                                'character) 
+                                                    'ccl::base-character 
+                                                    'unsigned-byte))))
+    (declare (special ccl::*passive-interface-address*))
+    new))
+
+(defmethod input-available-p ((stream ccl::opentransport-stream))
+  (macrolet ((when-io-buffer-lock-grabbed ((lock &optional multiple-value-p) &body body)
+              "Evaluates the body if and only if the lock is successfully grabbed"
+              ;; like with-io-buffer-lock-grabbed but returns immediately instead of polling the lock
+              (let ((needs-unlocking-p (gensym))
+                    (lock-var (gensym)))
+                `(let* ((,lock-var ,lock)
+                        (ccl::*grabbed-io-buffer-locks* (cons ,lock-var ccl::*grabbed-io-buffer-locks*))
+                        (,needs-unlocking-p (needs-unlocking-p ,lock-var)))
+                   (declare (dynamic-extent ccl::*grabbed-io-buffer-locks*))
+                   (when ,needs-unlocking-p
+                     (,(if multiple-value-p 'multiple-value-prog1 'prog1)
+                        (progn ,@body)
+                        (ccl::%release-io-buffer-lock ,lock-var)))))))
+    (labels ((needs-unlocking-p (lock)
+              (declare (type ccl::lock lock))
+              ;; crucial - clears bogus lock.value as in grab-io-buffer-lock-out-of-line:
+              (ccl::%io-buffer-lock-really-grabbed-p lock)
+              (ccl:store-conditional lock nil ccl:*current-process*)))
+      "similar to stream-listen on buffered-input-stream-mixin but without waiting for lock"
+      (let ((io-buffer (ccl::stream-io-buffer stream)))
+       (or (not (eql 0 (ccl::io-buffer-incount io-buffer)))
+           (ccl::io-buffer-untyi-char io-buffer)
+           (locally (declare (optimize (speed 3) (safety 0)))
+             (when-io-buffer-lock-grabbed ((ccl::io-buffer-lock io-buffer))
+                       (funcall (ccl::io-buffer-listen-function io-buffer) stream io-buffer))))))))
+
+(defmethod connection-established-p ((stream ccl::opentransport-stream))
+  (ccl::with-io-buffer-locked ((ccl::stream-io-buffer stream nil))
+    (let ((state (ccl::opentransport-stream-connection-state stream)))
+      (not (eq :unbnd state)))))
+
+(defun wait-for-input-internal (wait-list &key timeout &aux result)
+  (labels ((ready-sockets (sockets)
+            (dolist (sock sockets result)
+              (when (cond ((stream-usocket-p sock)
+                           (input-available-p (socket-stream sock)))
+                          ((stream-server-usocket-p sock)
+                           (let ((ot-stream (first (socket-streams (socket sock)))))
+                             (or (input-available-p ot-stream)
+                                 (connection-established-p ot-stream)))))
+                (push sock result)))))
+    (with-mapped-conditions ()
+      (ccl:process-wait-with-timeout
+       "socket input"
+       (when timeout (truncate (* timeout 60)))
+       #'ready-sockets
+       (wait-list-waiters wait-list)))
+    (nreverse result)))
+
+;;; datagram socket methods
+
+(defmethod initialize-instance :after ((usocket datagram-usocket) &key)
+  (with-slots (socket send-buffer recv-buffer) usocket
+    (setq send-buffer
+         (ccl::make-TUnitData (ccl::ot-conn-endpoint socket)))
+    (setq recv-buffer
+         (ccl::make-TUnitData (ccl::ot-conn-endpoint socket)))))
+
+(defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
+  (with-mapped-conditions (usocket)
+    (with-slots (socket send-buffer) usocket
+      (unless (and host port)
+       (unsupported 'host 'socket-send))
+      (ccl::send-message socket send-buffer buffer size host port offset))))
+
+(defmethod socket-receive ((usocket datagram-usocket) buffer length &key)
+  (with-mapped-conditions (usocket)
+    (with-slots (socket recv-buffer) usocket
+      (ccl::receive-message socket recv-buffer buffer length))))
+
+(defmethod socket-close ((socket datagram-usocket))
+  nil) ; TODO
diff --git a/deps/usocket/backend/mocl.lisp b/deps/usocket/backend/mocl.lisp
new file mode 100644 (file)
index 0000000..58bdf17
--- /dev/null
@@ -0,0 +1,162 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+(defun handle-condition (condition &optional (socket nil))
+  "Dispatch correct usocket condition."
+  (declare (ignore socket))
+  (signal condition))
+
+(defun socket-connect (host port &key (protocol :stream) (element-type 'character)
+                       timeout deadline (nodelay t nodelay-specified)
+                      (local-host nil local-host-p)
+                      (local-port nil local-port-p))
+  (when (and nodelay-specified 
+             (not (eq nodelay :if-supported)))
+    (unsupported 'nodelay 'socket-connect))
+  (when deadline (unsupported 'deadline 'socket-connect))
+  (when timeout (unimplemented 'timeout 'socket-connect))
+  (when local-host-p
+     (unimplemented 'local-host 'socket-connect))
+  (when local-port-p
+     (unimplemented 'local-port 'socket-connect))
+
+  (let (socket)
+    (ecase protocol
+      (:stream
+       (setf socket (rt::socket-connect host port))
+       (let ((stream (rt::make-socket-stream socket :binaryp (not (eq element-type 'character)))))
+        (make-stream-socket :socket socket :stream stream)))
+      (:datagram
+       (error 'unsupported
+              :feature '(protocol :datagram)
+              :context 'socket-connect)))))
+
+(defun socket-listen (host port
+                           &key reuseaddress
+                           (reuse-address nil reuse-address-supplied-p)
+                           (backlog 5)
+                           (element-type 'character))
+  (unimplemented 'socket-listen 'mocl))
+
+(defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
+  (unimplemented 'socket-accept 'mocl))
+
+;; Sockets and their associated streams are modelled as
+;; different objects. Be sure to close the socket stream
+;; when closing stream-sockets; it makes sure buffers
+;; are flushed and the socket is closed correctly afterwards.
+(defmethod socket-close ((usocket usocket))
+  "Close socket."
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (rt::socket-shutdown usocket)
+  (rt::c-fclose usocket))
+
+(defmethod socket-close ((usocket stream-usocket))
+  "Close socket."
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (close (socket-stream usocket)))
+
+;; (defmethod socket-close :after ((socket datagram-usocket))
+;;   (setf (%open-p socket) nil))
+
+(defmethod socket-shutdown ((usocket stream-usocket) direction)
+  (declare (ignore usocket direction))
+  ;; sure would be nice if there was some documentation for mocl...
+  (unimplemented "shutdown" 'socket-shutdown))
+
+;; (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port)
+;;   (let ((s (socket usocket))
+;;     (host (if host (host-to-hbo host)))
+;;     (real-buffer (if (zerop offset)
+;;                      buffer
+;;                      (subseq buffer offset (+ offset size)))))
+;;     (multiple-value-bind (result errno)
+;;     (ext:inet-socket-send-to s real-buffer size
+;;                              :remote-host host :remote-port port)
+;;       (or result
+;;       (mocl-map-socket-error errno :socket usocket)))))
+
+;; (defmethod socket-receive ((socket datagram-usocket) buffer length &key)
+;;   (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+;;                (integer 0)                          ; size
+;;                (unsigned-byte 32)                   ; host
+;;                (unsigned-byte 16)))                 ; port
+;;   (let ((s (socket socket)))
+;;     (let ((real-buffer (or buffer
+;;                        (make-array length :element-type '(unsigned-byte 8))))
+;;       (real-length (or length
+;;                        (length buffer))))
+;;       (multiple-value-bind (result errno remote-host remote-port)
+;;       (ext:inet-socket-receive-from s real-buffer real-length)
+;;     (if result
+;;         (values real-buffer result remote-host remote-port)
+;;         (mocl-map-socket-error errno :socket socket))))))
+
+;; (defmethod get-local-name ((usocket usocket))
+;;   (multiple-value-bind (address port)
+;;       (with-mapped-conditions (usocket)
+;;         (ext:get-socket-host-and-port (socket usocket)))
+;;     (values (hbo-to-vector-quad address) port)))
+
+;; (defmethod get-peer-name ((usocket stream-usocket))
+;;   (multiple-value-bind (address port)
+;;       (with-mapped-conditions (usocket)
+;;         (ext:get-peer-host-and-port (socket usocket)))
+;;     (values (hbo-to-vector-quad address) port)))
+
+;; (defmethod get-local-address ((usocket usocket))
+;;   (nth-value 0 (get-local-name usocket)))
+
+;; (defmethod get-peer-address ((usocket stream-usocket))
+;;   (nth-value 0 (get-peer-name usocket)))
+
+;; (defmethod get-local-port ((usocket usocket))
+;;   (nth-value 1 (get-local-name usocket)))
+
+;; (defmethod get-peer-port ((usocket stream-usocket))
+;;   (nth-value 1 (get-peer-name usocket)))
+
+
+;; (defun get-host-by-address (address)
+;;   (multiple-value-bind (host errno)
+;;       (ext:lookup-host-entry (host-byte-order address))
+;;     (cond (host
+;;            (ext:host-entry-name host))
+;;           (t
+;;            (let ((condition (cdr (assoc errno +unix-ns-error-map+))))
+;;              (cond (condition
+;;                     (error condition :host-or-ip address))
+;;                    (t
+;;                     (error 'ns-unknown-error :host-or-ip address
+;;                            :real-error errno))))))))
+
+(defun get-hosts-by-name (name)
+  (rt::lookup-host name))
+
+;; (defun get-host-name ()
+;;   (unix:unix-gethostname))
+
+
+;;
+;;
+;;  WAIT-LIST part
+;;
+
+
+(defun %add-waiter (wl waiter)
+  (declare (ignore wl waiter)))
+
+(defun %remove-waiter (wl waiter)
+  (declare (ignore wl waiter)))
+
+(defun %setup-wait-list (wl)
+  (declare (ignore wl)))
+
+(defun wait-for-input-internal (wait-list &key timeout)
+  (unimplemented 'wait-for-input-internal 'mocl))
diff --git a/deps/usocket/backend/openmcl.lisp b/deps/usocket/backend/openmcl.lisp
new file mode 100644 (file)
index 0000000..2fab670
--- /dev/null
@@ -0,0 +1,270 @@
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+(defun get-host-name ()
+  (ccl::%stack-block ((resultbuf 256))
+    (when (zerop (#_gethostname resultbuf 256))
+      (ccl::%get-cstring resultbuf))))
+
+(defparameter +openmcl-error-map+
+  '((:address-in-use . address-in-use-error)
+    (:connection-aborted . connection-aborted-error)
+    (:no-buffer-space . no-buffers-error)
+    (:connection-timed-out . timeout-error)
+    (:connection-refused . connection-refused-error)
+    (:host-unreachable . host-unreachable-error)
+    (:host-down . host-down-error)
+    (:network-down . network-down-error)
+    (:address-not-available . address-not-available-error)
+    (:network-reset . network-reset-error)
+    (:connection-reset . connection-reset-error)
+    (:shutdown . shutdown-error)
+    (:access-denied . operation-not-permitted-error)))
+
+(defparameter +openmcl-nameserver-error-map+
+  '((:no-recovery . ns-no-recovery-error)
+    (:try-again . ns-try-again-condition)
+    (:host-not-found . ns-host-not-found-error)))
+
+;; we need something which the openmcl implementors 'forgot' to do:
+;; wait for more than one socket-or-fd
+
+(defun input-available-p (sockets &optional ticks-to-wait)
+  (ccl::rletZ ((tv :timeval))
+    (ccl::ticks-to-timeval ticks-to-wait tv)
+    ;;### The trickery below can be moved to the wait-list now...
+    (ccl::%stack-block ((infds ccl::*fd-set-size*))
+      (ccl::fd-zero infds)
+      (let ((max-fd -1))
+        (dolist (sock sockets)
+          (let ((fd (openmcl-socket:socket-os-fd (socket sock))))
+            (when fd ;; may be NIL if closed
+              (setf max-fd (max max-fd fd))
+              (ccl::fd-set fd infds))))
+        (let ((res (#_select (1+ max-fd)
+                             infds (ccl::%null-ptr) (ccl::%null-ptr)
+                             (if ticks-to-wait tv (ccl::%null-ptr)))))
+          (when (> res 0)
+            (dolist (sock sockets)
+              (let ((fd (openmcl-socket:socket-os-fd (socket sock))))
+                (when (and fd (ccl::fd-is-set fd infds))
+                  (setf (state sock) :READ)))))
+          sockets)))))
+
+(defun raise-error-from-id (condition-id socket real-condition)
+  (let ((usock-err (cdr (assoc condition-id +openmcl-error-map+))))
+    (if usock-err
+        (error usock-err :socket socket)
+      (error 'unknown-error :socket socket :real-error real-condition))))
+
+(defun handle-condition (condition &optional socket)
+  (typecase condition
+    (openmcl-socket:socket-error
+       (raise-error-from-id (openmcl-socket:socket-error-identifier condition)
+                            socket condition))
+    (ccl:input-timeout
+       (error 'timeout-error :socket socket))
+    (ccl:communication-deadline-expired
+       (error 'deadline-timeout-error :socket socket))
+    (ccl::socket-creation-error #| ugh! |#
+       (let* ((condition-id (ccl::socket-creation-error-identifier condition))
+             (nameserver-error (cdr (assoc condition-id
+                                           +openmcl-nameserver-error-map+))))
+        (if nameserver-error
+             (if (typep nameserver-error 'serious-condition)
+                 (error nameserver-error :host-or-ip nil)
+                 (signal nameserver-error :host-or-ip nil))
+          (raise-error-from-id condition-id socket condition))))))
+
+(defun to-format (element-type protocol)
+  (cond ((null element-type)
+        (ecase protocol ; default value of different protocol
+          (:stream :text)
+          (:datagram :binary)))
+       ((subtypep element-type 'character)
+        :text)
+       (t :binary)))
+
+#-ipv6
+(defun socket-connect (host port &key (protocol :stream) element-type
+                      timeout deadline nodelay
+                      local-host local-port)
+  (when (eq nodelay :if-supported)
+    (setf nodelay t))
+  (with-mapped-conditions ()
+    (ecase protocol
+      (:stream
+       (let ((mcl-sock
+             (openmcl-socket:make-socket :remote-host (host-to-hostname host)
+                                         :remote-port port
+                                         :local-host local-host
+                                         :local-port local-port
+                                         :format (to-format element-type protocol)
+                                         :external-format ccl:*default-external-format*
+                                         :deadline deadline
+                                         :nodelay nodelay
+                                         :connect-timeout timeout)))
+        (make-stream-socket :stream mcl-sock :socket mcl-sock)))
+      (:datagram
+       (let* ((mcl-sock
+              (openmcl-socket:make-socket :address-family :internet
+                                          :type :datagram
+                                          :local-host local-host
+                                          :local-port local-port
+                                          :input-timeout timeout
+                                          :format (to-format element-type protocol)
+                                          :external-format ccl:*default-external-format*))
+             (usocket (make-datagram-socket mcl-sock)))
+        (when (and host port)
+          (ccl::inet-connect (ccl::socket-device mcl-sock)
+                             (ccl::host-as-inet-host host)
+                             (ccl::port-as-inet-port port "udp")))
+        (setf (connected-p usocket) t)
+        usocket)))))
+
+#-ipv6
+(defun socket-listen (host port
+                      &key reuseaddress
+                           (reuse-address nil reuse-address-supplied-p)
+                           (backlog 5)
+                           (element-type 'character))
+  (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
+        (real-host (host-to-hostname host))
+         (sock (with-mapped-conditions ()
+                  (apply #'openmcl-socket:make-socket
+                         (append (list :connect :passive
+                                       :reuse-address reuseaddress
+                                       :local-port port
+                                       :backlog backlog
+                                       :format (to-format element-type :stream))
+                                 (unless (eq host *wildcard-host*)
+                                   (list :local-host real-host)))))))
+    (make-stream-server-socket sock :element-type element-type)))
+
+(defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
+  (declare (ignore element-type)) ;; openmcl streams are bi/multivalent
+  (let ((sock (with-mapped-conditions (usocket)
+                 (openmcl-socket:accept-connection (socket usocket)))))
+    (make-stream-socket :socket sock :stream sock)))
+
+;; One close method is sufficient because sockets
+;; and their associated objects are represented
+;; by the same object.
+(defmethod socket-close ((usocket usocket))
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+    (close (socket usocket))))
+
+(defmethod socket-shutdown ((usocket stream-usocket) direction)
+  (with-mapped-conditions (usocket)
+    (openmcl-socket:shutdown sock :direction direction)))
+
+#-ipv6
+(defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
+  (with-mapped-conditions (usocket)
+    (if (and host port)
+       (openmcl-socket:send-to (socket usocket) buffer size
+                               :remote-host (host-to-hbo host)
+                               :remote-port port
+                               :offset offset)
+       ;; Clozure CL's socket function SEND-TO doesn't support operations on connected UDP sockets,
+       ;; so we have to define our own.
+       (let* ((socket (socket usocket))
+              (fd (ccl::socket-device socket)))
+         (multiple-value-setq (buffer offset)
+           (ccl::verify-socket-buffer buffer offset size))
+         (ccl::%stack-block ((bufptr size))
+           (ccl::%copy-ivector-to-ptr buffer offset bufptr 0 size)
+           (ccl::socket-call socket "send"
+             (ccl::with-eagain fd :output
+               (ccl::ignoring-eintr
+                 (ccl::check-socket-error (#_send fd bufptr size 0))))))))))
+
+(defmethod socket-receive ((usocket datagram-usocket) buffer length &key)
+  (with-mapped-conditions (usocket)
+    (openmcl-socket:receive-from (socket usocket) length :buffer buffer)))
+
+(defun usocket-host-address (address)
+  (cond
+    ((integerp address)
+     (hbo-to-vector-quad address))
+    ((and (arrayp address)
+          (= (length address) 16)
+          (every #'= address #(0 0 0 0 0 0 0 0 0 0 #xff #xff)))
+     (make-array 4 :displaced-to address :displaced-index-offset 12))
+    (t
+     address)))
+
+(defmethod get-local-address ((usocket usocket))
+  (usocket-host-address (openmcl-socket:local-host (socket usocket))))
+
+(defmethod get-peer-address ((usocket stream-usocket))
+  (usocket-host-address (openmcl-socket:remote-host (socket usocket))))
+
+(defmethod get-local-port ((usocket usocket))
+  (openmcl-socket:local-port (socket usocket)))
+
+(defmethod get-peer-port ((usocket stream-usocket))
+  (openmcl-socket:remote-port (socket usocket)))
+
+(defmethod get-local-name ((usocket usocket))
+  (values (get-local-address usocket)
+          (get-local-port usocket)))
+
+(defmethod get-peer-name ((usocket stream-usocket))
+  (values (get-peer-address usocket)
+          (get-peer-port usocket)))
+
+(defun get-host-by-address (address)
+  (with-mapped-conditions ()
+     (openmcl-socket:ipaddr-to-hostname (host-to-hbo address))))
+
+(defun get-hosts-by-name (name)
+  (with-mapped-conditions ()
+     (list (hbo-to-vector-quad (openmcl-socket:lookup-hostname
+                                (host-to-hostname name))))))
+
+(defun %setup-wait-list (wait-list)
+  (declare (ignore wait-list)))
+
+(defun %add-waiter (wait-list waiter)
+  (declare (ignore wait-list waiter)))
+
+(defun %remove-waiter (wait-list waiter)
+  (declare (ignore wait-list waiter)))
+
+(defun wait-for-input-internal (wait-list &key timeout)
+  (with-mapped-conditions ()
+    (let* ((ticks-timeout (truncate (* (or timeout 1)
+                                       ccl::*ticks-per-second*))))
+      (input-available-p (wait-list-waiters wait-list)
+                        (when timeout ticks-timeout))
+      wait-list)))
+
+;;; Helper functions for option.lisp
+
+(defun get-socket-option-reuseaddr (socket)
+  (ccl::int-getsockopt (ccl::socket-device socket)
+                                  #$SOL_SOCKET #$SO_REUSEADDR))
+
+(defun set-socket-option-reuseaddr (socket value)
+  (ccl::int-setsockopt (ccl::socket-device socket)
+                                  #$SOL_SOCKET #$SO_REUSEADDR value))
+
+(defun get-socket-option-broadcast (socket)
+  (ccl::int-getsockopt (ccl::socket-device socket)
+                                  #$SOL_SOCKET #$SO_BROADCAST))
+
+(defun set-socket-option-broadcast (socket value)
+  (ccl::int-setsockopt (ccl::socket-device socket)
+                                  #$SOL_SOCKET #$SO_BROADCAST value))
+
+(defun get-socket-option-tcp-nodelay (socket)
+  (ccl::int-getsockopt (ccl::socket-device socket)
+                                  #$IPPROTO_TCP #$TCP_NODELAY))
+
+(defun set-socket-option-tcp-nodelay (socket value)
+  (ccl::int-setsockopt (ccl::socket-device socket)
+                                  #$IPPROTO_TCP #$TCP_NODELAY value))
diff --git a/deps/usocket/backend/sbcl.lisp b/deps/usocket/backend/sbcl.lisp
new file mode 100644 (file)
index 0000000..6fccaed
--- /dev/null
@@ -0,0 +1,866 @@
+;;;; -*- Mode: Lisp -*-
+;;;; $Id$
+;;;; $URL$
+
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+#+sbcl
+(progn
+  #-win32
+  (defun get-host-name ()
+    (sb-unix:unix-gethostname))
+
+  ;; we assume winsock has already been loaded, after all,
+  ;; we already loaded sb-bsd-sockets and sb-alien
+  #+win32
+  (defun get-host-name ()
+    (sb-alien:with-alien ((buf (sb-alien:array sb-alien:char 256)))
+       (let ((result (sb-alien:alien-funcall
+                      (sb-alien:extern-alien "gethostname"
+                                             (sb-alien:function sb-alien:int
+                                                                (* sb-alien:char)
+                                                                sb-alien:int))
+                      (sb-alien:cast buf (* sb-alien:char))
+                      256)))
+         (when (= result 0)
+           (sb-alien:cast buf sb-alien:c-string))))))
+
+#+(and ecl (not ecl-bytecmp))
+(progn
+  #-:wsock
+  (ffi:clines
+   "#include <errno.h>"
+   "#include <sys/socket.h>"
+   "#include <unistd.h>")
+  #+:wsock
+  (ffi:clines
+   "#ifndef FD_SETSIZE"
+   "#define FD_SETSIZE 1024"
+   "#endif"
+   "#include <winsock2.h>")
+
+  (ffi:clines
+   #+:msvc "#include <time.h>"
+   #-:msvc "#include <sys/time.h>"
+   "#include <ecl/ecl-inl.h>")
+#|
+  #+:prefixed-api
+  (ffi:clines
+   "#define CONS(x, y) ecl_cons((x), (y))"
+   "#define MAKE_INTEGER(x) ecl_make_integer((x))")
+  #-:prefixed-api
+  (ffi:clines
+   "#define CONS(x, y) make_cons((x), (y))"
+   "#define MAKE_INTEGER(x) make_integer((x))")
+|#
+
+  (defun cerrno ()
+    (ffi:c-inline () () :int
+     "errno" :one-liner t))
+
+  (defun fd-setsize ()
+    (ffi:c-inline () () :fixnum
+     "FD_SETSIZE" :one-liner t))
+
+  (defun fdset-alloc ()
+    (ffi:c-inline () () :pointer-void
+     "ecl_alloc_atomic(sizeof(fd_set))" :one-liner t))
+
+  (defun fdset-zero (fdset)
+    (ffi:c-inline (fdset) (:pointer-void) :void
+     "FD_ZERO((fd_set*)#0)" :one-liner t))
+
+  (defun fdset-set (fdset fd)
+    (ffi:c-inline (fdset fd) (:pointer-void :fixnum) :void
+     "FD_SET(#1,(fd_set*)#0)" :one-liner t))
+
+  (defun fdset-clr (fdset fd)
+    (ffi:c-inline (fdset fd) (:pointer-void :fixnum) :void
+     "FD_CLR(#1,(fd_set*)#0)" :one-liner t))
+
+  (defun fdset-fd-isset (fdset fd)
+    (ffi:c-inline (fdset fd) (:pointer-void :fixnum) :bool
+     "FD_ISSET(#1,(fd_set*)#0)" :one-liner t))
+
+  (declaim (inline cerrno
+                   fd-setsize
+                   fdset-alloc
+                   fdset-zero
+                   fdset-set
+                   fdset-clr
+                   fdset-fd-isset))
+
+  (defun get-host-name ()
+    (ffi:c-inline
+     () () :object
+     "{ char *buf = (char *) ecl_alloc_atomic(257);
+
+        if (gethostname(buf,256) == 0)
+          @(return) = make_simple_base_string(buf);
+        else
+          @(return) = Cnil;
+      }" :one-liner nil :side-effects nil))
+
+  (defun read-select (wl to-secs &optional (to-musecs 0))
+    (let* ((sockets (wait-list-waiters wl))
+           (rfds (wait-list-%wait wl))
+           (max-fd (reduce #'(lambda (x y)
+                               (let ((sy (sb-bsd-sockets:socket-file-descriptor
+                                          (socket y))))
+                                 (if (< x sy) sy x)))
+                           (cdr sockets)
+                           :initial-value (sb-bsd-sockets:socket-file-descriptor
+                                           (socket (car sockets))))))
+      (fdset-zero rfds)
+      (dolist (sock sockets)
+        (fdset-set rfds (sb-bsd-sockets:socket-file-descriptor
+                         (socket sock))))
+      (let ((count
+             (ffi:c-inline (to-secs to-musecs rfds max-fd)
+                           (t :unsigned-int :pointer-void :int)
+                           :int
+      "
+          int count;
+          struct timeval tv;
+
+          if (#0 != Cnil) {
+            tv.tv_sec = fixnnint(#0);
+            tv.tv_usec = #1;
+          }
+        @(return) = select(#3 + 1, (fd_set*)#2, NULL, NULL,
+                           (#0 != Cnil) ? &tv : NULL);
+" :one-liner nil)))
+        (cond
+          ((= 0 count)
+           (values nil nil))
+          ((< count 0)
+           ;; check for EINTR and EAGAIN; these should not err
+           (values nil (cerrno)))
+          (t
+           (dolist (sock sockets)
+             (when (fdset-fd-isset rfds (sb-bsd-sockets:socket-file-descriptor
+                                         (socket sock)))
+               (setf (state sock) :READ))))))))
+) ; progn
+
+(defun map-socket-error (sock-err)
+  (map-errno-error (sb-bsd-sockets::socket-error-errno sock-err)))
+
+(defparameter +sbcl-condition-map+
+  '((interrupted-error . interrupted-condition)))
+
+(defparameter +sbcl-error-map+
+  `((sb-bsd-sockets:address-in-use-error . address-in-use-error)
+    (sb-bsd-sockets::no-address-error . address-not-available-error)
+    (sb-bsd-sockets:bad-file-descriptor-error . bad-file-descriptor-error)
+    (sb-bsd-sockets:connection-refused-error . connection-refused-error)
+    (sb-bsd-sockets:invalid-argument-error . invalid-argument-error)
+    (sb-bsd-sockets:no-buffers-error . no-buffers-error)
+    (sb-bsd-sockets:operation-not-supported-error
+     . operation-not-supported-error)
+    (sb-bsd-sockets:operation-not-permitted-error
+     . operation-not-permitted-error)
+    (sb-bsd-sockets:protocol-not-supported-error
+     . protocol-not-supported-error)
+    #-ecl
+    (sb-bsd-sockets:unknown-protocol
+     . protocol-not-supported-error)
+    (sb-bsd-sockets:socket-type-not-supported-error
+     . socket-type-not-supported-error)
+    (sb-bsd-sockets:network-unreachable-error . network-unreachable-error)
+    (sb-bsd-sockets:operation-timeout-error . timeout-error)
+    #-ecl
+    (sb-sys:io-timeout . timeout-error)
+    #+sbcl
+    (sb-ext:timeout . timeout-error)
+    (sb-bsd-sockets:socket-error . ,#'map-socket-error)
+
+    ;; Nameservice errors: mapped to unknown-error
+    #-ecl #-ecl #-ecl
+    (sb-bsd-sockets:no-recovery-error . ns-no-recovery-error)
+    (sb-bsd-sockets:try-again-error . ns-try-again-condition)
+    (sb-bsd-sockets:host-not-found-error . ns-host-not-found-error)))
+
+(defun handle-condition (condition &optional (socket nil))
+  "Dispatch correct usocket condition."
+  (typecase condition
+    (serious-condition (let* ((usock-error (cdr (assoc (type-of condition)
+                                           +sbcl-error-map+)))
+                  (usock-error (if (functionp usock-error)
+                                   (funcall usock-error condition)
+                                 usock-error)))
+             (when usock-error
+                 (error usock-error :socket socket))))
+    (condition (let* ((usock-cond (cdr (assoc (type-of condition)
+                                              +sbcl-condition-map+)))
+                      (usock-cond (if (functionp usock-cond)
+                                      (funcall usock-cond condition)
+                                    usock-cond)))
+                 (if usock-cond
+                     (signal usock-cond :socket socket))))))
+
+;;; "The socket stream ends up with a bogus name as it is created before
+;;; the socket is connected, making things harder to debug than they need
+;;; to be." -- Nikodemus Siivola <nikodemus@random-state.net>
+
+(defvar *dummy-stream*
+  (let ((stream (make-broadcast-stream)))
+    (close stream)
+    stream))
+
+;;; Amusingly, neither SBCL's own, nor GBBopen's WITH-TIMEOUT is asynch
+;;; unwind safe. The one I posted is -- that's what the WITHOUT-INTERRUPTS
+;;; and WITH-LOCAL-INTERRUPTS were for. :) But yeah, it's miles saner than
+;;; the SB-EXT:WITH-TIMEOUT. -- Nikodemus Siivola <nikodemus@random-state.net>
+
+#+(and sbcl (not win32))
+(defmacro %with-timeout ((seconds timeout-form) &body body)
+  "Runs BODY as an implicit PROGN with timeout of SECONDS. If
+timeout occurs before BODY has finished, BODY is unwound and
+TIMEOUT-FORM is executed with its values returned instead.
+
+Note that BODY is unwound asynchronously when a timeout occurs,
+so unless all code executed during it -- including anything
+down the call chain -- is asynch unwind safe, bad things will
+happen. Use with care."
+  (let ((exec (gensym)) (unwind (gensym)) (timer (gensym))
+       (timeout (gensym)) (block (gensym)))
+    `(block ,block
+       (tagbody
+         (flet ((,unwind ()
+                  (go ,timeout))
+                (,exec ()
+                  ,@body))
+           (declare (dynamic-extent #',exec #',unwind))
+           (let ((,timer (sb-ext:make-timer #',unwind)))
+             (declare (dynamic-extent ,timer))
+             (sb-sys:without-interrupts
+                 (unwind-protect
+                      (progn
+                        (sb-ext:schedule-timer ,timer ,seconds)
+                        (return-from ,block
+                          (sb-sys:with-local-interrupts
+                              (,exec))))
+                   (sb-ext:unschedule-timer ,timer)))))
+         ,timeout
+         (return-from ,block ,timeout-form)))))
+
+(defun get-hosts-by-name (name)
+  (with-mapped-conditions ()
+    (multiple-value-bind (host4 host6)
+        (sb-bsd-sockets:get-host-by-name name)
+      (let ((addr4 (when host4
+                     (sb-bsd-sockets::host-ent-addresses host4)))
+            (addr6 (when host6
+                     (sb-bsd-sockets::host-ent-addresses host6))))
+        (append addr4 addr6)))))
+
+(defun socket-connect (host port &key (protocol :stream) (element-type 'character)
+                       timeout deadline (nodelay t nodelay-specified)
+                       local-host local-port
+                      &aux
+                      (sockopt-tcp-nodelay-p
+                       (fboundp 'sb-bsd-sockets::sockopt-tcp-nodelay)))
+  (when deadline (unsupported 'deadline 'socket-connect))
+  #+ecl
+  (when timeout (unsupported 'timeout 'socket-connect))
+  (when (and nodelay-specified
+             ;; 20080802: ECL added this function to its sockets
+             ;; package today. There's no guarantee the functions
+             ;; we need are available, but we can make sure not to
+             ;; call them if they aren't
+             (not (eq nodelay :if-supported))
+             (not sockopt-tcp-nodelay-p))
+    (unsupported 'nodelay 'socket-connect))
+  (when (eq nodelay :if-supported)
+    (setf nodelay t))
+
+  (let* ((remote (when host
+                   (car (get-hosts-by-name (host-to-hostname host)))))
+         (local (when local-host
+                  (car (get-hosts-by-name (host-to-hostname local-host)))))
+         (ipv6 (or (and remote (= 16 (length remote)))
+                   (and local (= 16 (length local)))))
+         (socket (make-instance #+sbcl (if ipv6
+                                           'sb-bsd-sockets::inet6-socket
+                                           'sb-bsd-sockets:inet-socket)
+                                #+ecl 'sb-bsd-sockets:inet-socket
+                                :type protocol
+                                :protocol (case protocol
+                                            (:stream :tcp)
+                                            (:datagram :udp))))
+         usocket
+         ok)
+
+    (unwind-protect
+         (progn
+           (ecase protocol
+             (:stream
+              ;; If make a real socket stream before the socket is
+              ;; connected, it gets a misleading name so supply a
+              ;; dummy value to start with.
+              (setf usocket (make-stream-socket :socket socket :stream *dummy-stream*))
+              ;; binghe: use SOCKOPT-TCP-NODELAY as internal symbol
+              ;;         to pass compilation on ECL without it.
+              (when (and nodelay-specified sockopt-tcp-nodelay-p)
+                (setf (sb-bsd-sockets::sockopt-tcp-nodelay socket) nodelay))
+              (when (or local-host local-port)
+                (sb-bsd-sockets:socket-bind socket
+                                            (if ipv6
+                                                (or local (ipv6-host-to-vector "::0"))
+                                                (or local (host-to-vector-quad *wildcard-host*)))
+                                            (or local-port *auto-port*)))
+
+              (with-mapped-conditions (usocket)
+               #+(and sbcl (not win32))
+               (labels ((connect ()
+                           (sb-bsd-sockets:socket-connect socket remote port)))
+                 (if timeout
+                     (%with-timeout (timeout (error 'sb-ext:timeout)) (connect))
+                     (connect)))
+               #+(or ecl (and sbcl win32))
+               (sb-bsd-sockets:socket-connect socket remote port)
+                ;; Now that we're connected make the stream.
+                (setf (socket-stream usocket)
+                      (sb-bsd-sockets:socket-make-stream socket
+                        :input t :output t :buffering :full
+                       :element-type element-type
+                       ;; Robert Brown <robert.brown@gmail.com> said on Aug 4, 2011:
+                       ;; ... This means that SBCL streams created by usocket have a true
+                       ;; serve-events property.  When writing large amounts of data to several
+                       ;; streams, the kernel will eventually stop accepting data from SBCL.
+                       ;; When this happens, SBCL either waits for I/O to be possible on
+                       ;; the file descriptor it's writing to or queues the data to be flushed later.
+                       ;; Because usocket streams specify serve-events as true, SBCL
+                       ;; always queues.  Instead, it should wait for I/O to be available and
+                       ;; write the remaining data to the socket.  That's what serve-events
+                       ;; equal to NIL gets you.
+                       ;;
+                       ;; Nikodemus Siivola <nikodemus@random-state.net> said on Aug 8, 2011:
+                       ;; It's set to T for purely historical reasons, and will soon change to
+                       ;; NIL in SBCL. (The docstring has warned of T being a temporary default
+                       ;; for as long as the :SERVE-EVENTS keyword argument has existed.)
+                       :serve-events nil))))
+             (:datagram
+              (when (or local-host local-port)
+                (sb-bsd-sockets:socket-bind socket
+                                            (if ipv6
+                                                (or local (ipv6-host-to-vector "::0"))
+                                                (or local (host-to-vector-quad *wildcard-host*)))
+                                            (or local-port *auto-port*)))
+              (setf usocket (make-datagram-socket socket))
+              (when (and host port)
+                (with-mapped-conditions (usocket)
+                  (sb-bsd-sockets:socket-connect socket remote port)
+                  (setf (connected-p usocket) t)))))
+           (setf ok t))
+      ;; Clean up in case of an error.
+      (unless ok
+        (sb-bsd-sockets:socket-close socket :abort t)))
+    usocket))
+
+(defun socket-listen (host port
+                           &key reuseaddress
+                           (reuse-address nil reuse-address-supplied-p)
+                           (backlog 5)
+                           (element-type 'character))
+  (let* ((local (when host
+                  (car (get-hosts-by-name (host-to-hostname host)))))
+         (ipv6 (and local (= 16 (length local))))
+         (reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
+         (ip #+sbcl (if (and local (not (eq host *wildcard-host*)))
+                        local
+                        (hbo-to-vector-quad sb-bsd-sockets-internal::inaddr-any))
+             #+ecl (host-to-vector-quad host))
+         (sock (make-instance #+sbcl (if ipv6
+                                         'sb-bsd-sockets::inet6-socket
+                                         'sb-bsd-sockets:inet-socket)
+                              #+ecl 'sb-bsd-sockets:inet-socket
+                              :type :stream
+                              :protocol :tcp)))
+    (handler-case
+        (with-mapped-conditions ()
+          (setf (sb-bsd-sockets:sockopt-reuse-address sock) reuseaddress)
+          (sb-bsd-sockets:socket-bind sock ip port)
+          (sb-bsd-sockets:socket-listen sock backlog)
+          (make-stream-server-socket sock :element-type element-type))
+      (t (c)
+        ;; Make sure we don't leak filedescriptors
+        (sb-bsd-sockets:socket-close sock)
+        (error c)))))
+
+;;; "2. SB-BSD-SOCKETS:SOCKET-ACCEPT method returns NIL for EAGAIN/EINTR,
+;;; instead of raising a condition. It's always possible for
+;;; SOCKET-ACCEPT on non-blocking socket to fail, even after the socket
+;;; was detected to be ready: connection might be reset, for example.
+;;;
+;;; "I had to redefine SOCKET-ACCEPT method of STREAM-SERVER-USOCKET to
+;;; handle this situation. Here is the redefinition:" -- Anton Kovalenko <anton@sw4me.com>
+
+(defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
+  (with-mapped-conditions (usocket)
+    (let ((socket (sb-bsd-sockets:socket-accept (socket usocket))))
+      (when socket
+        (prog1
+         (make-stream-socket
+          :socket socket
+          :stream (sb-bsd-sockets:socket-make-stream
+                   socket
+                   :input t :output t :buffering :full
+                   :element-type (or element-type
+                                     (element-type usocket))))
+
+          ;; next time wait for event again if we had EAGAIN/EINTR
+          ;; or else we'd enter a tight loop of failed accepts
+          #+win32
+          (setf (%ready-p usocket) nil))))))
+
+;; Sockets and their associated streams are modelled as
+;; different objects. Be sure to close the stream (which
+;; closes the socket too) when closing a stream-socket.
+(defmethod socket-close ((usocket usocket))
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+    (sb-bsd-sockets:socket-close (socket usocket))))
+
+(defmethod socket-close ((usocket stream-usocket))
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+    (close (socket-stream usocket))))
+
+#+sbcl
+(defmethod socket-shutdown ((usocket stream-usocket) direction)
+  (with-mapped-conditions (usocket)
+    (sb-bsd-sockets::socket-shutdown (socket usocket) :direction direction)))
+
+#+ecl
+(defmethod socket-shutdown ((usocket stream-usocket) direction)
+  (let ((sock-fd (sb-bsd-sockets:socket-file-descriptor (socket usocket)))
+        (direction-flag (ecase direction
+                          (:input 0)
+                          (:output 1))))
+    (unless (zerop (ffi:c-inline (sock-fd direction-flag) (:int :int) :int
+                               "shutdown(#0, #1)" :one-liner t))
+      (error (map-errno-error (cerrno))))))
+
+(defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
+  (let ((remote (when host
+                  (car (get-hosts-by-name (host-to-hostname host))))))
+    (with-mapped-conditions (usocket)
+      (let* ((s (socket usocket))
+             (dest (if (and host port) (list remote port) nil))
+             (real-buffer (if (zerop offset)
+                              buffer
+                              (subseq buffer offset (+ offset size)))))
+        (sb-bsd-sockets:socket-send s real-buffer size :address dest)))))
+
+(defmethod socket-receive ((socket datagram-usocket) buffer length
+                          &key (element-type '(unsigned-byte 8)))
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+                  (integer 0)                          ; size
+                  (simple-array (unsigned-byte 8) (*)) ; host
+                  (unsigned-byte 16)))                 ; port
+  (with-mapped-conditions (socket)
+    (let ((s (socket socket)))
+      (sb-bsd-sockets:socket-receive s buffer length :element-type element-type))))
+
+(defmethod get-local-name ((usocket usocket))
+  (sb-bsd-sockets:socket-name (socket usocket)))
+
+(defmethod get-peer-name ((usocket stream-usocket))
+  (sb-bsd-sockets:socket-peername (socket usocket)))
+
+(defmethod get-local-address ((usocket usocket))
+  (nth-value 0 (get-local-name usocket)))
+
+(defmethod get-peer-address ((usocket stream-usocket))
+  (nth-value 0 (get-peer-name usocket)))
+
+(defmethod get-local-port ((usocket usocket))
+  (nth-value 1 (get-local-name usocket)))
+
+(defmethod get-peer-port ((usocket stream-usocket))
+  (nth-value 1 (get-peer-name usocket)))
+
+(defun get-host-by-address (address)
+  (with-mapped-conditions ()
+    (sb-bsd-sockets::host-ent-name
+        (sb-bsd-sockets:get-host-by-address address))))
+
+#+(and sbcl (not win32))
+(progn
+  (defun %setup-wait-list (wait-list)
+    (declare (ignore wait-list)))
+
+  (defun %add-waiter (wait-list waiter)
+    (push (socket waiter) (wait-list-%wait wait-list)))
+
+  (defun %remove-waiter (wait-list waiter)
+    (setf (wait-list-%wait wait-list)
+          (remove (socket waiter) (wait-list-%wait wait-list))))
+
+  (defun wait-for-input-internal (sockets &key timeout)
+    (with-mapped-conditions ()
+      (sb-alien:with-alien ((rfds (sb-alien:struct sb-unix:fd-set)))
+         (sb-unix:fd-zero rfds)
+         (dolist (socket (wait-list-%wait sockets))
+           (sb-unix:fd-set
+            (sb-bsd-sockets:socket-file-descriptor socket)
+            rfds))
+         (multiple-value-bind
+             (secs musecs)
+             (split-timeout (or timeout 1))
+           (multiple-value-bind
+               (count err)
+               (sb-unix:unix-fast-select
+                (1+ (reduce #'max (wait-list-%wait sockets)
+                            :key #'sb-bsd-sockets:socket-file-descriptor))
+                (sb-alien:addr rfds) nil nil
+                (when timeout secs) (when timeout musecs))
+            (if (null count)
+                (unless (= err sb-unix:EINTR)
+                  (error (map-errno-error err)))
+                (when (< 0 count)
+                  ;; process the result...
+                   (dolist (x (wait-list-waiters sockets))
+                     (when (sb-unix:fd-isset
+                            (sb-bsd-sockets:socket-file-descriptor
+                             (socket x))
+                            rfds)
+                       (setf (state x) :READ))))))))))
+) ; progn
+
+;;; WAIT-FOR-INPUT support for SBCL on Windows platform (Chun Tian (binghe))
+;;; Based on LispWorks version written by Erik Huelsmann.
+
+#+win32 ; shared by ECL and SBCL
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defconstant +wsa-wait-failed+ #xffffffff)
+  (defconstant +wsa-wait-event-0+ 0)
+  (defconstant +wsa-wait-timeout+ 258))
+
+#+win32 ; shared by ECL and SBCL
+(progn
+  (defconstant fd-read 1)
+  (defconstant fd-read-bit 0)
+  (defconstant fd-write 2)
+  (defconstant fd-write-bit 1)
+  (defconstant fd-oob 4)
+  (defconstant fd-oob-bit 2)
+  (defconstant fd-accept 8)
+  (defconstant fd-accept-bit 3)
+  (defconstant fd-connect 16)
+  (defconstant fd-connect-bit 4)
+  (defconstant fd-close 32)
+  (defconstant fd-close-bit 5)
+  (defconstant fd-qos 64)
+  (defconstant fd-qos-bit 6)
+  (defconstant fd-group-qos 128)
+  (defconstant fd-group-qos-bit 7)
+  (defconstant fd-routing-interface 256)
+  (defconstant fd-routing-interface-bit 8)
+  (defconstant fd-address-list-change 512)
+  (defconstant fd-address-list-change-bit 9)
+  (defconstant fd-max-events 10)
+  (defconstant fionread 1074030207)
+
+  ;; Note: for ECL, socket-handle will return raw Windows Handle,
+  ;;       while SBCL returns OSF Handle instead.
+  (defun socket-handle (usocket)
+    (sb-bsd-sockets:socket-file-descriptor (socket usocket)))
+
+  (defun socket-ready-p (socket)
+    (if (typep socket 'stream-usocket)
+        (plusp (bytes-available-for-read socket))
+      (%ready-p socket)))
+
+  (defun waiting-required (sockets)
+    (notany #'socket-ready-p sockets))
+
+  (defun raise-usock-err (errno &optional socket)
+    (error 'unknown-error
+           :socket socket
+           :real-error errno))
+
+  (defun wait-for-input-internal (wait-list &key timeout)
+    (when (waiting-required (wait-list-waiters wait-list))
+      (let ((rv (wsa-wait-for-multiple-events 1 (wait-list-%wait wait-list)
+                                              nil (truncate (* 1000 (if timeout timeout 0))) nil)))
+        (ecase rv
+          ((#.+wsa-wait-event-0+)
+           (update-ready-and-state-slots (wait-list-waiters wait-list)))
+          ((#.+wsa-wait-timeout+)) ; do nothing here
+          ((#.+wsa-wait-failed+)
+           (maybe-wsa-error rv))))))
+
+  (defun %add-waiter (wait-list waiter)
+    (let ((events (etypecase waiter
+                    (stream-server-usocket (logior fd-connect fd-accept fd-close))
+                    (stream-usocket (logior fd-read))
+                    (datagram-usocket (logior fd-read)))))
+      (maybe-wsa-error
+       (wsa-event-select (os-socket-handle waiter) (os-wait-list-%wait wait-list) events)
+       waiter)))
+
+  (defun %remove-waiter (wait-list waiter)
+    (maybe-wsa-error
+     (wsa-event-select (os-socket-handle waiter) (os-wait-list-%wait wait-list) 0)
+     waiter))
+) ; progn
+
+#+(and sbcl win32)
+(progn
+  ;; "SOCKET is defined as intptr_t in Windows headers; however, WS-SOCKET
+  ;; is defined as unsigned-int, i.e. 32-bit even on 64-bit platform.  It
+  ;; seems to be a good thing to redefine WS-SOCKET as SB-ALIEN:SIGNED,
+  ;; which is always machine word-sized (exactly as intptr_t;
+  ;; N.B. as of Windows/x64, long and signed-long are 32-bit, and thus not
+  ;; enough -- potentially)."
+  ;; -- Anton Kovalenko <anton@sw4me.com>, Mar 22, 2011
+  (sb-alien:define-alien-type ws-socket sb-alien:signed)
+
+  (sb-alien:define-alien-type ws-dword sb-alien:unsigned-long)
+  (sb-alien:define-alien-type ws-event sb-alien::hinstance)
+
+  (sb-alien:define-alien-type nil
+    (sb-alien:struct wsa-network-events
+      (network-events sb-alien:long)
+      (error-code (array sb-alien:int 10)))) ; 10 = fd-max-events
+
+  (sb-alien:define-alien-routine ("WSACreateEvent" wsa-event-create)
+      ws-event) ; return type only
+
+  (sb-alien:define-alien-routine ("WSACloseEvent" wsa-event-close)
+      (boolean #.sb-vm::n-machine-word-bits)
+    (event-object ws-event))
+
+  (sb-alien:define-alien-routine ("WSAEnumNetworkEvents" wsa-enum-network-events)
+      sb-alien:int
+    (socket ws-socket)
+    (event-object ws-event)
+    (network-events (* (sb-alien:struct wsa-network-events))))
+
+  (sb-alien:define-alien-routine ("WSAEventSelect" wsa-event-select)
+      sb-alien:int
+    (socket ws-socket)
+    (event-object ws-event)
+    (network-events sb-alien:long))
+
+  (sb-alien:define-alien-routine ("WSAWaitForMultipleEvents" wsa-wait-for-multiple-events)
+      ws-dword
+    (number-of-events ws-dword)
+    (events (* ws-event))
+    (wait-all-p (boolean #.sb-vm::n-machine-word-bits))
+    (timeout ws-dword)
+    (alertable-p (boolean #.sb-vm::n-machine-word-bits)))
+
+  (sb-alien:define-alien-routine ("ioctlsocket" wsa-ioctlsocket)
+      sb-alien:int
+    (socket ws-socket)
+    (cmd sb-alien:long)
+    (argp (* sb-alien:unsigned-long)))
+
+  (defun maybe-wsa-error (rv &optional socket)
+    (unless (zerop rv)
+      (raise-usock-err (sockint::wsa-get-last-error) socket)))
+
+  (defun os-socket-handle (usocket)
+    (sb-bsd-sockets:socket-file-descriptor (socket usocket)))
+
+  (defun bytes-available-for-read (socket)
+    (sb-alien:with-alien ((int-ptr sb-alien:unsigned-long))
+      (maybe-wsa-error (wsa-ioctlsocket (os-socket-handle socket) fionread (sb-alien:addr int-ptr))
+                       socket)
+      (prog1 int-ptr
+        (when (plusp int-ptr)
+          (setf (state socket) :read)))))
+
+  (defun map-network-events (func network-events)
+    (let ((event-map (sb-alien:slot network-events 'network-events))
+          (error-array (sb-alien:slot network-events 'error-code)))
+      (unless (zerop event-map)
+        (dotimes (i fd-max-events)
+          (unless (zerop (ldb (byte 1 i) event-map)) ;;### could be faster with ash and logand?
+            (funcall func (sb-alien:deref error-array i)))))))
+
+  (defun update-ready-and-state-slots (sockets)
+    (dolist (socket sockets)
+      (if (%ready-p socket)
+          (progn
+            (setf (state socket) :READ))
+        (sb-alien:with-alien ((network-events (sb-alien:struct wsa-network-events)))
+          (let ((rv (wsa-enum-network-events (os-socket-handle socket) 0
+                                             (sb-alien:addr network-events))))
+            (if (zerop rv)
+                (map-network-events
+                 #'(lambda (err-code)
+                     (if (zerop err-code)
+                         (progn
+                           (setf (state socket) :READ)
+                           (when (stream-server-usocket-p socket)
+                             (setf (%ready-p socket) t)))
+                       (raise-usock-err err-code socket)))
+                 network-events)
+              (maybe-wsa-error rv socket)))))))
+
+  (defun os-wait-list-%wait (wait-list)
+    (sb-alien:deref (wait-list-%wait wait-list)))
+
+  (defun (setf os-wait-list-%wait) (value wait-list)
+    (setf (sb-alien:deref (wait-list-%wait wait-list)) value))
+
+  ;; "Event handles are leaking in current SBCL backend implementation,
+  ;; because of SBCL-unfriendly usage of finalizers.
+  ;;
+  ;; "SBCL never calls a finalizer that closes over a finalized object: a
+  ;; reference from that closure prevents its collection forever. That's
+  ;; the case with USOCKET in %SETUP-WAIT-LIST.
+  ;;
+  ;; "I use the following redefinition of %SETUP-WAIT-LIST: 
+  ;;
+  ;; "Of course it may be rewritten with more clarity, but you can see the
+  ;; core idea: I'm closing over those components of WAIT-LIST that I need
+  ;; for finalization, not the wait-list itself. With the original
+  ;; %SETUP-WAIT-LIST, hunchentoot stops working after ~100k accepted
+  ;; connections; it doesn't happen with redefined %SETUP-WAIT-LIST."
+  ;;
+  ;; -- Anton Kovalenko <anton@sw4me.com>, Mar 22, 2011
+
+  (defun %setup-wait-list (wait-list)
+    (setf (wait-list-%wait wait-list) (sb-alien:make-alien ws-event))
+    (setf (os-wait-list-%wait wait-list) (wsa-event-create))
+    (sb-ext:finalize wait-list
+                    (let ((event-handle (os-wait-list-%wait wait-list))
+                          (alien (wait-list-%wait wait-list)))
+                      #'(lambda ()
+                          (wsa-event-close event-handle)
+                          (unless (null alien)
+                            (sb-alien:free-alien alien))))))
+
+) ; progn
+
+#+(and ecl (not win32))
+(progn
+  (defun wait-for-input-internal (wl &key timeout)
+    (with-mapped-conditions ()
+      (multiple-value-bind
+            (secs usecs)
+          (split-timeout (or timeout 1))
+        (multiple-value-bind
+              (result-fds err)
+            (read-select wl (when timeout secs) usecs)
+          (unless (null err)
+            (error (map-errno-error err)))))))
+
+  (defun %setup-wait-list (wl)
+    (setf (wait-list-%wait wl)
+          (fdset-alloc)))
+
+  (defun %add-waiter (wl w)
+    (declare (ignore wl w)))
+
+  (defun %remove-waiter (wl w)
+    (declare (ignore wl w)))
+) ; progn
+
+#+(and ecl win32 (not ecl-bytecmp))
+(progn
+  (defun maybe-wsa-error (rv &optional syscall)
+    (unless (zerop rv)
+      (sb-bsd-sockets::socket-error syscall)))
+
+  (defun %setup-wait-list (wl)
+    (setf (wait-list-%wait wl)
+          (ffi:c-inline () () :int
+           "WSAEVENT event;
+            event = WSACreateEvent();
+            @(return) = event;")))
+
+  (defun %add-waiter (wait-list waiter)
+    (let ((events (etypecase waiter
+                    (stream-server-usocket (logior fd-connect fd-accept fd-close))
+                    (stream-usocket (logior fd-read))
+                    (datagram-usocket (logior fd-read)))))
+      (maybe-wsa-error
+       (ffi:c-inline ((socket-handle waiter) (wait-list-%wait wait-list) events)
+                     (:fixnum :fixnum :fixnum) :fixnum
+        "int result;
+         result = WSAEventSelect((SOCKET)#0, (WSAEVENT)#1, (long)#2);
+         @(return) = result;")
+       '%add-waiter)))
+
+  (defun %remove-waiter (wait-list waiter)
+    (maybe-wsa-error
+     (ffi:c-inline ((socket-handle waiter) (wait-list-%wait wait-list))
+                   (:fixnum :fixnum) :fixnum
+      "int result;
+       result = WSAEventSelect((SOCKET)#0, (WSAEVENT)#1, 0L);
+       @(return) = result;")
+     '%remove-waiter))
+
+  ;; TODO: how to handle error (result) in this call?
+  (declaim (inline %bytes-available-for-read))
+  (defun %bytes-available-for-read (socket)
+    (ffi:c-inline ((socket-handle socket)) (:fixnum) :fixnum
+     "u_long nbytes;
+      int result;
+      nbytes = 0L;
+      result = ioctlsocket((SOCKET)#0, FIONREAD, &nbytes);
+      @(return) = nbytes;"))
+
+  (defun bytes-available-for-read (socket)
+    (let ((nbytes (%bytes-available-for-read socket)))
+      (when (plusp nbytes)
+       (setf (state socket) :read))
+      nbytes))
+
+  (defun update-ready-and-state-slots (sockets)
+    (dolist (socket sockets)
+      (if (%ready-p socket)
+          (setf (state socket) :READ)
+        (let ((events (etypecase socket
+                        (stream-server-usocket (logior fd-connect fd-accept fd-close))
+                        (stream-usocket (logior fd-read))
+                        (datagram-usocket (logior fd-read)))))
+          ;; TODO: check the iErrorCode array
+          (multiple-value-bind (valid-p ready-p)
+              (ffi:c-inline ((socket-handle socket) events) (:fixnum :fixnum)
+                                                            (values :bool :bool)
+                "WSANETWORKEVENTS network_events;
+                 int i, result;
+                 result = WSAEnumNetworkEvents((SOCKET)#0, 0, &network_events);
+                 if (!result) {
+                   @(return 0) = Ct;
+                   @(return 1) = (#1 & network_events.lNetworkEvents)? Ct : Cnil;
+                 } else {
+                   @(return 0) = Cnil;
+                   @(return 1) = Cnil;
+                 }")
+            (if valid-p
+                (when ready-p
+                  (setf (state socket) :READ)
+                  (when (stream-server-usocket-p socket)
+                    (setf (%ready-p socket) t)))
+              (sb-bsd-sockets::socket-error 'update-ready-and-state-slots)))))))
+
+  (defun wait-for-input-internal (wait-list &key timeout)
+    (when (waiting-required (wait-list-waiters wait-list))
+      (let ((rv (ffi:c-inline ((wait-list-%wait wait-list) (truncate (* 1000 timeout)))
+                              (:fixnum :fixnum) :fixnum
+                 "DWORD result;
+                  WSAEVENT events[1];
+                  events[0] = (WSAEVENT)#0;
+                  result = WSAWaitForMultipleEvents(1, events, NULL, #1, NULL);
+                  @(return) = result;")))
+        (ecase rv
+          ((#.+wsa-wait-event-0+)
+           (update-ready-and-state-slots (wait-list-waiters wait-list)))
+          ((#.+wsa-wait-timeout+)) ; do nothing here
+          ((#.+wsa-wait-failed+)
+           (sb-bsd-sockets::socket-error 'wait-for-input-internal))))))
+
+) ; progn
diff --git a/deps/usocket/backend/scl.lisp b/deps/usocket/backend/scl.lisp
new file mode 100644 (file)
index 0000000..2689cda
--- /dev/null
@@ -0,0 +1,273 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+(defparameter +scl-error-map+
+  (append +unix-errno-condition-map+
+          +unix-errno-error-map+))
+
+(defun scl-map-socket-error (err &key condition socket)
+  (let ((usock-err (cdr (assoc err +scl-error-map+ :test #'member))))
+    (cond (usock-err
+       (if (subtypep usock-err 'error)
+           (error usock-err :socket socket)
+           (signal usock-err :socket socket)))
+      (t
+       (error 'unknown-error
+          :socket socket
+          :real-error condition)))))
+
+(defun handle-condition (condition &optional (socket nil))
+  "Dispatch correct usocket condition."
+  (typecase condition
+    (ext::socket-error
+     (scl-map-socket-error (ext::socket-errno condition)
+                          :socket socket
+                          :condition condition))))
+
+(defun socket-connect (host port &key (protocol :stream) (element-type 'character)
+                       timeout deadline (nodelay t nodelay-specified)
+                      (local-host nil local-host-p)
+                      (local-port nil local-port-p)
+                      &aux
+                      (patch-udp-p (fboundp 'ext::inet-socket-send-to)))
+  (when (and nodelay-specified 
+             (not (eq nodelay :if-supported)))
+    (unsupported 'nodelay 'socket-connect))
+  (when deadline (unsupported 'deadline 'socket-connect))
+  (when timeout (unsupported 'timeout 'socket-connect))
+  (when (and local-host-p (not patch-udp-p))
+     (unsupported 'local-host 'socket-connect :minimum "1.3.9"))
+  (when (and local-port-p (not patch-udp-p))
+     (unsupported 'local-port 'socket-connect :minimum "1.3.9"))
+
+  (let ((socket))
+    (ecase protocol
+      (:stream
+       (setf socket (let ((args (list (host-to-hbo host) port :kind protocol)))
+                     (when (and patch-udp-p (or local-host-p local-port-p))
+                       (nconc args (list :local-host (when local-host
+                                                       (host-to-hbo local-host))
+                                         :local-port local-port)))
+                     (with-mapped-conditions (socket)
+                       (apply #'ext:connect-to-inet-socket args))))
+       (let ((stream (sys:make-fd-stream socket :input t :output t
+                                        :element-type element-type
+                                        :buffering :full)))
+        (make-stream-socket :socket socket :stream stream)))
+      (:datagram
+       (when (not patch-udp-p)
+        (error 'unsupported
+               :feature '(protocol :datagram)
+               :context 'socket-connect
+               :minumum "1.3.9"))
+       (setf socket
+            (if (and host port)
+                (let ((args (list (host-to-hbo host) port :kind protocol)))
+                  (when (and patch-udp-p (or local-host-p local-port-p))
+                    (nconc args (list :local-host (when local-host
+                                                    (host-to-hbo local-host))
+                                      :local-port local-port)))
+                  (with-mapped-conditions (socket)
+                    (apply #'ext:connect-to-inet-socket args)))
+                (if (or local-host-p local-port-p)
+                    (with-mapped-conditions ()
+                      (ext:create-inet-listener (or local-port 0)
+                                                protocol
+                                                :host (when local-host
+                                                        (if (ip= local-host *wildcard-host*)
+                                                            0
+                                                            (host-to-hbo local-host)))))
+                    (with-mapped-conditions ()
+                      (ext:create-inet-socket protocol)))))
+       (let ((usocket (make-datagram-socket socket :connected-p (and host port t))))
+        (ext:finalize usocket #'(lambda ()
+                                  (when (%open-p usocket)
+                                    (ext:close-socket socket))))
+        usocket)))))
+
+(defun socket-listen (host port
+                           &key reuseaddress
+                           (reuse-address nil reuse-address-supplied-p)
+                           (backlog 5)
+                           (element-type 'character))
+  (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
+         (host (if (ip= host *wildcard-host*)
+                   0
+                 (host-to-hbo host)))
+         (server-sock
+          (with-mapped-conditions ()
+            (ext:create-inet-listener port :stream
+                                      :host host
+                                      :reuse-address reuseaddress
+                                      :backlog backlog))))
+   (make-stream-server-socket server-sock :element-type element-type)))
+
+(defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
+  (with-mapped-conditions (usocket)
+    (let* ((sock (ext:accept-tcp-connection (socket usocket)))
+           (stream (sys:make-fd-stream sock :input t :output t
+                                      :element-type (or element-type
+                                                        (element-type usocket))
+                                      :buffering :full)))
+      (make-stream-socket :socket sock :stream stream))))
+
+;; Sockets and their associated streams are modelled as
+;; different objects. Be sure to close the socket stream
+;; when closing stream-sockets; it makes sure buffers
+;; are flushed and the socket is closed correctly afterwards.
+(defmethod socket-close ((usocket usocket))
+  "Close socket."
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+    (ext:close-socket (socket usocket))))
+
+(defmethod socket-close ((usocket stream-usocket))
+  "Close socket."
+  (when (wait-list usocket)
+     (remove-waiter (wait-list usocket) usocket))
+  (with-mapped-conditions (usocket)
+    (close (socket-stream usocket))))
+
+(defmethod socket-close :after ((socket datagram-usocket))
+  (setf (%open-p socket) nil))
+
+(defmethod socket-shutdown ((usocket usocket) direction)
+  (declare (ignore usocket direction))
+  (unsupported "shutdown" 'socket-shutdown))
+
+(defmethod socket-send ((usocket datagram-usocket) buffer size &key host port)
+  (let ((s (socket usocket))
+       (host (if host (host-to-hbo host)))
+       (real-buffer (if (zerop offset)
+                        buffer
+                        (subseq buffer offset (+ offset size)))))
+    (multiple-value-bind (result errno)
+       (ext:inet-socket-send-to s real-buffer size
+                                :remote-host host :remote-port port)
+      (or result
+         (scl-map-socket-error errno :socket usocket)))))
+
+(defmethod socket-receive ((socket datagram-usocket) buffer length &key)
+  (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
+                  (integer 0)                          ; size
+                  (unsigned-byte 32)                   ; host
+                  (unsigned-byte 16)))                 ; port
+  (let ((s (socket socket)))
+    (let ((real-buffer (or buffer
+                          (make-array length :element-type '(unsigned-byte 8))))
+         (real-length (or length
+                          (length buffer))))
+      (multiple-value-bind (result errno remote-host remote-port)
+         (ext:inet-socket-receive-from s real-buffer real-length)
+       (if result
+           (values real-buffer result remote-host remote-port)
+           (scl-map-socket-error errno :socket socket))))))
+
+(defmethod get-local-name ((usocket usocket))
+  (multiple-value-bind (address port)
+      (with-mapped-conditions (usocket)
+        (ext:get-socket-host-and-port (socket usocket)))
+    (values (hbo-to-vector-quad address) port)))
+
+(defmethod get-peer-name ((usocket stream-usocket))
+  (multiple-value-bind (address port)
+      (with-mapped-conditions (usocket)
+        (ext:get-peer-host-and-port (socket usocket)))
+    (values (hbo-to-vector-quad address) port)))
+
+(defmethod get-local-address ((usocket usocket))
+  (nth-value 0 (get-local-name usocket)))
+
+(defmethod get-peer-address ((usocket stream-usocket))
+  (nth-value 0 (get-peer-name usocket)))
+
+(defmethod get-local-port ((usocket usocket))
+  (nth-value 1 (get-local-name usocket)))
+
+(defmethod get-peer-port ((usocket stream-usocket))
+  (nth-value 1 (get-peer-name usocket)))
+
+
+(defun get-host-by-address (address)
+  (multiple-value-bind (host errno)
+      (ext:lookup-host-entry (host-byte-order address))
+    (cond (host
+           (ext:host-entry-name host))
+          (t
+           (let ((condition (cdr (assoc errno +unix-ns-error-map+))))
+             (cond (condition
+                    (error condition :host-or-ip address))
+                   (t
+                    (error 'ns-unknown-error :host-or-ip address
+                           :real-error errno))))))))
+
+(defun get-hosts-by-name (name)
+  (multiple-value-bind (host errno)
+      (ext:lookup-host-entry name)
+    (cond (host
+           (mapcar #'hbo-to-vector-quad
+                   (ext:host-entry-addr-list host)))
+          (t
+           (let ((condition (cdr (assoc errno +unix-ns-error-map+))))
+             (cond (condition
+                    (error condition :host-or-ip name))
+                   (t
+                    (error 'ns-unknown-error :host-or-ip name
+                           :real-error errno))))))))
+
+(defun get-host-name ()
+  (unix:unix-gethostname))
+
+
+;;
+;;
+;;  WAIT-LIST part
+;;
+
+
+(defun %add-waiter (wl waiter)
+  (declare (ignore wl waiter)))
+
+(defun %remove-waiter (wl waiter)
+  (declare (ignore wl waiter)))
+
+(defun %setup-wait-list (wl)
+  (declare (ignore wl)))
+
+(defun wait-for-input-internal (wait-list &key timeout)
+  (let* ((sockets (wait-list-waiters wait-list))
+         (pollfd-size (alien:alien-size (alien:struct unix::pollfd) :bytes))
+         (nfds (length sockets))
+         (bytes (* nfds pollfd-size)))
+    (alien:with-bytes (fds-sap bytes)
+      (do ((sockets sockets (rest sockets))
+          (base 0 (+ base 8)))
+         ((endp sockets))
+       (let ((fd (socket (first sockets))))
+         (setf (sys:sap-ref-32 fds-sap base) fd)
+         (setf (sys:sap-ref-16 fds-sap (+ base 4)) unix::pollin)))
+      (multiple-value-bind (result errno)
+         (let ((thread:*thread-whostate* "Poll wait")
+               (timeout (if timeout
+                            (truncate (* timeout 1000))
+                            -1)))
+           (declare (inline unix:unix-poll))
+           (unix:unix-poll (alien:sap-alien fds-sap
+                                            (* (alien:struct unix::pollfd)))
+                           nfds timeout))
+       (cond ((not result)
+              (error "~@<Polling error: ~A~:@>"
+                     (unix:get-unix-error-msg errno)))
+             (t
+              (do ((sockets sockets (rest sockets))
+                   (base 0 (+ base 8)))
+                  ((endp sockets))
+                (let ((flags (sys:sap-ref-16 fds-sap (+ base 6))))
+                  (unless (zerop (logand flags unix::pollin))
+                    (setf (state (first sockets)) :READ))))))))))
+
diff --git a/deps/usocket/condition.lisp b/deps/usocket/condition.lisp
new file mode 100644 (file)
index 0000000..3657312
--- /dev/null
@@ -0,0 +1,238 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+;; Condition signalled by operations with unsupported arguments
+;; For trivial-sockets compatibility.
+
+(define-condition insufficient-implementation (error)
+  ((feature :initarg :feature :reader feature)
+   (context :initarg :context :reader context
+    :documentation "String designator of the public API function which
+the feature belongs to."))
+  (:documentation "The ancestor of all errors usocket may generate
+because of insufficient support from the underlying implementation
+with respect to the arguments given to `function'.
+
+One call may signal several errors, if the caller allows processing
+to continue.
+"))
+
+(define-condition unsupported (insufficient-implementation)
+  ((minimum :initarg :minimum :reader minimum
+            :documentation "Indicates the minimal version of the
+implementation required to support the requested feature."))
+  (:report (lambda (c stream)
+            (format stream "~A in ~A is unsupported."
+                    (feature c) (context c))
+            (when (minimum c)
+              (format stream " Minimum version (~A) is required."
+                      (minimum c)))))
+  (:documentation "Signalled when the underlying implementation
+doesn't allow supporting the requested feature.
+
+When you see this error, go bug your vendor/implementation developer!"))
+
+(define-condition unimplemented (insufficient-implementation)
+  ()
+  (:report (lambda (c stream)
+            (format stream "~A in ~A is unimplemented."
+                    (feature c) (context c))))
+  (:documentation "Signalled if a certain feature might be implemented,
+based on the features of the underlying implementation, but hasn't
+been implemented yet."))
+
+;; Conditions raised by sockets operations
+
+(define-condition socket-condition (condition)
+  ((socket :initarg :socket
+           :accessor usocket-socket))
+  ;;###FIXME: no slots (yet); should at least be the affected usocket...
+  (:documentation "Parent condition for all socket related conditions."))
+
+(define-condition socket-error (socket-condition error)
+  () ;; no slots (yet)
+  (:documentation "Parent error for all socket related errors"))
+
+(define-condition ns-condition (condition)
+  ((host-or-ip :initarg :host-or-ip
+               :accessor host-or-ip))
+  (:documentation "Parent condition for all name resolution conditions."))
+
+(define-condition ns-error (ns-condition error)
+  ()
+  (:documentation "Parent error for all name resolution errors."))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defun define-usocket-condition-class (class &rest parents)
+    `(progn
+       (define-condition ,class ,parents ())
+       (export ',class))))
+
+(defmacro define-usocket-condition-classes (class-list parents)
+  `(progn ,@(mapcar #'(lambda (x)
+                        (apply #'define-usocket-condition-class
+                               x parents))
+                    class-list)))
+
+;; Mass define and export our conditions
+(define-usocket-condition-classes
+  (interrupted-condition)
+  (socket-condition))
+
+(define-condition unknown-condition (socket-condition)
+  ((real-condition :initarg :real-condition
+                   :accessor usocket-real-condition))
+  (:documentation "Condition raised when there's no other - more applicable -
+condition available."))
+
+
+;; Mass define and export our errors
+(define-usocket-condition-classes
+  (address-in-use-error
+   address-not-available-error
+   bad-file-descriptor-error
+   connection-refused-error
+   connection-aborted-error
+   connection-reset-error
+   invalid-argument-error
+   no-buffers-error
+   operation-not-supported-error
+   operation-not-permitted-error
+   protocol-not-supported-error
+   socket-type-not-supported-error
+   network-unreachable-error
+   network-down-error
+   network-reset-error
+   host-down-error
+   host-unreachable-error
+   shutdown-error
+   timeout-error
+   deadline-timeout-error
+   invalid-socket-error
+   invalid-socket-stream-error)
+  (socket-error))
+
+(define-condition unknown-error (socket-error)
+  ((real-error :initarg :real-error
+               :accessor usocket-real-error
+               :initform nil)
+   (errno      :initarg :errno
+               :reader usocket-errno
+               :initform 0))
+  (:report (lambda (c stream)
+             (typecase c
+               (simple-condition
+                (format stream
+                        (simple-condition-format-control (usocket-real-error c))
+                        (simple-condition-format-arguments (usocket-real-error c))))
+               (otherwise
+                (format stream "The condition ~A occurred with errno: ~D."
+                        (usocket-real-error c)
+                        (usocket-errno c))))))
+  (:documentation "Error raised when there's no other - more applicable -
+error available."))
+
+(define-usocket-condition-classes
+  (ns-try-again-condition)
+  (ns-condition))
+
+(define-condition ns-unknown-condition (ns-condition)
+  ((real-condition :initarg :real-condition
+                   :accessor ns-real-condition
+                   :initform nil))
+  (:documentation "Condition raised when there's no other - more applicable -
+condition available."))
+
+(define-usocket-condition-classes
+  ;; the no-data error code in the Unix 98 api
+  ;; isn't really an error: there's just no data to return.
+  ;; with lisp, we just return NIL (indicating no data) instead of
+  ;; raising an exception...
+  (ns-host-not-found-error
+   ns-no-recovery-error)
+  (ns-error))
+
+(define-condition ns-unknown-error (ns-error)
+  ((real-error :initarg :real-error
+               :accessor ns-real-error
+               :initform nil))
+  (:report (lambda (c stream)
+             (typecase c
+               (simple-condition
+                (format stream
+                        (simple-condition-format-control (usocket-real-error c))
+                        (simple-condition-format-arguments (usocket-real-error c))))
+               (otherwise
+                (format stream "The condition ~A occurred." (usocket-real-error c))))))
+  (:documentation "Error raised when there's no other - more applicable -
+error available."))
+
+(defmacro with-mapped-conditions ((&optional socket) &body body)
+  `(handler-bind ((condition #'(lambda (c) (handle-condition c ,socket))))
+    ,@body))
+
+(defparameter +unix-errno-condition-map+
+  `(((11) . ns-try-again-condition) ;; EAGAIN
+    ((35) . ns-try-again-condition) ;; EDEADLCK
+    ((4) . interrupted-condition))) ;; EINTR
+
+(defparameter +unix-errno-error-map+
+  ;;### the first column is for non-(linux or srv4) systems
+  ;; the second for linux
+  ;; the third for srv4
+  ;;###FIXME: How do I determine on which Unix we're running
+  ;;          (at least in clisp and sbcl; I know about cmucl...)
+  ;; The table below works under the assumption we'll *only* see
+  ;; socket associated errors...
+  `(((48 98) . address-in-use-error)
+    ((49 99) . address-not-available-error)
+    ((9) . bad-file-descriptor-error)
+    ((61 111) . connection-refused-error)
+    ((54 104) . connection-reset-error)
+    ((53 103) . connection-aborted-error)
+    ((22) . invalid-argument-error)
+    ((55 105) . no-buffers-error)
+    ((12) . out-of-memory-error)
+    ((45 95) . operation-not-supported-error)
+    ((1) . operation-not-permitted-error)
+    ((43 92) . protocol-not-supported-error)
+    ((44 93) . socket-type-not-supported-error)
+    ((51 101) . network-unreachable-error)
+    ((50 100) . network-down-error)
+    ((52 102) . network-reset-error)
+    ((58 108) . already-shutdown-error)
+    ((60 110) . timeout-error)
+    ((64 112) . host-down-error)
+    ((65 113) . host-unreachable-error)))
+
+(defun map-errno-condition (errno)
+  (cdr (assoc errno +unix-errno-error-map+ :test #'member)))
+
+(defun map-errno-error (errno)
+  (cdr (assoc errno +unix-errno-error-map+ :test #'member)))
+
+(defparameter +unix-ns-error-map+
+  `((1 . ns-host-not-found-error)
+    (2 . ns-try-again-condition)
+    (3 . ns-no-recovery-error)))
+
+(defmacro unsupported (feature context &key minimum)
+  `(cerror "Ignore it and continue" 'unsupported
+          :feature ,feature
+          :context ,context
+          :minimum ,minimum))
+
+(defmacro unimplemented (feature context)
+  `(signal 'unimplemented :feature ,feature :context ,context))
+
+
+;;; People may want to ignore all unsupported warnings, here it is.
+(defmacro ignore-unsupported-warnings (&body body)
+  `(handler-bind ((unsupported
+                   #'(lambda (c)
+                       (declare (ignore c)) (continue))))
+     (progn ,@body)))
diff --git a/deps/usocket/doc/backends.txt b/deps/usocket/doc/backends.txt
new file mode 100644 (file)
index 0000000..c2c770f
--- /dev/null
@@ -0,0 +1,60 @@
+                                                                -*- text -*-
+
+$Id$
+
+A document to describe which APIs a backend should implement.
+
+
+Each backend should implement:
+
+Functions:
+
+ - handle-condition
+ - socket-connect
+ - socket-listen
+ - get-hosts-by-name [ optional ]
+ - get-host-by-address [ optional ]
+
+ - wait-for-input-internal (new in 0.4.x)
+
+Methods:
+
+ - socket-close
+ - socket-accept
+ - get-local-name
+ - get-peer-name
+
+ and - for ip sockets - these methods:
+
+ - get-local-address
+ - get-local-port
+ - get-peer-address
+ - get-peer-port
+
+
+An error-handling function, resolving implementation specific errors
+to this list of errors:
+
+ - address-in-use-error
+ - address-not-available-error
+ - bad-file-descriptor-error
+ - connection-refused-error
+ - invalid-argument-error
+ - no-buffers-error
+ - operation-not-supported-error
+ - operation-not-permitted-error
+ - protocol-not-supported-error
+ - socket-type-not-supported-error
+ - network-unreachable-error
+ - network-down-error
+ - network-reset-error
+ - host-down-error
+ - host-unreachable-error
+ - shutdown-error
+ - timeout-error
+ - unkown-error
+
+and these conditions:
+
+ - interrupted-condition
+ - unkown-condition
diff --git a/deps/usocket/doc/design.txt b/deps/usocket/doc/design.txt
new file mode 100644 (file)
index 0000000..2f9c487
--- /dev/null
@@ -0,0 +1,136 @@
+
+                                                        -*- text -*-
+
+$Id$
+
+
+                  usocket: Universal sockets library
+                  ==================================
+
+Contents
+========
+
+ * Motivation
+ * Design goal
+ * Functional requirements
+ * Class structure
+
+
+\f
+Motivation
+==========
+
+There are 2 other portability sockets packages [that I know of]
+out there:
+
+ 1) trivial-sockets
+ 2) acl-compat (which is a *lot* broader, but contains sockets too)
+
+The first misses some functionality which is fundamental when
+the requirements stop being 'trivial', such as finding out the
+addresses of either side connected to the tcp/ip stream.
+
+The second, being a complete compatibility library for Allegro,
+contains much more than only sockets.  Next to that, as the docs
+say, is it mainly directed at providing the functionality required
+to port portable-allegroserve - meaning it may be (very) incomplete
+on some platforms.
+
+So, that's why I decided to inherit Erik Enge's project to build
+a library with the intention to provide portability code in only
+1 area of programming, targeted at 'not so trivial' programming.
+
+Also, I need this library to extend cl-irc with full DCC functionality.
+
+
+\f
+Design goal
+===========
+
+To provide a portable TCP/IP socket interface for as many
+implementations as possible, while keeping the portability layer
+as thin as possible.
+
+
+\f
+Functional requirements
+=======================
+
+The interface provided should allow:
+ - 'client'/active sockets
+ - 'server'/listening sockets
+ - provide the usual stream methods to operate on the connection stream
+   (not necessarily the socket itself; maybe a socket slot too)
+
+For now, as long as there are no possibilities to have UDP sockets
+to write a DNS client library: (which in the end may work better,
+because in this respect all implementations are different...)
+ - retrieve IP addresses/ports for both sides of the connection
+
+Several relevant support functionalities will have to be provided too:
+ - long <-> quad-vector operators
+ - quad-vector <-> string operators
+ - hostname <-> quad-vector operators (hostname resolution)
+
+
+Minimally, I'd like to support:
+ - SBCL
+ - CMUCL
+ - ABCL (ArmedBear)
+ - clisp
+ - Allegro
+ - LispWorks
+ - OpenMCL
+
+
+Comments on the design above
+============================
+
+I don't think it's a good idea to implement name lookup in the
+very first of steps: we'll see if this is required to get the
+package accepted; not all implementations support it.
+
+Name resolution errors ...
+Since there is no name resolution library (yet), nor standardized
+hooks into the standard C library to do it the same way on
+all platforms, name resolution errors can manifest themselves
+in a lot of different ways.  How to marshall these to the
+library users?
+
+Several solutions come to mind:
+
+1) Map them to 'unknown-error
+2) Give them their own errors and map to those
+   ... which implies that they are actually supported atm.
+3) ...
+
+Given that the library doesn't now, but may in the future,
+include name resolution officially, I tend to think (1) is the
+right answer: it leaves it all undecided.
+
+These errors can be raised by the nameresolution service
+(netdb.h) as values for 'int h_errno':
+
+- HOST_NOT_FOUND (1)
+- TRY_AGAIN      (2) /* Server fail or non-authoritive Host not found */
+- NO_RECOVERY    (3) /* Failed permanently */
+- NO_DATA        (4) /* Valid address, no data for requested record */
+
+int *__h_errno_location(void) points to thread local h_errno on
+threaded glibc2 systems.
+
+\f
+Class structure
+===============
+
+ usocket
+  |
+  +- datagram-usocket
+  +- stream-usocket
+  \- stream-server-usocket
+
+The usocket class will have methods to query local properties, such
+as:
+
+ - get-local-name: to query to which interface the socket is bound
+ - <other socket and protocol options such as SO_REUSEADDRESS>
diff --git a/deps/usocket/notes/abcl-socket.txt b/deps/usocket/notes/abcl-socket.txt
new file mode 100644 (file)
index 0000000..531ace9
--- /dev/null
@@ -0,0 +1,18 @@
+
+ABCL provides a callback interface to java objects, next to these calls:
+
+ - ext:make-socket
+ - ext:socket-close
+ - ext:make-server-socket
+ - ext:socket-accept
+ - ext:get-socket-stream (returning an io-stream)
+
+abcl-swank (see SLIME) shows how to call directly into java.
+
+
+See for the sockets implementation:
+
+ - src/org/armedbear/lisp
+   * socket.lisp
+   * socket_stream.java
+   * SocketStream.java
diff --git a/deps/usocket/notes/active-sockets-apis.txt b/deps/usocket/notes/active-sockets-apis.txt
new file mode 100644 (file)
index 0000000..b74ccfe
--- /dev/null
@@ -0,0 +1,75 @@
+                                                          -*- text -*-
+
+A document to summarizing which API's of the different implementations
+are associated with 'Step 1'.
+
+Interface to be implemented in step 1:
+
+ - socket-connect
+ - socket-close
+ - get-host-by-address
+ - get-hosts-by-name
+
+(and something to do with errors; maybe move this to step 1a?)
+
+SBCL
+====
+
+  sockets:
+  - socket-bind
+  - make-instance 'inet-socket
+  - socket-make-stream
+  - socket-connect (ip vector-quad) port
+  - socket-close
+
+  DNS name resolution:
+  - get-host-by-name
+  - get-host-by-address
+  - ::host-ent-addresses
+  - host-ent-name
+
+
+CMUCL
+=====
+
+  sockets:
+  - ext:connect-to-inet-socket (ip integer) port
+  - sys:make-fd-stream
+  - ext:close-socket
+
+  DNS name resolution:
+  - ext:host-entry-name
+  - ext::lookup-host-entry
+  - ext:host-entry-addr-list
+  - ext:lookup-host-entry
+
+
+ABCL
+====
+
+  sockets
+  - ext:socket-connect (hostname string) port
+  - ext:get-socket-stream
+  - ext:socket-close
+
+
+clisp
+=====
+
+  sockets
+  - socket-connect port (hostname string)
+  - close (socket)
+
+
+Allegro
+=======
+
+  sockets
+  - make-socket
+  - socket-connect
+  - close
+
+  DNS resolution
+  - lookup-hostname
+  - ipaddr-to-hostname
+
diff --git a/deps/usocket/notes/address-apis.txt b/deps/usocket/notes/address-apis.txt
new file mode 100644 (file)
index 0000000..2661ac2
--- /dev/null
@@ -0,0 +1,73 @@
+
+                                                           -*- text -*-
+
+Step 2 of the master plan: Implementing (get-local-address sock) and
+(get-peer-address sock).
+
+
+Step 2 is about implementing:
+
+ (get-local-address sock) -> ip
+ (get-peer-address sock) -> ip
+ (get-local-port sock) -> port
+ (get-peer-port sock) -> port
+ (get-local-name sock) -> ip, port
+ (get-peer-name sock) -> ip, port
+
+
+ABCL
+====
+
+ FFI / J-calls to "getLocalAddress"+"getAddress", "getLocalPort" (local)
+ FFI / J-calls to "getInetAddress"+"getAddress", "getPort" (peer)
+
+ (see SLIME / swank-abcl.lisp for an example on how to do that)
+
+
+Allegro
+=======
+
+ (values (socket:remote-host sock)
+         (socket:remote-port)) -> 32bit ip, port
+
+ (values (socket:local-host sock)
+         (socket:local-port sock)) -> 32bit ip, port
+
+CLISP
+=====
+
+ (socket:socket-stream-local sock nil) -> address (as dotted quad), port
+ (socket:socket-stream-peer sock nil) -> address (as dotted quad), port
+
+
+CMUCL
+=====
+
+ (ext:get-peer-host-and-port sock-fd) -> 32-bit-addr, port (peer)
+ (ext:get-socket-host-and-port sock-fd) -> 32-bit-addr, port (local)
+
+
+LispWorks
+=========
+
+ (comm:socket-stream-address sock-stream) -> 32-bit-addr, port
+   or: (comm:get-socket-address sock) -> 32-bit-addr, port
+
+ (comm:socket-stream-peer-address sock-stream) -> 32-bit-addr, port
+   or: (comm:get-socket-peer-address sock) -> 32-bit-addr, port
+
+
+OpenMCL
+=======
+
+ (values (ccl:local-host sock) (ccl:local-port sock)) -> 32-bit ip, port
+ (values (ccl:remote-host sock) (ccl:remote-port sock)) -> 32-bit ip, port
+
+
+SBCL
+====
+
+ (sb-bsd-sockets:socket-name sock) -> vector-quad, port
+ (sb-bsd-sockets:socket-peer-name sock) -> vector-quad, port
+
+
diff --git a/deps/usocket/notes/allegro-socket.txt b/deps/usocket/notes/allegro-socket.txt
new file mode 100644 (file)
index 0000000..8f90ca7
--- /dev/null
@@ -0,0 +1,46 @@
+
+
+(require :sock)
+
+accept-connection      (sock passive-socket) &key wait         Generic function. 
+dotted-to-ipaddr       dotted &key errorp      Function.
+ipaddr-to-dotted       ipaddr &key values      Function.
+ipaddr-to-hostname     ipaddr  Function.
+lookup-hostname        hostname
+lookup-port    portname protocol       Function.
+make-socket    &key type format address-family connect &allow-other-keys       Function.
+with-pending-connect   &body body      Macro.
+receive-from   (sock datagram-socket) size &key buffer extract         Generic function.
+send-to        sock &key
+shutdown       sock &key direction 
+socket-control         stream &key output-chunking output-chunking-eof input-chunking 
+socket-os-fd   sock    Generic function.
+
+remote-host    socket  Generic function.
+local-host     socket  Generic function.
+local-port     socket
+
+remote-filename        socket
+local-filename         socket
+remote-port    socket
+socket-address-family  socket
+socket-connect         socket
+socket-format  socket
+socket-type    socket
+
+errors
+
+:address-in-use        Local socket address already in use
+:address-not-available         Local socket address not available
+:network-down  Network is down
+:network-reset         Network has been reset
+:connection-aborted    Connection aborted
+:connection-reset      Connection reset by peer
+:no-buffer-space       No buffer space
+:shutdown      Connection shut down
+:connection-timed-out  Connection timed out
+:connection-refused    Connection refused
+:host-down     Host is down
+:host-unreachable      Host is unreachable
+:unknown       Unknown error
+
diff --git a/deps/usocket/notes/clisp-sockets.txt b/deps/usocket/notes/clisp-sockets.txt
new file mode 100644 (file)
index 0000000..e680fe8
--- /dev/null
@@ -0,0 +1,38 @@
+http://clisp.cons.org/impnotes.html#socket
+
+(SOCKET:SOCKET-SERVER &OPTIONAL [port-or-socket])
+(SOCKET:SOCKET-SERVER-HOST socket-server)
+(SOCKET:SOCKET-SERVER-PORT socket-server)
+(SOCKET:SOCKET-WAIT socket-server &OPTIONAL [seconds [microseconds]])
+(SOCKET:SOCKET-ACCEPT socket-server &KEY :ELEMENT-TYPE :EXTERNAL-FORMAT :BUFFERED :TIMEOUT)
+(SOCKET:SOCKET-CONNECT port &OPTIONAL [host] &KEY :ELEMENT-TYPE :EXTERNAL-FORMAT :BUFFERED :TIMEOUT)
+(SOCKET:SOCKET-STATUS socket-stream-or-list &OPTIONAL [seconds [microseconds]])
+(SOCKET:SOCKET-STREAM-HOST socket-stream)
+(SOCKET:SOCKET-STREAM-PORT socket-stream)
+(SOCKET:SOCKET-SERVICE-PORT &OPTIONAL service-name (protocol "tcp"))
+(SOCKET:SOCKET-STREAM-PEER socket-stream [do-not-resolve-p])
+(SOCKET:SOCKET-STREAM-LOCAL socket-stream [do-not-resolve-p])
+(SOCKET:SOCKET-STREAM-SHUTDOWN socket-stream direction)
+(SOCKET:SOCKET-OPTIONS socket-server &REST {option}*)
+
+
+(posix:resolve-host-ipaddr &optional host)
+
+with the host-ent structure:
+
+  name      - host name
+  aliases   - LIST of aliases
+  addr-list - LIST of IPs as dotted quads (IPv4) or coloned octets (IPv6)
+  addrtype  - INTEGER address type IPv4 or IPv6
+
+
+Errors are of type
+
+SYSTEM::SIMPLE-OS-ERROR
+ with a 1 element (integer) SYSTEM::$FORMAT-ARGUMENTS list
+
+This integer stores the OS error reported; meaning WSA* codes on Win32
+and E* codes on *nix, only: unix.lisp in CMUCL shows
+BSD, Linux and SRV4 have different number assignments for the same
+E* constant names  :-(
+
diff --git a/deps/usocket/notes/cmucl-sockets.txt b/deps/usocket/notes/cmucl-sockets.txt
new file mode 100644 (file)
index 0000000..7b81ca3
--- /dev/null
@@ -0,0 +1,69 @@
+http://cvs2.cons.org/ftp-area/cmucl/doc/cmu-user/internet.html
+
+$Id$
+
+extensions:lookup-host-entry host
+
+[structure]
+host-entry    
+
+    name aliases addr-type addr-list
+
+[Function]
+extensions:create-inet-listener port &optional kind &key :reuse-address :backlog :interface
+  => socket fd
+
+[Function]
+extensions:accept-tcp-connection unconnected
+  => socket fd, address
+
+[Function]
+extensions:connect-to-inet-socket host port &optional kind
+  => socket fd
+
+[Function]
+extensions:close-socket socket
+
+
+
+[Private function]
+extensions::get-peer-host-and-port socket-fd
+
+[Private function]
+extentsions::get-socket-host-and-port socket-fd
+
+
+
+There's currently only 1 condition to be raised:
+
+  SOCKET-ERROR (derived from SIMPLE-ERROR)
+    which has a SOCKET-ERRNO slot containing the unix error number.
+
+
+
+
+[Function]
+extensions:add-oob-handler fd char handler
+
+[Function]
+extensions:remove-oob-handler fd char
+
+[Function]
+extensions:remove-all-oob-handlers fd
+
+[Function]
+extensions:send-character-out-of-band fd char
+
+[Function]
+extensions:create-inet-socket &optional type
+  => socket fd
+
+[Function]
+extensions:get-socket-option socket level optname
+
+[Function]
+extensions:set-socket-option socket level optname optval
+
+[Function]
+extensions:ip-string addr    
+
diff --git a/deps/usocket/notes/errors.txt b/deps/usocket/notes/errors.txt
new file mode 100644 (file)
index 0000000..148525c
--- /dev/null
@@ -0,0 +1,20 @@
+EADDRINUSE 48 address-in-use-error
+EADDRNOTAVAIL 49 address-not-available-error
+EAGAIN interrupted-error  ;; not 1 error code: bsd == 11; non-bsd == 35
+EBADF 9 bad-file-descriptor-error
+ECONNREFUSED 61 connection-refused-error
+EINTR 4 interrupted-error
+EINVAL 22 invalid-argument-error
+ENOBUFS 55 no-buffers-error
+ENOMEM 12 out-of-memory-error
+EOPNOTSUPP 45 operation-not-supported-error
+EPERM 1 operation-not-permitted-error
+EPROTONOSUPPORT 43 protocol-not-supported-error
+ESOCKTNOSUPPORT 44 socket-type-not-supported-error
+ENETUNREACH 51 network-unreachable-error
+ENETDOWN 50 network-down-error
+ENETRESET 52 network-reset-error
+ESHUTDOWN 58 already-shutdown-error
+ETIMEDOUT 60 connection-timeout-error
+EHOSTDOWN 64 host-down-error
+EHOSTUNREACH 65 host-unreachable-error
diff --git a/deps/usocket/notes/lw-sockets.txt b/deps/usocket/notes/lw-sockets.txt
new file mode 100644 (file)
index 0000000..666ede8
--- /dev/null
@@ -0,0 +1,41 @@
+
+$Id$
+
+http://www.lispworks.com/reference/lwu41/lwref/LWRM_37.HTM
+
+Package: COMM
+
+ip-address-string
+socket-stream-address
+socket-stream-peer-address
+start-up-server
+start-up-server-and-mp
+string-ip-address
+with-noticed-socket-stream
+
+Needed components for usocket:
+
+comm::get-fd-from-socket socket-fd
+  => socket-fd
+
+comm::accept-connection-to-socket socket-fd
+  => socket-fd
+
+comm::close-socket
+comm::create-tcp-socket-for-service
+  => socket-fd
+
+open-tcp-stream peer-host peer-port &key direction element-type
+  => socket-stream
+
+get-host-entry (see http://www.lispworks.com/documentation/lw445/LWRM/html/lwref-30.htm#pgfId-897837)
+get-socket-address
+
+get-socket-peer-address
+  => address, port
+
+socket-stream socket-fd
+  => stream
+
+socket socket-stream (guessed from http://www.lispworks.com/documentation/lw445/LWRM/html/lwref-43.htm)
+  => socket-fd
diff --git a/deps/usocket/notes/openmcl-sockets.txt b/deps/usocket/notes/openmcl-sockets.txt
new file mode 100644 (file)
index 0000000..1a7ee4d
--- /dev/null
@@ -0,0 +1,27 @@
+http://openmcl.clozure.com/Doc/sockets.html
+
+    make-socket [Function]
+    accept-connection [Function]
+    dotted-to-ipaddr [Function]
+    ipaddr-to-dotted [Function]
+    ipaddr-to-hostname [Function]
+    lookup-hostname [Function]
+    lookup-port [Function]
+    receive-from [Function]
+    send-to [Function]
+    shutdown [Function]
+    socket-os-fd [Function]
+    remote-port [Function]
+    local-host [Function]
+    local-port [Function]
+
+        socket-address-family [Function]
+
+    socket-connect [Function]
+    socket-format [Function]
+    socket-type [Function]
+    socket-error [Class]
+    socket-error-code [Function]
+    socket-error-identifier [Function]
+    socket-error-situation [Function]
+    close [method]
diff --git a/deps/usocket/notes/sb-bsd-sockets.txt b/deps/usocket/notes/sb-bsd-sockets.txt
new file mode 100644 (file)
index 0000000..e80b583
--- /dev/null
@@ -0,0 +1,114 @@
+http://www.xach.com/sbcl/sb-bsd-sockets.html
+
+$Id$
+
+package: sb-bsd-sockets
+
+class: socket
+
+slots:
+
+    * file-descriptor :
+    * family :
+    * protocol :
+    * type :
+    * stream :
+
+operators:
+
+  (socket-bind (s socket) &rest address)       Generic Function
+  (socket-accept (socket socket))      Method
+  (socket-connect (s socket) &rest address)    Generic Function
+  (socket-peername (socket socket))    Method
+  (socket-name (socket socket))        Method
+  (socket-receive (socket socket) buffer length &key oob peek waitall (element-type 'character))       Method
+  (socket-listen (socket socket) backlog)      Method
+  (socket-close (socket socket))       Method
+  (socket-make-stream (socket socket) &rest args)      Method
+
+  (sockopt-reuse-address (socket socket) argument)     Accessor
+  (sockopt-keep-alive (socket socket) argument)        Accessor
+  (sockopt-oob-inline (socket socket) argument)        Accessor
+  (sockopt-bsd-compatible (socket socket) argument)    Accessor
+  (sockopt-pass-credentials (socket socket) argument)  Accessor
+  (sockopt-debug (socket socket) argument)     Accessor
+  (sockopt-dont-route (socket socket) argument)        Accessor
+  (sockopt-broadcast (socket socket) argument) Accessor
+  (sockopt-tcp-nodelay (socket socket) argument)       Accessor
+
+inet-domain sockets
+
+class: inet-socket
+
+slots:
+
+    * family :
+
+operators:
+
+   (make-inet-address dotted-quads)    Function
+   (get-protocol-by-name name) Function
+   (make-inet-socket type protocol)    Function
+
+file-domain sockets
+
+class: unix-socket
+
+slots:
+
+    * family :
+
+class: host-ent
+
+Slots:
+
+    * name :
+    * aliases :
+    * address-type :
+    * addresses :
+
+   (host-ent-address (host-ent host-ent))      Method
+   (get-host-by-name host-name)        Function
+   (get-host-by-address address)       Function
+   (name-service-error where)  Function
+   (non-blocking-mode (socket socket)) Method
+
+(define-socket-condition sockint::EADDRINUSE address-in-use-error)
+(define-socket-condition sockint::EAGAIN interrupted-error)
+(define-socket-condition sockint::EBADF bad-file-descriptor-error)
+(define-socket-condition sockint::ECONNREFUSED connection-refused-error)
+(define-socket-condition sockint::EINTR interrupted-error)
+(define-socket-condition sockint::EINVAL invalid-argument-error)
+(define-socket-condition sockint::ENOBUFS no-buffers-error)
+(define-socket-condition sockint::ENOMEM out-of-memory-error)
+(define-socket-condition sockint::EOPNOTSUPP operation-not-supported-error)
+(define-socket-condition sockint::EPERM operation-not-permitted-error)
+(define-socket-condition sockint::EPROTONOSUPPORT protocol-not-supported-error)
+(define-socket-condition sockint::ESOCKTNOSUPPORT socket-type-not-supported-error)
+(define-socket-condition sockint::ENETUNREACH network-unreachable-error)
+
+Exported errors:
+* (apropos "ERROR" :sb-bsd-sockets)
+
+SB-BSD-SOCKETS:INTERRUPTED-ERROR
+SB-BSD-SOCKETS:TRY-AGAIN-ERROR
+* SB-BSD-SOCKETS:NO-RECOVERY-ERROR (EFAIL?)
+SB-BSD-SOCKETS:CONNECTION-REFUSED-ERROR
+SB-BSD-SOCKETS:INVALID-ARGUMENT-ERROR
+* SB-BSD-SOCKETS:HOST-NOT-FOUND-ERROR
+SB-BSD-SOCKETS:OPERATION-NOT-PERMITTED-ERROR
+SB-BSD-SOCKETS:OPERATION-NOT-SUPPORTED-ERROR
+SB-BSD-SOCKETS:PROTOCOL-NOT-SUPPORTED-ERROR
+SB-BSD-SOCKETS:OPERATION-TIMEOUT-ERROR
+SB-BSD-SOCKETS:SOCKET-TYPE-NOT-SUPPORTED-ERROR
+SB-BSD-SOCKETS:NO-BUFFERS-ERROR
+SB-BSD-SOCKETS:NETWORK-UNREACHABLE-ERROR
+SB-BSD-SOCKETS:BAD-FILE-DESCRIPTOR-ERROR
+SB-BSD-SOCKETS:ADDRESS-IN-USE-ERROR
+SB-BSD-SOCKETS:OUT-OF-MEMORY-ERROR
+
+And 1 non-exported error:
+
+SB-BSD-SOCKETS::NO-ADDRESS-ERROR
+
+*-ed errors aren't yet addressed in the errorlist supported by usocket
diff --git a/deps/usocket/notes/usock-sockets.txt b/deps/usocket/notes/usock-sockets.txt
new file mode 100644 (file)
index 0000000..562dc58
--- /dev/null
@@ -0,0 +1,28 @@
+Package:
+
+  clisp  : socket
+  cmucl  : extensions
+  sbcl   : sb-bsd-sockets
+  lw     : comm
+  openmcl: openmcl-socket
+  allegro: sock
+
+Connecting (TCP/inet only)
+
+  clisp  : socket-connect port &optional [host] &key :element-type :external-format :buffered :timeout = > socket-stream
+  cmucl  : connect-to-inet-socket host port &optional kind => file descriptor
+  sbcl   : sb-socket-connect socket &rest address => socket
+  lw     : open-tcp-stream hostname service &key direction element-type buffered => stream-object
+  openmcl: socket-connect socket => :active, :passive or nil
+  allegro: make-socket (&rest args &key type format connect address-family eol) => socket
+
+Closing
+
+  clisp  : close socket
+  cmucl  : close-socket socket
+  sbcl   : socket-close socket
+  lw     : close socket
+  openmcl: close socket
+  allegro: close socket
+
+Errors
\ No newline at end of file
diff --git a/deps/usocket/option.lisp b/deps/usocket/option.lisp
new file mode 100644 (file)
index 0000000..df65d09
--- /dev/null
@@ -0,0 +1,276 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; SOCKET-OPTION, a high-level socket option get/set framework
+
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+;;; Small utility functions
+(declaim (inline bool->int) (inline int->bool))
+(defun bool->int (bool) (if bool 1 0))
+(defun int->bool (int) (= 1 int))
+
+;;; Interface definition
+
+(defgeneric socket-option (socket option &key)
+  (:documentation
+   "Get a socket's internal options"))
+
+(defgeneric (setf socket-option) (new-value socket option &key)
+  (:documentation
+   "Set a socket's internal options"))
+
+;;; Handling of wrong type of arguments
+
+(defmethod socket-option ((socket usocket) (option t) &key)
+  (error 'type-error :datum option :expected-type 'keyword))
+
+(defmethod (setf socket-option) (new-value (socket usocket) (option t) &key)
+  (declare (ignore new-value))
+  (socket-option socket option))
+
+(defmethod socket-option ((socket usocket) (option symbol) &key)
+  (if (keywordp option)
+    (error 'unimplemented :feature option :context 'socket-option)
+    (error 'type-error :datum option :expected-type 'keyword)))
+
+(defmethod (setf socket-option) (new-value (socket usocket) (option symbol) &key)
+  (declare (ignore new-value))
+  (socket-option socket option))
+
+;;; Socket option: RECEIVE-TIMEOUT (SO_RCVTIMEO)
+
+(defmethod socket-option ((usocket stream-usocket)
+                          (option (eql :receive-timeout)) &key)
+  (declare (ignorable option))
+  (let ((socket (socket usocket)))
+    (declare (ignorable socket))
+    #+abcl
+    () ; TODO
+    #+allegro
+    () ; TODO
+    #+clisp
+    (socket:socket-options socket :so-rcvtimeo)
+    #+clozure
+    (ccl:stream-input-timeout socket)
+    #+cmu
+    (lisp::fd-stream-timeout (socket-stream usocket))
+    #+ecl
+    (sb-bsd-sockets:sockopt-receive-timeout socket)
+    #+lispworks
+    (get-socket-receive-timeout socket)
+    #+mcl
+    () ; TODO
+    #+mocl
+    () ; unknown
+    #+sbcl
+    (sb-impl::fd-stream-timeout (socket-stream usocket))
+    #+scl
+    ())) ; TODO
+
+(defmethod (setf socket-option) (new-value (usocket stream-usocket)
+                                           (option (eql :receive-timeout)) &key)
+  (declare (type number new-value) (ignorable new-value option))
+  (let ((socket (socket usocket))
+        (timeout new-value))
+    (declare (ignorable socket timeout))
+    #+abcl
+    () ; TODO
+    #+allegro
+    () ; TODO
+    #+clisp
+    (socket:socket-options socket :so-rcvtimeo timeout)
+    #+clozure
+    (setf (ccl:stream-input-timeout socket) timeout)
+    #+cmu
+    (setf (lisp::fd-stream-timeout (socket-stream usocket))
+          (coerce timeout 'integer))
+    #+ecl
+    (setf (sb-bsd-sockets:sockopt-receive-timeout socket) timeout)
+    #+lispworks
+    (set-socket-receive-timeout socket timeout)
+    #+mcl
+    () ; TODO
+    #+mocl
+    () ; unknown
+    #+sbcl
+    (setf (sb-impl::fd-stream-timeout (socket-stream usocket))
+          (coerce timeout 'single-float))
+    #+scl
+    () ; TODO
+    new-value))
+
+;;; Socket option: REUSE-ADDRESS (SO_REUSEADDR), for TCP server
+
+(defmethod socket-option ((usocket stream-server-usocket)
+                          (option (eql :reuse-address)) &key)
+  (declare (ignorable option))
+  (let ((socket (socket usocket)))
+    (declare (ignorable socket))
+    #+abcl
+    () ; TODO
+    #+allegro
+    () ; TODO
+    #+clisp
+    (int->bool (socket:socket-options socket :so-reuseaddr))
+    #+clozure
+    (int->bool (get-socket-option-reuseaddr socket))
+    #+cmu
+    () ; TODO
+    #+lispworks
+    (get-socket-reuse-address socket)
+    #+mcl
+    () ; TODO
+    #+mocl
+    () ; unknown
+    #+(or ecl sbcl)
+    (sb-bsd-sockets:sockopt-reuse-address socket)
+    #+scl
+    ())) ; TODO
+
+(defmethod (setf socket-option) (new-value (usocket stream-server-usocket)
+                                           (option (eql :reuse-address)) &key)
+  (declare (type boolean new-value) (ignorable new-value option))
+  (let ((socket (socket usocket)))
+    (declare (ignorable socket))
+    #+abcl
+    () ; TODO
+    #+allegro
+    (socket:set-socket-options socket option new-value)
+    #+clisp
+    (socket:socket-options socket :so-reuseaddr (bool->int new-value))
+    #+clozure
+    (set-socket-option-reuseaddr socket (bool->int new-value))
+    #+cmu
+    () ; TODO
+    #+lispworks
+    (set-socket-reuse-address socket new-value)
+    #+mcl
+    () ; TODO
+    #+mocl
+    () ; unknown
+    #+(or ecl sbcl)
+    (setf (sb-bsd-sockets:sockopt-reuse-address socket) new-value)
+    #+scl
+    () ; TODO
+    new-value))
+
+;;; Socket option: BROADCAST (SO_BROADCAST), for UDP client
+
+(defmethod socket-option ((usocket datagram-usocket)
+                          (option (eql :broadcast)) &key)
+  (declare (ignorable option))
+  (let ((socket (socket usocket)))
+    (declare (ignorable socket))
+    #+abcl
+    () ; TODO
+    #+allegro
+    () ; TODO
+    #+clisp
+    (int->bool (socket:socket-options socket :so-broadcast))
+    #+clozure
+    (int->bool (get-socket-option-broadcast socket))
+    #+cmu
+    () ; TODO
+    #+ecl
+    () ; TODO
+    #+lispworks
+    () ; TODO
+    #+mcl
+    () ; TODO
+    #+mocl
+    () ; unknown
+    #+sbcl
+    (sb-bsd-sockets:sockopt-broadcast socket)
+    #+scl
+    ())) ; TODO
+
+(defmethod (setf socket-option) (new-value (usocket datagram-usocket)
+                                           (option (eql :broadcast)) &key)
+  (declare (type boolean new-value) (ignorable new-value option))
+  (let ((socket (socket usocket)))
+    (declare (ignorable socket))
+    #+abcl
+    () ; TODO
+    #+allegro
+    (socket:set-socket-options socket option new-value)
+    #+clisp
+    (socket:socket-options socket :so-broadcast (bool->int new-value))
+    #+clozure
+    (set-socket-option-broadcast socket (bool->int new-value))
+    #+cmu
+    () ; TODO
+    #+ecl
+    () ; TODO
+    #+lispworks
+    () ; TODO
+    #+mcl
+    () ; TODO
+    #+mocl
+    () ; unknown
+    #+sbcl
+    (setf (sb-bsd-sockets:sockopt-broadcast socket) new-value)
+    #+scl
+    () ; TODO
+    new-value))
+
+;;; Socket option: TCP-NO-DELAY (TCP_NODELAY), for TCP client
+
+(defmethod socket-option ((usocket stream-usocket)
+                          (option (eql :tcp-no-delay)) &key)
+  (declare (ignorable option))
+  (let ((socket (socket usocket)))
+    (declare (ignorable socket))
+    #+abcl
+    () ; TODO
+    #+allegro
+    () ; TODO
+    #+clisp
+    (int->bool (socket:socket-options socket :tcp-nodelay))
+    #+clozure
+    (int->bool (get-socket-option-tcp-nodelay socket))
+    #+cmu
+    ()
+    #+ecl
+    (sb-bsd-sockets::sockopt-tcp-nodelay socket)
+    #+lispworks
+    () ; TODO
+    #+mcl
+    () ; TODO
+    #+mocl
+    () ; unknown
+    #+sbcl
+    (sb-bsd-sockets::sockopt-tcp-nodelay socket)
+    #+scl
+    ())) ; TODO
+
+(defmethod (setf socket-option) (new-value (usocket stream-usocket)
+                                           (option (eql :tcp-no-delay)) &key)
+  (declare (type boolean new-value) (ignorable new-value option))
+  (let ((socket (socket usocket)))
+    (declare (ignorable socket))
+    #+abcl
+    () ; TODO
+    #+allegro
+    (socket:set-socket-options socket :no-delay new-value)
+    #+clisp
+    (socket:socket-options socket :tcp-nodelay (bool->int new-value))
+    #+clozure
+    (set-socket-option-tcp-nodelay socket (bool->int new-value))
+    #+cmu
+    ()
+    #+ecl
+    (setf (sb-bsd-sockets::sockopt-tcp-nodelay socket) new-value)
+    #+lispworks
+    (comm::set-socket-tcp-nodelay socket new-value)
+    #+mcl
+    () ; TODO
+    #+mocl
+    () ; unknown
+    #+sbcl
+    (setf (sb-bsd-sockets::sockopt-tcp-nodelay socket) new-value)
+    #+scl
+    () ; TODO
+    new-value))
diff --git a/deps/usocket/package.lisp b/deps/usocket/package.lisp
new file mode 100644 (file)
index 0000000..88e2545
--- /dev/null
@@ -0,0 +1,90 @@
+;;;; See the LICENSE file for licensing information.
+
+(defpackage :usocket
+  (:use :common-lisp #+abcl :java)
+  (:export   #:*version*
+
+             #:*wildcard-host*
+             #:*auto-port*
+
+             #:*remote-host* ; special variables (udp)
+             #:*remote-port*
+
+             #:+max-datagram-packet-size+
+
+             #:socket-connect ; socket constructors and methods
+             #:socket-listen
+             #:socket-accept
+             #:socket-close
+             #:socket-shutdown
+             #:get-local-address
+             #:get-peer-address
+             #:get-local-port
+             #:get-peer-port
+             #:get-local-name
+             #:get-peer-name
+
+             #:socket-send    ; udp function (send)
+             #:socket-receive ; udp function (receive)
+             #:socket-server  ; udp server
+             #:socket-option  ; 0.6.x
+
+             #:wait-for-input ; waiting for input-ready state (select() like)
+             #:make-wait-list
+             #:add-waiter
+             #:remove-waiter
+             #:remove-all-waiters
+
+             #:with-connected-socket ; convenience macros
+             #:with-server-socket
+             #:with-client-socket
+             #:with-socket-listener
+
+             #:usocket ; socket object and accessors
+             #:stream-usocket
+             #:stream-server-usocket
+             #:socket
+             #:socket-stream
+             #:datagram-usocket
+             #:socket-state ; 0.6.4
+
+             ;; predicates (for version 0.6 or 1.0 ?)
+             #:usocket-p
+             #:stream-usocket-p
+             #:stream-server-usocket-p
+             #:datagram-usocket-p
+
+             #:host-byte-order ; IPv4 utility functions
+             #:hbo-to-dotted-quad
+             #:hbo-to-vector-quad
+             #:vector-quad-to-dotted-quad
+             #:dotted-quad-to-vector-quad
+
+             #:vector-to-ipv6-host ; IPv6 utility functions
+             #:ipv6-host-to-vector
+
+             #:ip= ; IPv4+IPv6 utility function
+             #:ip/=
+
+             #:integer-to-octet-buffer ; Network utility functions
+             #:octet-buffer-to-integer
+             #:port-to-octet-buffer
+             #:port-from-octet-buffer
+             #:ip-to-octet-buffer
+             #:ip-from-octet-buffer
+
+             #:with-mapped-conditions
+
+             #:socket-condition ; conditions
+             #:ns-condition
+             #:socket-error ; errors
+             #:ns-error
+             #:unknown-condition
+             #:ns-unknown-condition
+             #:unknown-error
+             #:ns-unknown-error
+             #:socket-warning ; warnings (udp)
+
+             #:insufficient-implementation ; conditions regarding usocket support level
+             #:unsupported
+             #:unimplemented))
diff --git a/deps/usocket/server.lisp b/deps/usocket/server.lisp
new file mode 100644 (file)
index 0000000..e050d13
--- /dev/null
@@ -0,0 +1,108 @@
+;;;; $Id$
+;;;; $URL$
+
+(in-package :usocket)
+
+(defun socket-server (host port function &optional arguments
+                      &key in-new-thread (protocol :stream)
+                           ;; for udp
+                           (timeout 1) (max-buffer-size +max-datagram-packet-size+)
+                           ;; for tcp
+                           element-type reuse-address multi-threading
+                           name)
+  (let* ((real-host (or host *wildcard-host*))
+         (socket (ecase protocol
+                   (:stream
+                    (apply #'socket-listen
+                           `(,real-host ,port
+                             ,@(when element-type `(:element-type ,element-type))
+                             ,@(when reuse-address `(:reuse-address ,reuse-address)))))
+                   (:datagram
+                    (socket-connect nil nil :protocol :datagram
+                                    :local-host real-host
+                                    :local-port port)))))
+    (labels ((real-call ()
+               (ecase protocol
+                 (:stream
+                  (tcp-event-loop socket function arguments
+                                  :element-type element-type
+                                  :multi-threading multi-threading))
+                 (:datagram
+                  (udp-event-loop socket function arguments
+                                  :timeout timeout
+                                  :max-buffer-size max-buffer-size)))))
+      (if in-new-thread
+         (values (spawn-thread (or name "USOCKET Server") #'real-call) socket)
+         (real-call)))))
+
+(defvar *remote-host*)
+(defvar *remote-port*)
+
+(defun default-udp-handler (buffer) ; echo
+  (declare (type (simple-array (unsigned-byte 8) *) buffer))
+  buffer)
+
+(defun udp-event-loop (socket function &optional arguments
+                       &key timeout max-buffer-size)
+  (let ((buffer (make-array max-buffer-size :element-type '(unsigned-byte 8) :initial-element 0))
+        (sockets (list socket)))
+    (unwind-protect
+        (loop do
+          (multiple-value-bind (return-sockets real-time)
+              (wait-for-input sockets :timeout timeout)
+            (declare (ignore return-sockets))
+            (when real-time
+              (multiple-value-bind (recv n *remote-host* *remote-port*)
+                  (socket-receive socket buffer max-buffer-size)
+                (declare (ignore recv))
+                (if (plusp n)
+                    (progn
+                      (let ((reply
+                             (apply function (subseq buffer 0 n) arguments)))
+                        (when reply
+                          (replace buffer reply)
+                          (let ((n (socket-send socket buffer (length reply)
+                                                :host *remote-host*
+                                                :port *remote-port*)))
+                            (when (minusp n)
+                              (error "send error: ~A~%" n))))))
+                  (error "receive error: ~A" n))))
+            #+scl (when thread:*quitting-lisp* (return))
+            #+(and cmu mp) (mp:process-yield)))
+      (socket-close socket)
+      (values))))
+
+(defun default-tcp-handler (stream) ; null
+  (declare (type stream stream))
+  (terpri stream))
+
+(defun echo-tcp-handler (stream)
+  (loop
+     (when (listen stream)
+       (let ((line (read-line stream nil)))
+        (write-line line stream)
+        (force-output stream)))))
+
+(defun tcp-event-loop (socket function &optional arguments
+                       &key element-type multi-threading)
+  (let ((real-function #'(lambda (client-socket &rest arguments)
+                           (unwind-protect
+                               (multiple-value-bind (*remote-host* *remote-port*) (get-peer-name client-socket)
+                                 (apply function (socket-stream client-socket) arguments))
+                             (close (socket-stream client-socket))
+                             (socket-close client-socket)
+                             nil))))
+    (unwind-protect
+        (loop do
+          (let* ((client-socket (apply #'socket-accept
+                                       `(,socket ,@(when element-type `(:element-type ,element-type)))))
+                 (client-stream (socket-stream client-socket)))
+            (if multi-threading
+                (apply #'spawn-thread "USOCKET Client" real-function client-socket arguments)
+              (prog1 (apply real-function client-socket arguments)
+                (close client-stream)
+                (socket-close client-socket)))
+            #+scl (when thread:*quitting-lisp* (return))
+            #+(and cmu mp) (mp:process-yield)))
+      (socket-close socket)
+      (values))))
diff --git a/deps/usocket/test/package.lisp b/deps/usocket/test/package.lisp
new file mode 100644 (file)
index 0000000..15935dc
--- /dev/null
@@ -0,0 +1,13 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; See the LICENSE file for licensing information.
+
+(in-package :cl-user)
+
+(defpackage :usocket-test
+  (:use :common-lisp
+       :usocket
+       :regression-test)
+  (:export #:do-tests
+          #:run-usocket-tests))
diff --git a/deps/usocket/test/test-condition.lisp b/deps/usocket/test/test-condition.lisp
new file mode 100644 (file)
index 0000000..4b8410e
--- /dev/null
@@ -0,0 +1,28 @@
+;;;; $Id$
+;;;; $URL$
+
+(in-package :usocket-test)
+
+(deftest ns-host-not-found-error.1
+  (with-caught-conditions (usocket:ns-host-not-found-error nil)
+    (usocket:socket-connect "xxx" 123)
+    t)
+  nil)
+
+(deftest timeout-error.1
+  (with-caught-conditions (usocket:timeout-error nil)
+    (usocket:socket-connect "common-lisp.net" 81 :timeout 0)
+    t)
+  nil)
+
+(deftest connection-refused-error.1
+  (with-caught-conditions (usocket:connection-refused-error nil)
+    (usocket:socket-connect "common-lisp.net" 81)
+    t)
+  nil)
+
+(deftest operation-not-permitted-error.1
+  (with-caught-conditions (usocket:operation-not-permitted-error nil)
+    (usocket:socket-listen "0.0.0.0" 81)
+    t)
+  nil)
diff --git a/deps/usocket/test/test-datagram.lisp b/deps/usocket/test/test-datagram.lisp
new file mode 100644 (file)
index 0000000..dd4c3c7
--- /dev/null
@@ -0,0 +1,122 @@
+(in-package :usocket-test)
+
+(defvar *echo-server*)
+(defvar *echo-server-port*)
+
+(defun start-server ()
+  (multiple-value-bind (thread socket)
+      (usocket:socket-server "127.0.0.1" 0 #'identity nil
+                            :in-new-thread t
+                            :protocol :datagram)
+    (setq *echo-server* thread
+         *echo-server-port* (usocket:get-local-port socket))))
+
+(defparameter *max-buffer-size* 32)
+
+(defvar *send-buffer*
+  (make-array *max-buffer-size* :element-type '(unsigned-byte 8) :initial-element 0))
+
+(defvar *receive-buffer*
+  (make-array *max-buffer-size* :element-type '(unsigned-byte 8) :initial-element 0))
+
+(defun clean-buffers ()
+  (fill *send-buffer* 0)
+  (fill *receive-buffer* 0))
+
+;;; UDP Send Test #1: connected socket
+(deftest udp-send.1
+  (progn
+    (unless (and *echo-server* *echo-server-port*)
+      (start-server))
+    (let ((s (usocket:socket-connect "127.0.0.1" *echo-server-port* :protocol :datagram)))
+      (clean-buffers)
+      (replace *send-buffer* #(1 2 3 4 5))
+      (usocket:socket-send s *send-buffer* 5)
+      (usocket:wait-for-input s :timeout 3)
+      (multiple-value-bind (buffer size host port)
+         (usocket:socket-receive s *receive-buffer* *max-buffer-size*)
+       (declare (ignore buffer size host port))
+       (reduce #'+ *receive-buffer* :start 0 :end 5))))
+  15)
+
+;;; UDP Send Test #2: unconnected socket
+(deftest udp-send.2
+  (progn
+    (unless (and *echo-server* *echo-server-port*)
+      (start-server))
+    (let ((s (usocket:socket-connect nil nil :protocol :datagram)))
+      (clean-buffers)
+      (replace *send-buffer* #(1 2 3 4 5))
+      (usocket:socket-send s *send-buffer* 5 :host "127.0.0.1" :port *echo-server-port*)
+      (usocket:wait-for-input s :timeout 3)
+      (multiple-value-bind (buffer size host port)
+         (usocket:socket-receive s *receive-buffer* *max-buffer-size*)
+       (declare (ignore buffer size host port))
+       (reduce #'+ *receive-buffer* :start 0 :end 5))))
+  15)
+
+(deftest mark-h-david ; Mark H. David's remarkable UDP test code
+  (let* ((host "localhost")
+        (port 1111)
+        (server-sock
+         (usocket:socket-connect nil nil :protocol ':datagram :local-host host :local-port port))
+        (client-sock
+         (usocket:socket-connect host port :protocol ':datagram))
+        (octet-vector
+         (make-array 2 :element-type '(unsigned-byte 8) :initial-contents `(,(char-code #\O) ,(char-code #\K))))
+        (recv-octet-vector
+         (make-array 2 :element-type '(unsigned-byte 8))))
+    (usocket:socket-send client-sock octet-vector 2)
+    (usocket:socket-receive server-sock recv-octet-vector 2)
+    (prog1 (and (equalp octet-vector recv-octet-vector)
+               recv-octet-vector)
+      (usocket:socket-close server-sock)
+      (usocket:socket-close client-sock)))
+  #(79 75))
+
+(deftest frank-james ; Frank James' test code for LispWorks/UDP
+  (with-caught-conditions (#+win32 USOCKET:CONNECTION-RESET-ERROR
+                          #-win32 USOCKET:CONNECTION-REFUSED-ERROR
+                          nil)
+    (let ((sock (usocket:socket-connect "localhost" 1234
+                                        :protocol ':datagram :element-type '(unsigned-byte 8))))
+      (unwind-protect
+          (progn
+            (usocket:socket-send sock (make-array 16 :element-type '(unsigned-byte 8) :initial-element 0) 16)
+            (let ((buffer (make-array 16 :element-type '(unsigned-byte 8) :initial-element 0)))
+              (usocket:socket-receive sock buffer 16)))
+        (usocket:socket-close sock))))
+  nil)
+
+(defun frank-wfi-test ()
+  (let ((s (usocket:socket-connect nil nil
+                                   :protocol :datagram
+                                   :element-type '(unsigned-byte 8)
+                                   :local-port 8001)))
+    (unwind-protect
+        (do ((i 0 (1+ i))
+             (buffer (make-array 1024 :element-type '(unsigned-byte 8)
+                                 :initial-element 0))
+             (now (get-universal-time))
+             (done nil))
+            ((or done (= i 4))
+             nil)
+          (format t "~Ds ~D Waiting state ~S~%" (- (get-universal-time) now) i (usocket::state s))
+          (when (usocket:wait-for-input s :ready-only t :timeout 5)
+            (format t "~D state ~S~%" i (usocket::state s))
+            (handler-bind 
+                ((error (lambda (c) 
+                          (format t "socket-receive error: ~A~%" c)
+                          (break)
+                          nil)))
+              (multiple-value-bind (buffer count remote-host remote-port)
+                  (usocket:socket-receive s buffer 1024)
+                (handler-bind
+                    ((error (lambda (c)
+                               (format t "socket-send error: ~A~%" c)
+                               (break))))                             
+                  (when buffer 
+                    (usocket:socket-send s (subseq buffer 0 count) count
+                                         :host remote-host
+                                         :port remote-port)))))))
+      (usocket:socket-close s))))
diff --git a/deps/usocket/test/test-usocket.lisp b/deps/usocket/test/test-usocket.lisp
new file mode 100644 (file)
index 0000000..5c833af
--- /dev/null
@@ -0,0 +1,181 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; See LICENSE for licensing information.
+
+;;;; Usage: (usoct:run-usocket-tests) or (usoct:do-tests)
+
+(in-package :usocket-test)
+
+(defparameter +non-existing-host+ "1.2.3.4")
+(defparameter +unused-local-port+ 15213)
+
+(defparameter *fake-usocket*
+  (usocket::make-stream-socket :socket :my-socket
+                               :stream :my-stream))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defvar *common-lisp-net*
+    #.(first (usocket::get-hosts-by-name "common-lisp.net"))))
+
+(defvar *local-ip*)
+
+(defmacro with-caught-conditions ((expect throw) &body body)
+  `(catch 'caught-error
+     (handler-case
+         (handler-bind ((usocket:unsupported
+                         #'(lambda (c)
+                             (declare (ignore c)) (continue))))
+           (progn ,@body))
+       (usocket:unknown-error (c) (if (typep c ',expect)
+                                      (throw 'caught-error ,throw)
+                                    (progn
+                                      (describe c)
+                                      (describe
+                                       (usocket::usocket-real-error c))
+                                      c)))
+       (error (c) (if (typep c ',expect)
+                      (throw 'caught-error ,throw)
+                    (progn
+                      (describe c)
+                      c)))
+       (usocket:unknown-condition (c) (if (typep c ',expect)
+                                          (throw 'caught-error ,throw)
+                                        (progn
+                                          (describe c)
+                                          (describe
+                                           (usocket::usocket-real-condition c))
+                                          c)))
+       (condition (c) (if (typep c ',expect)
+                          (throw 'caught-error ,throw)
+                        (progn
+                          (describe c)
+                          c))))))
+
+(deftest make-socket.1 (usocket:socket *fake-usocket*) :my-socket)
+(deftest make-socket.2 (usocket:socket-stream *fake-usocket*) :my-stream)
+
+(deftest socket-no-connect.1
+  (with-caught-conditions (usocket:socket-error nil)
+    (usocket:socket-connect "127.0.0.0" +unused-local-port+ :timeout 1)
+    t)
+  nil)
+
+(deftest socket-no-connect.2
+  (with-caught-conditions (usocket:socket-error nil)
+    (usocket:socket-connect #(127 0 0 0) +unused-local-port+ :timeout 1)
+    t)
+  nil)
+
+(deftest socket-no-connect.3
+  (with-caught-conditions (usocket:socket-error nil)
+    (usocket:socket-connect 2130706432 +unused-local-port+ :timeout 1) ;; == #(127 0 0 0)
+    t)
+  nil)
+
+(deftest socket-failure.1
+  (with-caught-conditions (usocket:timeout-error nil)
+    (usocket:socket-connect 2130706432 +unused-local-port+ :timeout 1) ;; == #(127 0 0 0)
+    :unreach)
+  nil)
+
+(deftest socket-failure.2
+  (with-caught-conditions (usocket:timeout-error nil)
+    (usocket:socket-connect +non-existing-host+ 80 :timeout 1) ;; 80 = just a port
+    :unreach)
+  nil)
+
+;; let's hope c-l.net doesn't move soon, or that people start to
+;; test usocket like crazy..
+(deftest socket-connect.1
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect "common-lisp.net" 80)))
+      (unwind-protect
+          (when (typep sock 'usocket:usocket) t)
+        (usocket:socket-close sock))))
+  t)
+
+(deftest socket-connect.2
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect *common-lisp-net* 80)))
+      (unwind-protect
+          (when (typep sock 'usocket:usocket) t)
+        (usocket:socket-close sock))))
+  t)
+
+(deftest socket-connect.3
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect (usocket::host-byte-order *common-lisp-net*) 80)))
+      (unwind-protect
+          (when (typep sock 'usocket:usocket) t)
+        (usocket:socket-close sock))))
+  t)
+
+;; let's hope c-l.net doesn't change its software any time soon
+(deftest socket-stream.1
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect "common-lisp.net" 80)))
+      (unwind-protect
+          (progn
+            (format (usocket:socket-stream sock)
+                    "GET / HTTP/1.0~2%")
+            (force-output (usocket:socket-stream sock))
+            (subseq (read-line (usocket:socket-stream sock)) 0 15))
+        (usocket:socket-close sock))))
+  "HTTP/1.1 200 OK")
+
+(deftest socket-name.1
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect *common-lisp-net* 80)))
+      (unwind-protect
+          (usocket::get-peer-address sock)
+        (usocket:socket-close sock))))
+  #.*common-lisp-net*)
+
+(deftest socket-name.2
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect *common-lisp-net* 80)))
+      (unwind-protect
+          (usocket::get-peer-port sock)
+        (usocket:socket-close sock))))
+  80)
+
+(deftest socket-name.3
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect *common-lisp-net* 80)))
+      (unwind-protect
+          (usocket::get-peer-name sock)
+        (usocket:socket-close sock))))
+  #.*common-lisp-net* 80)
+
+#+ignore
+(deftest socket-name.4
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect *common-lisp-net* 80)))
+      (unwind-protect
+          (equal (usocket::get-local-address sock) *local-ip*)
+        (usocket:socket-close sock))))
+  t)
+
+(deftest socket-shutdown.1
+    (with-caught-conditions (nil nil)
+      (let ((sock (usocket:socket-connect *common-lisp-net* 80)))
+        (unwind-protect
+             (usocket::ignore-unsupported-warnings
+               (usocket:socket-shutdown sock :input))
+          (usocket:socket-close sock))
+        t))
+  t)
+
+(deftest socket-shutdown.2
+    (with-caught-conditions (nil nil)
+      (let ((sock (usocket:socket-connect *common-lisp-net* 80)))
+        (unwind-protect
+             (usocket::ignore-unsupported-warnings
+               (usocket:socket-shutdown sock :output))
+          (usocket:socket-close sock))
+        t))
+  t)
+
+(defun run-usocket-tests ()
+  (do-tests))
diff --git a/deps/usocket/test/wait-for-input.lisp b/deps/usocket/test/wait-for-input.lisp
new file mode 100644 (file)
index 0000000..2746230
--- /dev/null
@@ -0,0 +1,141 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; See LICENSE for licensing information.
+(in-package :usocket-test)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defparameter *wait-for-input-timeout* 2))
+
+(deftest wait-for-input.1
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect *common-lisp-net* 80))
+          (time (get-universal-time)))
+      (unwind-protect
+          (progn (usocket:wait-for-input sock :timeout *wait-for-input-timeout*)
+            (- (get-universal-time) time))
+        (usocket:socket-close sock))))
+  #.*wait-for-input-timeout*)
+
+(deftest wait-for-input.2
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect *common-lisp-net* 80))
+          (time (get-universal-time)))
+      (unwind-protect
+          (progn (usocket:wait-for-input sock :timeout *wait-for-input-timeout* :ready-only t)
+            (- (get-universal-time) time))
+        (usocket:socket-close sock))))
+  #.*wait-for-input-timeout*)
+
+(deftest wait-for-input.3
+  (with-caught-conditions (nil nil)
+    (let ((sock (usocket:socket-connect *common-lisp-net* 80)))
+      (unwind-protect
+          (progn
+            (format (usocket:socket-stream sock)
+                    "GET / HTTP/1.0~2%")
+            (force-output (usocket:socket-stream sock))
+            (usocket:wait-for-input sock :timeout *wait-for-input-timeout*)
+            (subseq (read-line (usocket:socket-stream sock)) 0 15))
+        (usocket:socket-close sock))))
+  "HTTP/1.1 200 OK")
+
+;;; Advanced W-F-I tests by Elliott Slaughter <elliottslaughter@gmail.com>
+
+(defvar *socket-server-port* 0)
+(defvar *socket-server-listen* nil)
+(defvar *socket-server-connection*)
+(defvar *socket-client-connection*)
+(defvar *output-p* t)
+
+(defun stage-1 ()
+  (unless *socket-server-listen*
+    (setf *socket-server-listen*
+         (socket-listen *wildcard-host* 0 :element-type '(unsigned-byte 8)))
+    (setf *socket-server-port* (get-local-port *socket-server-listen*)))
+
+  (setf *socket-server-connection*
+       (when (wait-for-input *socket-server-listen* :timeout 0 :ready-only t)
+         (socket-accept *socket-server-listen*)))
+  
+  (when *output-p* ; should be NIL
+    (format t "First time (before client connects) is ~s.~%"
+           *socket-server-connection*))
+
+  *socket-server-connection*)
+
+;; TODO: original test code have addition (:TIMEOUT 0) when doing the SOCKET-CONNECT,
+;; it seems cannot work on SBCL/Windows, need to investigate, but here we ignore it.
+
+(defun stage-2 ()
+  (setf *socket-client-connection*
+        (socket-connect "localhost" *socket-server-port* :protocol :stream
+                        :element-type '(unsigned-byte 8)))
+  (setf *socket-server-connection*
+       (when (wait-for-input *socket-server-listen* :timeout 0 :ready-only t)
+         #+(and win32 (or lispworks ecl sbcl))
+          (when *output-p*
+            (format t "%READY-P: ~D~%" (usocket::%ready-p *socket-server-listen*)))
+         (socket-accept *socket-server-listen*)))
+
+  (when *output-p* ; should be a usocket object
+    (format t "Second time (after client connects) is ~s.~%"
+           *socket-server-connection*))
+
+  *socket-server-connection*)
+
+(defun stage-3 ()
+  (setf *socket-server-connection*
+       (when (wait-for-input *socket-server-listen* :timeout 0 :ready-only t)
+         #+(and win32 (or lispworks ecl sbcl))
+          (when *output-p*
+            (format t "%READY-P: ~D~%" (usocket::%ready-p *socket-server-listen*)))
+         (socket-accept *socket-server-listen*)))
+
+  (when *output-p* ; should be NIL again
+    (format t "Third time (before second client) is ~s.~%"
+           *socket-server-connection*))
+
+  *socket-server-connection*)
+
+(deftest elliott-slaughter.1
+    (let ((*output-p* nil))
+      (let* ((s-1 (stage-1)) (s-2 (stage-2)) (s-3 (stage-3)))
+       (prog1 (and (null s-1) (usocket::usocket-p s-2) (null s-3))
+         (socket-close *socket-server-listen*)
+         (setf *socket-server-listen* nil))))
+  t)
+
+#|
+
+Issue elliott-slaughter.2 (WAIT-FOR-INPUT/win32 on TCP socket)
+
+W-F-I correctly found the inputs, but :READY-ONLY didn't work.
+
+|#
+(defun receive-each (connections)
+  (let ((ready (usocket:wait-for-input connections :timeout 0 :ready-only t)))
+    (loop for connection in ready
+       collect (read-line (usocket:socket-stream connection)))))
+
+(defun receive-all (connections)
+  (loop for messages = (receive-each connections)
+     then (receive-each connections)
+     while messages append messages))
+
+(defun send (connection message)
+  (format (usocket:socket-stream connection) "~a~%" message)
+  (force-output (usocket:socket-stream connection)))
+
+(defun server ()
+  (let* ((listen (usocket:socket-listen usocket:*wildcard-host* 12345))
+         (connection (usocket:socket-accept listen)))
+    (loop for messages = (receive-all connection) then (receive-all connection)
+       do (format t "Got messages:~%~s~%" messages)
+       do (sleep 1/50))))
+
+(defun client ()
+  (let ((connection (usocket:socket-connect "localhost" 12345)))
+    (loop for i from 0
+       do (send connection (format nil "This is message ~a." i))
+       do (sleep 1/100))))
diff --git a/deps/usocket/usocket-test.asd b/deps/usocket/usocket-test.asd
new file mode 100644 (file)
index 0000000..4c2edac
--- /dev/null
@@ -0,0 +1,25 @@
+;;;; -*- Mode: Lisp -*-
+;;;; $Id: usocket-test.asd 46 2006-02-06 20:50:07Z ehuelsmann $
+;;;; $URL: svn+ssh://common-lisp.net/project/usocket/svn/usocket/trunk/test/usocket-test.asd $
+
+;;;; See the LICENSE file for licensing information.
+
+(defsystem usocket-test
+    :name "usocket test"
+    :author "Erik Enge"
+    :maintainer "Chun Tian (binghe)"
+    :version "0.2.0"
+    :licence "MIT"
+    :description "Tests for usocket"
+    :depends-on (:usocket
+                 :rt)
+    :components ((:module "test"
+                 :serial t
+                 :components ((:file "package")
+                              (:file "test-usocket")
+                              (:file "test-condition")
+                              (:file "test-datagram")
+                              (:file "wait-for-input")))))
+
+(defmethod perform ((op test-op) (c (eql (find-system :usocket-test))))
+  (funcall (intern "DO-TESTS" "USOCKET-TEST")))
diff --git a/deps/usocket/usocket.asd b/deps/usocket/usocket.asd
new file mode 100644 (file)
index 0000000..aa4d134
--- /dev/null
@@ -0,0 +1,40 @@
+;;;; -*- Mode: Lisp -*-
+;;;;
+;;;; See the LICENSE file for licensing information.
+
+(defsystem usocket
+    :name "usocket"
+    :author "Erik Enge & Erik Huelsmann"
+    :maintainer "Chun Tian (binghe) & Hans Huebner"
+    :version "0.6.4"
+    :licence "MIT"
+    :description "Universal socket library for Common Lisp"
+    :depends-on (#+(or sbcl ecl) :sb-bsd-sockets)
+    :components ((:file "package")
+                (:module "vendor" :depends-on ("package")
+                 :components (#+mcl (:file "kqueue")
+                              #+mcl (:file "OpenTransportUDP")
+                              (:file "spawn-thread")
+                              (:file "split-sequence")))
+                (:file "usocket" :depends-on ("vendor"))
+                (:file "condition" :depends-on ("usocket"))
+                (:module "backend" :depends-on ("condition")
+                 :components (#+abcl           (:file "abcl")
+                              #+(or allegro cormanlisp)
+                                               (:file "allegro")
+                              #+clisp          (:file "clisp")
+                              #+clozure        (:file "clozure" :depends-on ("openmcl"))
+                              #+cmu            (:file "cmucl")
+                              #+ecl            (:file "ecl" :depends-on ("sbcl"))
+                              #+lispworks      (:file "lispworks")
+                              #+mcl            (:file "mcl")
+                              #+mocl           (:file "mocl")
+                              #+openmcl        (:file "openmcl")
+                              #+(or ecl sbcl)  (:file "sbcl")
+                              #+scl            (:file "scl")))
+                (:file "option" :depends-on ("backend"))
+                (:file "server" :depends-on ("backend" "option"))))
+
+(defmethod perform ((op test-op) (c (eql (find-system :usocket))))
+  (oos 'load-op :usocket-test)
+  (oos 'test-op :usocket-test))
diff --git a/deps/usocket/usocket.lisp b/deps/usocket/usocket.lisp
new file mode 100644 (file)
index 0000000..b393ea3
--- /dev/null
@@ -0,0 +1,679 @@
+;;;; See LICENSE for licensing information.
+
+(in-package :usocket)
+
+(defparameter *wildcard-host* #(0 0 0 0)
+  "Hostname to pass when all interfaces in the current system are to
+  be bound.  If this variable is passed to socket-listen, IPv6 capable
+  systems will also listen for IPv6 connections.")
+
+(defparameter *auto-port* 0
+  "Port number to pass when an auto-assigned port number is wanted.")
+
+(defparameter *version* #.(asdf:component-version (asdf:find-system :usocket)))
+
+(defconstant +max-datagram-packet-size+ 65507
+  "The theoretical maximum amount of data in a UDP datagram.
+
+The IPv4 UDP packets have a 16-bit length constraint, and IP+UDP header has 28-byte.
+
+IP_MAXPACKET = 65535,       /* netinet/ip.h */
+sizeof(struct ip) = 20,     /* netinet/ip.h */
+sizeof(struct udphdr) = 8,  /* netinet/udp.h */
+
+65535 - 20 - 8 = 65507
+
+(But for UDP broadcast, the maximum message size is limited by the MTU size of the underlying link)")
+
+(defclass usocket ()
+  ((socket
+    :initarg :socket
+    :accessor socket
+    :documentation "Implementation specific socket object instance.'")
+   (wait-list
+    :initform nil
+    :accessor wait-list
+    :documentation "WAIT-LIST the object is associated with.")
+   (state
+    :initform nil
+    :accessor state
+    :documentation "Per-socket return value for the `wait-for-input' function.
+
+The value stored in this slot can be any of
+ NIL          - not ready
+ :READ        - ready to read
+ :READ-WRITE  - ready to read and write
+ :WRITE       - ready to write
+
+The last two remain unused in the current version.
+")
+   #+(and win32 (or sbcl ecl lispworks))
+   (%ready-p
+    :initform nil
+    :accessor %ready-p
+    :documentation "Indicates whether the socket has been signalled
+as ready for reading a new connection.
+
+The value will be set to T by `wait-for-input-internal' (given the
+right conditions) and reset to NIL by `socket-accept'.
+
+Don't modify this slot or depend on it as it is really intended
+to be internal only.
+
+Note: Accessed, but not used for 'stream-usocket'.
+"
+   ))
+  (:documentation
+"The main socket class.
+
+Sockets should be closed using the `socket-close' method."))
+
+(defgeneric socket-state (socket)
+  (:documentation "NIL          - not ready
+:READ        - ready to read
+:READ-WRITE  - ready to read and write
+:WRITE       - ready to write"))
+
+(defmethod socket-state ((socket usocket))
+  (state socket))
+
+(defclass stream-usocket (usocket)
+   ((stream
+     :initarg :stream
+     :accessor socket-stream
+     :documentation "Stream instance associated with the socket."
+;;
+;;Iff an external-format was passed to `socket-connect' or `socket-listen'
+;;the stream is a flexi-stream. Otherwise the stream is implementation
+;;specific."
+))
+   (:documentation
+"Stream socket class.
+'
+Contrary to other sockets, these sockets may be closed either
+with the `socket-close' method or by closing the associated stream
+(which can be retrieved with the `socket-stream' accessor)."))
+
+(defclass stream-server-usocket (usocket)
+  ((element-type
+    :initarg :element-type
+    :initform #-lispworks 'character
+              #+lispworks 'base-char
+    :reader element-type
+    :documentation "Default element type for streams created by
+`socket-accept'."))
+  (:documentation "Socket which listens for stream connections to
+be initiated from remote sockets."))
+
+(defclass datagram-usocket (usocket)
+  ((connected-p :type boolean
+                :accessor connected-p
+                :initarg :connected-p)
+   #+(or cmu scl lispworks mcl
+         (and clisp ffi (not rawsock)))
+   (%open-p     :type boolean
+                :accessor %open-p
+                :initform t
+                :documentation "Flag to indicate if usocket is open,
+for GC on implementions operate on raw socket fd.")
+   #+(or lispworks mcl
+         (and clisp ffi (not rawsock)))
+   (recv-buffer :documentation "Private RECV buffer.")
+   #+(or lispworks mcl)
+   (send-buffer :documentation "Private SEND buffer."))
+  (:documentation "UDP (inet-datagram) socket"))
+
+(defun usocket-p (socket)
+  (typep socket 'usocket))
+
+(defun stream-usocket-p (socket)
+  (typep socket 'stream-usocket))
+
+(defun stream-server-usocket-p (socket)
+  (typep socket 'stream-server-usocket))
+
+(defun datagram-usocket-p (socket)
+  (typep socket 'datagram-usocket))
+
+(defun make-socket (&key socket)
+  "Create a usocket socket type from implementation specific socket."
+  (unless socket
+    (error 'invalid-socket-error))
+  (make-stream-socket :socket socket))
+
+(defun make-stream-socket (&key socket stream)
+  "Create a usocket socket type from implementation specific socket
+and stream objects.
+
+Sockets returned should be closed using the `socket-close' method or
+by closing the stream associated with the socket.
+"
+  (unless socket
+    (error 'invalid-socket-error))
+  (unless stream
+    (error 'invalid-socket-stream-error))
+  (make-instance 'stream-usocket
+                 :socket socket
+                 :stream stream))
+
+(defun make-stream-server-socket (socket &key (element-type
+                                               #-lispworks 'character
+                                               #+lispworks 'base-char))
+  "Create a usocket-server socket type from an
+implementation-specific socket object.
+
+The returned value is a subtype of `stream-server-usocket'.
+"
+  (unless socket
+    (error 'invalid-socket-error))
+  (make-instance 'stream-server-usocket
+                 :socket socket
+                 :element-type element-type))
+
+(defun make-datagram-socket (socket &key connected-p)
+  (unless socket
+    (error 'invalid-socket-error))
+  (make-instance 'datagram-usocket
+                 :socket socket
+                 :connected-p connected-p))
+
+(defgeneric socket-accept (socket &key element-type)
+  (:documentation
+      "Accepts a connection from `socket', returning a `stream-socket'.
+
+The stream associated with the socket returned has `element-type' when
+explicitly specified, or the element-type passed to `socket-listen' otherwise."))
+
+(defgeneric socket-close (usocket)
+  (:documentation "Close a previously opened `usocket'."))
+
+(defgeneric socket-shutdown (usocket direction)
+  (:documentation "Shutdown communication on the socket in DIRECTION.
+
+After a shutdown no input and/or output of the indicated DIRECTION
+can be performed on the `usocket'.
+
+DIRECTION should be either :INPUT or :OUTPUT
+
+Only guaranteed to work for STREAM-SOCKETs."))
+
+(defgeneric socket-send (usocket buffer length &key host port)
+  (:documentation "Send packets through a previously opend `usocket'."))
+
+(defgeneric socket-receive (usocket buffer length &key)
+  (:documentation "Receive packets from a previously opend `usocket'.
+
+Returns 4 values: (values buffer size host port)"))
+
+(defgeneric get-local-address (socket)
+  (:documentation "Returns the IP address of the socket."))
+
+(defgeneric get-peer-address (socket)
+  (:documentation
+   "Returns the IP address of the peer the socket is connected to."))
+
+(defgeneric get-local-port (socket)
+  (:documentation "Returns the IP port of the socket.
+
+This function applies to both `stream-usocket' and `server-stream-usocket'
+type objects."))
+
+(defgeneric get-peer-port (socket)
+  (:documentation "Returns the IP port of the peer the socket to."))
+
+(defgeneric get-local-name (socket)
+  (:documentation "Returns the IP address and port of the socket as values.
+
+This function applies to both `stream-usocket' and `server-stream-usocket'
+type objects."))
+
+(defgeneric get-peer-name (socket)
+  (:documentation
+   "Returns the IP address and port of the peer
+the socket is connected to as values."))
+
+(defmacro with-connected-socket ((var socket) &body body)
+  "Bind `socket' to `var', ensuring socket destruction on exit.
+
+`body' is only evaluated when `var' is bound to a non-null value.
+
+The `body' is an implied progn form."
+  `(let ((,var ,socket))
+     (unwind-protect
+         (when ,var
+           (with-mapped-conditions (,var)
+             ,@body))
+       (when ,var
+         (socket-close ,var)))))
+
+(defmacro with-client-socket ((socket-var stream-var &rest socket-connect-args)
+                              &body body)
+  "Bind the socket resulting from a call to `socket-connect' with
+the arguments `socket-connect-args' to `socket-var' and if `stream-var' is
+non-nil, bind the associated socket stream to it."
+  `(with-connected-socket (,socket-var (socket-connect ,@socket-connect-args))
+     ,(if (null stream-var)
+          `(progn ,@body)
+           `(let ((,stream-var (socket-stream ,socket-var)))
+              ,@body))))
+
+(defmacro with-server-socket ((var server-socket) &body body)
+  "Bind `server-socket' to `var', ensuring socket destruction on exit.
+
+`body' is only evaluated when `var' is bound to a non-null value.
+
+The `body' is an implied progn form."
+  `(with-connected-socket (,var ,server-socket)
+     ,@body))
+
+(defmacro with-socket-listener ((socket-var &rest socket-listen-args)
+                                &body body)
+  "Bind the socket resulting from a call to `socket-listen' with arguments
+`socket-listen-args' to `socket-var'."
+  `(with-server-socket (,socket-var (socket-listen ,@socket-listen-args))
+     ,@body))
+
+(defstruct (wait-list (:constructor %make-wait-list))
+  %wait     ;; implementation specific
+  waiters ;; the list of all usockets
+  map)  ;; maps implementation sockets to usockets
+
+;; Implementation specific:
+;;
+;;  %setup-wait-list
+;;  %add-waiter
+;;  %remove-waiter
+
+(defun make-wait-list (waiters)
+  (let ((wl (%make-wait-list)))
+    (setf (wait-list-map wl) (make-hash-table))
+    (%setup-wait-list wl)
+    (dolist (x waiters wl)
+      (add-waiter wl x))))
+
+(defun add-waiter (wait-list input)
+  (setf (gethash (socket input) (wait-list-map wait-list)) input
+        (wait-list input) wait-list)
+  (pushnew input (wait-list-waiters wait-list))
+  (%add-waiter wait-list input))
+
+(defun remove-waiter (wait-list input)
+  (%remove-waiter wait-list input)
+  (setf (wait-list-waiters wait-list)
+        (remove input (wait-list-waiters wait-list))
+        (wait-list input) nil)
+  (remhash (socket input) (wait-list-map wait-list)))
+
+(defun remove-all-waiters (wait-list)
+  (dolist (waiter (wait-list-waiters wait-list))
+    (%remove-waiter wait-list waiter))
+  (setf (wait-list-waiters wait-list) nil)
+  (clrhash (wait-list-map wait-list)))
+
+(defun wait-for-input (socket-or-sockets &key timeout ready-only)
+  "Waits for one or more streams to become ready for reading from
+the socket.  When `timeout' (a non-negative real number) is
+specified, wait `timeout' seconds, or wait indefinitely when
+it isn't specified.  A `timeout' value of 0 (zero) means polling.
+
+Returns two values: the first value is the list of streams which
+are readable (or in case of server streams acceptable).  NIL may
+be returned for this value either when waiting timed out or when
+it was interrupted (EINTR).  The second value is a real number
+indicating the time remaining within the timeout period or NIL if
+none.
+
+Without the READY-ONLY arg, WAIT-FOR-INPUT will return all sockets in
+the original list you passed it. This prevents a new list from being
+consed up. Some users of USOCKET were reluctant to use it if it
+wouldn't behave that way, expecting it to cost significant performance
+to do the associated garbage collection.
+
+Without the READY-ONLY arg, you need to check the socket STATE slot for
+the values documented in usocket.lisp in the usocket class."
+  (unless (wait-list-p socket-or-sockets)
+    (let ((wl (make-wait-list (if (listp socket-or-sockets)
+                                  socket-or-sockets (list socket-or-sockets)))))
+      (multiple-value-bind
+            (socks to)
+          (wait-for-input wl :timeout timeout :ready-only ready-only)
+        (return-from wait-for-input
+          (values (if ready-only socks socket-or-sockets) to)))))
+  (let* ((start (get-internal-real-time))
+         (sockets-ready 0))
+    (dolist (x (wait-list-waiters socket-or-sockets))
+      (when (setf (state x)
+                  #+(and win32 (or sbcl ecl)) nil ; they cannot rely on LISTEN
+                  #-(and win32 (or sbcl ecl))
+                  (if (and (stream-usocket-p x)
+                           (listen (socket-stream x)))
+                      :read
+                      nil))
+        (incf sockets-ready)))
+    ;; the internal routine is responsibe for
+    ;; making sure the wait doesn't block on socket-streams of
+    ;; which theready- socket isn't ready, but there's space left in the
+    ;; buffer
+    (wait-for-input-internal socket-or-sockets
+                             :timeout (if (zerop sockets-ready) timeout 0))
+    (let ((to-result (when timeout
+                       (let ((elapsed (/ (- (get-internal-real-time) start)
+                                         internal-time-units-per-second)))
+                         (when (< elapsed timeout)
+                           (- timeout elapsed))))))
+      (values (if ready-only
+                  (remove-if #'null (wait-list-waiters socket-or-sockets) :key #'state)
+                  socket-or-sockets)
+              to-result))))
+
+;;
+;; Data utility functions
+;;
+
+(defun integer-to-octet-buffer (integer buffer octets &key (start 0))
+  (do ((b start (1+ b))
+       (i (ash (1- octets) 3) ;; * 8
+          (- i 8)))
+      ((> 0 i) buffer)
+    (setf (aref buffer b)
+          (ldb (byte 8 i) integer))))
+
+(defun octet-buffer-to-integer (buffer octets &key (start 0))
+  (let ((integer 0))
+    (do ((b start (1+ b))
+         (i (ash (1- octets) 3) ;; * 8
+            (- i 8)))
+        ((> 0 i)
+         integer)
+      (setf (ldb (byte 8 i) integer)
+            (aref buffer b)))))
+
+(defmacro port-to-octet-buffer (port buffer &key (start 0))
+  `(integer-to-octet-buffer ,port ,buffer 2 :start ,start))
+
+(defmacro ip-to-octet-buffer (ip buffer &key (start 0))
+  `(integer-to-octet-buffer (host-byte-order ,ip) ,buffer 4 :start ,start))
+
+(defmacro port-from-octet-buffer (buffer &key (start 0))
+  `(octet-buffer-to-integer ,buffer 2 :start ,start))
+
+(defmacro ip-from-octet-buffer (buffer &key (start 0))
+  `(octet-buffer-to-integer ,buffer 4 :start ,start))
+
+;;
+;; IPv4 utility functions
+;;
+
+(defun list-of-strings-to-integers (list)
+  "Take a list of strings and return a new list of integers (from
+parse-integer) on each of the string elements."
+  (let ((new-list nil))
+    (dolist (element (reverse list))
+      (push (parse-integer element) new-list))
+    new-list))
+
+(defun ip-address-string-p (string)
+  "Return a true value if the given string could be an IP address."
+  (every (lambda (char)
+           (or (digit-char-p char)
+               (eql char #\.)))
+         string))
+
+(defun hbo-to-dotted-quad (integer)
+  "Host-byte-order integer to dotted-quad string conversion utility."
+  (let ((first (ldb (byte 8 24) integer))
+        (second (ldb (byte 8 16) integer))
+        (third (ldb (byte 8 8) integer))
+        (fourth (ldb (byte 8 0) integer)))
+    (format nil "~A.~A.~A.~A" first second third fourth)))
+
+(defun hbo-to-vector-quad (integer)
+  "Host-byte-order integer to dotted-quad string conversion utility."
+  (let ((first (ldb (byte 8 24) integer))
+        (second (ldb (byte 8 16) integer))
+        (third (ldb (byte 8 8) integer))
+        (fourth (ldb (byte 8 0) integer)))
+    (vector first second third fourth)))
+
+(defun vector-quad-to-dotted-quad (vector)
+  (format nil "~A.~A.~A.~A"
+          (aref vector 0)
+          (aref vector 1)
+          (aref vector 2)
+          (aref vector 3)))
+
+(defun dotted-quad-to-vector-quad (string)
+  (let ((list (list-of-strings-to-integers (split-sequence #\. string))))
+    (vector (first list) (second list) (third list) (fourth list))))
+
+(defgeneric host-byte-order (address))
+(defmethod host-byte-order ((string string))
+  "Convert a string, such as 192.168.1.1, to host-byte-order,
+such as 3232235777."
+  (let ((list (list-of-strings-to-integers (split-sequence #\. string))))
+    (+ (* (first list) 256 256 256) (* (second list) 256 256)
+       (* (third list) 256) (fourth list))))
+
+(defmethod host-byte-order ((vector vector))
+  "Convert a vector, such as #(192 168 1 1), to host-byte-order, such as
+3232235777."
+  (+ (* (aref vector 0) 256 256 256) (* (aref vector 1) 256 256)
+     (* (aref vector 2) 256) (aref vector 3)))
+
+(defmethod host-byte-order ((int integer))
+  int)
+
+;;
+;; IPv6 utility functions
+;;
+
+(defun vector-to-ipv6-host (vector)
+  (with-output-to-string (*standard-output*)
+    (loop with zeros-collapsed-p
+          with collapsing-zeros-p
+          for i below 16 by 2
+          for word = (+ (ash (aref vector i) 8)
+                        (aref vector (1+ i)))
+          do (cond
+               ((and (zerop word)
+                     (not collapsing-zeros-p)
+                     (not zeros-collapsed-p))
+                (setf collapsing-zeros-p t))
+               ((or (not (zerop word))
+                    zeros-collapsed-p)
+                (when collapsing-zeros-p
+                  (write-string ":")
+                  (setf collapsing-zeros-p nil
+                        zeros-collapsed-p t))
+                (format t "~:[~;:~]~X" (plusp i) word)))
+          finally (when collapsing-zeros-p
+                    (write-string "::")))))
+
+(defun split-ipv6-address (string)
+  (let ((pos 0)
+        word
+        double-colon-seen-p
+        words-before-double-colon
+        words-after-double-colon)
+    (loop
+      (multiple-value-setq (word pos) (parse-integer string :radix 16 :junk-allowed t :start pos))
+      (labels ((at-end-p ()
+                 (= pos (length string)))
+               (looking-at-colon-p ()
+                 (char= (char string pos) #\:))
+               (ensure-colon ()
+                 (unless (looking-at-colon-p)
+                   (error "unsyntactic IPv6 address string ~S, expected a colon at position ~D"
+                          string pos))
+                 (incf pos)))
+        (cond
+          ((null word)
+           (when double-colon-seen-p
+             (error "unsyntactic IPv6 address string ~S, can only have one double-colon filler mark"
+                    string))
+           (setf double-colon-seen-p t))
+          (double-colon-seen-p
+           (push word words-after-double-colon))
+          (t
+           (push word words-before-double-colon)))
+        (if (at-end-p)
+            (return (list (nreverse words-before-double-colon) (nreverse words-after-double-colon)))
+            (ensure-colon))))))
+
+(defun ipv6-host-to-vector (string)
+  (assert (> (length string) 2) ()
+          "Unsyntactic IPv6 address literal ~S, expected at least three characters" string)
+  (destructuring-bind (words-before-double-colon words-after-double-colon)
+      (split-ipv6-address (concatenate 'string
+                                       (when (eql (char string 0) #\:)
+                                         "0")
+                                       string
+                                       (when (eql (char string (1- (length string))) #\:)
+                                         "0")))
+    (let ((number-of-words-specified (+ (length words-before-double-colon) (length words-after-double-colon))))
+      (assert (<= number-of-words-specified 8) ()
+              "Unsyntactic IPv6 address literal ~S, too many colon separated address components" string)
+      (assert (or (= number-of-words-specified 8) words-after-double-colon) ()
+              "Unsyntactic IPv6 address literal ~S, too few address components and no double-colon filler found" string)
+      (loop with vector = (make-array 16 :element-type '(unsigned-byte 8))
+            for i below 16 by 2
+            for word in (append words-before-double-colon
+                                (make-list (- 8 number-of-words-specified) :initial-element 0)
+                                words-after-double-colon)
+            do (setf (aref vector i) (ldb (byte 8 8) word)
+                     (aref vector (1+ i)) (ldb (byte 8 0) word))
+            finally (return vector)))))
+
+(defun host-to-hostname (host)
+  "Translate a string, vector quad or 16 byte IPv6 address to a
+stringified hostname."
+  (etypecase host
+    (string host)
+    ((or (vector t 4)
+         (array (unsigned-byte 8) (4)))
+     (vector-quad-to-dotted-quad host))
+    ((or (vector t 16)
+         (array (unsigned-byte 8) (16)))
+     (vector-to-ipv6-host host))
+    (integer (hbo-to-dotted-quad host))
+    (null "0.0.0.0")))
+
+(defun ip= (ip1 ip2)
+  (etypecase ip1
+    (string (string= ip1 (host-to-hostname ip2)))
+    ((or (vector t 4)
+         (array (unsigned-byte 8) (4))
+         (vector t 16)
+         (array (unsigned-byte 8) (16)))
+     (equalp ip1 ip2))
+    (integer (= ip1 (host-byte-order ip2)))))
+
+(defun ip/= (ip1 ip2)
+  (not (ip= ip1 ip2)))
+
+;;
+;; DNS helper functions
+;;
+
+(defun get-host-by-name (name)
+  (let ((hosts (get-hosts-by-name name)))
+    (car hosts)))
+
+(defun get-random-host-by-name (name)
+  (let ((hosts (get-hosts-by-name name)))
+    (when hosts
+      (elt hosts (random (length hosts))))))
+
+(defun host-to-vector-quad (host)
+  "Translate a host specification (vector quad, dotted quad or domain name)
+to a vector quad."
+  (etypecase host
+    (string (let* ((ip (when (ip-address-string-p host)
+                         (dotted-quad-to-vector-quad host))))
+              (if (and ip (= 4 (length ip)))
+                  ;; valid IP dotted quad?
+                  ip
+                (get-random-host-by-name host))))
+    ((or (vector t 4)
+         (array (unsigned-byte 8) (4)))
+     host)
+    (integer (hbo-to-vector-quad host))))
+
+(defun host-to-hbo (host)
+  (etypecase host
+    (string (let ((ip (when (ip-address-string-p host)
+                        (dotted-quad-to-vector-quad host))))
+              (if (and ip (= 4 (length ip)))
+                  (host-byte-order ip)
+                  (host-to-hbo (get-host-by-name host)))))
+    ((or (vector t 4)
+         (array (unsigned-byte 8) (4)))
+     (host-byte-order host))
+    (integer host)))
+
+;;
+;; Other utility functions
+;;
+
+(defun split-timeout (timeout &optional (fractional 1000000))
+  "Split real value timeout into seconds and microseconds.
+Optionally, a different fractional part can be specified."
+  (multiple-value-bind
+      (secs sec-frac)
+      (truncate timeout 1)
+    (values secs
+            (truncate (* fractional sec-frac) 1))))
+
+;;
+;; Setting of documentation for backend defined functions
+;;
+
+;; Documentation for the function
+;;
+;; (defun SOCKET-CONNECT (host port &key element-type nodelay some-other-keys...) ..)
+;;
+(setf (documentation 'socket-connect 'function)
+      "Connect to `host' on `port'.  `host' is assumed to be a string or
+an IP address represented in vector notation, such as #(192 168 1 1).
+`port' is assumed to be an integer.
+
+`element-type' specifies the element type to use when constructing the
+stream associated with the socket.  The default is 'character.
+
+`nodelay' Allows to disable/enable Nagle's algorithm (http://en.wikipedia.org/wiki/Nagle%27s_algorithm).
+If this parameter is omitted, the behaviour is inherited from the
+CL implementation (in most cases, Nagle's algorithm is
+enabled by default, but for example in ACL it is disabled).
+If the parmeter is specified, one of these three values is possible:
+  T - Disable Nagle's algorithm; signals an UNSUPPORTED
+      condition if the implementation does not support explicit
+      manipulation with that option.
+  NIL - Leave Nagle's algorithm enabled on the socket;
+      signals an UNSUPPORTED condition if the implementation does
+      not support explicit manipulation with that option.
+  :IF-SUPPORTED - Disables Nagle's algorithm if the implementation
+      allows this, otherwises just ignore this option.
+
+Returns a usocket object.")
+
+;; Documentation for the function
+;;
+;; (defun SOCKET-LISTEN (host port &key reuseaddress backlog element-type) ..)
+;;###FIXME: extend with default-element-type
+(setf (documentation 'socket-listen 'function)
+      "Bind to interface `host' on `port'. `host' should be the
+representation of an ready-interface address.  The implementation is
+not required to do an address lookup, making no guarantees that
+hostnames will be correctly resolved.  If `*wildcard-host*' or NIL is
+passed for `host', the socket will be bound to all available
+interfaces for the system.  `port' can be selected by the IP stack by
+passing `*auto-port*'.
+
+Returns an object of type `stream-server-usocket'.
+
+`reuse-address' and `backlog' are advisory parameters for setting socket
+options at creation time. `element-type' is the element type of the
+streams to be created by `socket-accept'.  `reuseaddress' is supported for
+backward compatibility (but deprecated); when both `reuseaddress' and
+`reuse-address' have been specified, the latter takes precedence.
+")
diff --git a/deps/usocket/vendor/OpenTransportUDP.lisp b/deps/usocket/vendor/OpenTransportUDP.lisp
new file mode 100644 (file)
index 0000000..5dc3cdc
--- /dev/null
@@ -0,0 +1,146 @@
+;;;-*-Mode: LISP; Package: CCL -*-
+;;
+;;; OpenTransportUDP.lisp
+;;; Copyright 2012 Chun Tian (binghe) <binghe.lisp@gmail.com>
+
+;;; UDP extension to OpenTransport.lisp (with some TCP patches)
+
+(in-package "CCL")
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (require :opentransport))
+
+;; MCL Issue 28: Passive TCP streams should be able to listen to the loopback interface
+;; see http://code.google.com/p/mcl/issues/detail?id=28 for details
+
+(defparameter *passive-interface-address* NIL
+  "Address to use for passive connections - optionally bind to loopback address while opening a tcp stream")
+
+(advise local-interface-ip-address
+  (or *passive-interface-address* (:do-it))
+  :when :around :name 'override-local-interface-ip-address)
+
+;; MCL Issue 29: Passive TCP connections on OS assigned ports
+;; see http://code.google.com/p/mcl/issues/detail?id=29 for details
+(advise ot-conn-tcp-passive-connect
+  (destructuring-bind (conn port &optional (allow-reuse t)) arglist
+    (declare (ignore allow-reuse))
+    (if (eql port #$kOTAnyInetAddress)
+       ;; Avoids registering a proxy for port 0 but instead registers one for the true port:
+       (multiple-value-bind (proxy result)
+           (let* ((*opentransport-class-proxies* NIL) ; makes ot-find-proxy return NIL
+                  (result (:do-it)) ;; pushes onto *opentransport-class-proxies*
+                  (proxy (prog1
+                             (pop *opentransport-class-proxies*)
+                           (assert (not *opentransport-class-proxies*))))
+                  (context (cdr proxy))
+                  (tmpconn (make-ot-conn :context context 
+                                         :endpoint (pref context :ot-context.ref)))
+                  (localaddress (ot-conn-tcp-get-addresses tmpconn)))
+             (declare (dynamic-extent tmpconn))
+             ;; replace original set in body of function
+             (setf (ot-conn-local-address conn) localaddress)
+             (values
+              (cons localaddress context)
+              result))
+         ;; need to be outside local binding of *opentransport-class-proxies* 
+         (without-interrupts
+             (push proxy *opentransport-class-proxies*))
+         result)
+       (:do-it)))
+  :when :around :name 'ot-conn-tcp-passive-connect-any-address)
+
+(defun open-udp-socket (&key local-address local-port)
+  (init-opentransport)
+  (let (endpoint ; TODO: opentransport-alloc-endpoint-from-freelist
+       (err #$kOTNoError)
+       (configptr (ot-cloned-configuration traps::$kUDPName)))
+    (rlet ((errP :osstatus))
+      (setq endpoint #+carbon-compat (#_OTOpenEndpointInContext configptr 0 (%null-ptr) errP *null-ptr*)
+                    #-carbon-compat (#_OTOpenEndpoint configptr 0 (%null-ptr) errP)
+           err (pref errP :osstatus))
+      (if (eql err #$kOTNoError)
+         (let* ((context (ot-make-endpoint-context endpoint nil nil)) ; no notifier, not minimal
+                (conn (make-ot-conn :context context :endpoint endpoint)))
+           (macrolet ((check-ot-error-return (error-context)
+                        `(unless (eql (setq err (pref errP :osstatus)) #$kOTNoError)
+                           (values (ot-error err ,error-context)))))
+             (setf (ot-conn-bindreq conn) 
+                   #-carbon-compat (#_OTAlloc endpoint #$T_BIND #$T_ADDR errP)
+                   #+carbon-compat (#_OTAllocInContext endpoint #$T_BIND #$T_ADDR errP *null-ptr*)
+                   )
+             (check-ot-error-return :alloc)
+             (setf (ot-conn-bindret conn) 
+                   #-carbon-compat (#_OTAlloc endpoint #$T_BIND #$T_ADDR errP)
+                   #+carbon-compat (#_OTAllocInContext endpoint #$T_BIND #$T_ADDR errP *null-ptr*)
+                   )
+             (check-ot-error-return :alloc)
+             (setf (ot-conn-options conn) 
+                   #-carbon-compat (#_OTAlloc endpoint #$T_OPTMGMT #$T_OPT errP)
+                   #+carbon-compat (#_OTAllocInContext endpoint #$T_OPTMGMT #$T_OPT errP *null-ptr*)
+                   )
+             (check-ot-error-return :alloc))
+           ;; BIND to local address (for UDP server)
+           (when local-port ; local-address
+             (let* ((host (or local-address (local-interface-ip-address)))
+                    (port (tcp-service-port-number local-port))
+                    (localaddress `(:tcp ,host ,port))
+                    (bindreq (ot-conn-bindreq conn))
+                    (bindret (ot-conn-bindret conn)))
+               (let* ((netbuf (pref bindreq :tbind.addr)))
+                 (declare (dynamic-extent netbuf))
+                 (setf (pref netbuf :tnetbuf.len) (record-length :inetaddress)
+                       (pref bindreq :tbind.qlen) 5)       ; arbitrary qlen
+                 (#_OTInitInetAddress (pref netbuf :tnetbuf.buf) port host)
+                 (setf (pref context :ot-context.completed) nil)
+                 (unless (= (setq err (#_OTBind endpoint bindreq bindret)) #$kOTNoError)
+                   (ot-error err :bind)))
+               (setf (ot-conn-local-address conn) localaddress)))
+           conn)
+       (ot-error err :create)))))
+
+(defun make-TUnitData (endpoint)
+  "create the send/recv buffer for UDP sockets"
+  (let ((err #$kOTNoError))
+    (rlet ((errP :osstatus))
+      (macrolet ((check-ot-error-return (error-context)
+                  `(unless (eql (setq err (pref errP :osstatus)) #$kOTNoError)
+                     (values (ot-error err ,error-context)))))
+       (let ((udata #-carbon-compat (#_OTAlloc endpoint #$T_UNITDATA #$T_ALL errP)
+                    #+carbon-compat (#_OTAllocInContext endpoint #$T_UNITDATA #$T_ALL errP *null-ptr*)))
+         (check-ot-error-return :alloc)
+         udata)))))
+
+(defun send-message (conn data buffer size host port &optional (offset 0))
+  ;; prepare dest address
+  (let ((addr (pref data :tunitdata.addr)))
+    (declare (dynamic-extent addr))
+    (setf (pref addr :tnetbuf.len) (record-length :inetaddress))
+    (#_OTInitInetAddress (pref addr :tnetbuf.buf) port host))
+  ;; prepare data buffer
+  (let* ((udata (pref data :tunitdata.udata))
+        (outptr (pref udata :tnetbuf.buf)))
+    (declare (dynamic-extent udata))
+    (%copy-ivector-to-ptr buffer offset outptr 0 size)
+    (setf (pref udata :tnetbuf.len) size))
+  ;; send the packet
+  (let* ((endpoint (ot-conn-endpoint conn))
+        (result (#_OTSndUData endpoint data)))
+    (the fixnum result)))
+
+(defun receive-message (conn data buffer length)
+  (let* ((endpoint (ot-conn-endpoint conn))
+        (err (#_OTRcvUData endpoint data *null-ptr*)))
+    (if (eql err #$kOTNoError)
+       (let* (;(addr (pref data :tunitdata.addr))
+              (udata (pref data :tunitdata.udata))
+              (inptr (pref udata :tnetbuf.buf))
+              (read-bytes (pref udata :tnetbuf.len))
+              (buffer (or buffer (make-array read-bytes :element-type '(unsigned-byte 8))))
+              (length (or length (length buffer)))
+              (actual-size (min read-bytes length)))
+         (%copy-ptr-to-ivector inptr 0 buffer 0 actual-size)
+         (values buffer
+                 actual-size
+                 0 0)) ; TODO: retrieve address and port
+      (ot-error err :receive)))) ; TODO: use OTRcvUDErr instead
diff --git a/deps/usocket/vendor/kqueue.lisp b/deps/usocket/vendor/kqueue.lisp
new file mode 100644 (file)
index 0000000..53ff8af
--- /dev/null
@@ -0,0 +1 @@
+;;;-*-Mode: LISP; Package: CCL -*-\r;;\r;; KQUEUE.LISP\r;;\r;; KQUEUE - BSD kernel event notification mechanism support for Common LISP.\r;; Copyright (C) 2007 Terje Norderhaug <terje@in-progress.com>\r;; Released under LGPL - see <http://www.gnu.org>.\r;; Alternative licensing available upon request.\r;; \r;; DISCLAIMER: The user of this module should understand that executing code is a potentially hazardous \r;; activity, and that many dangers and obstacles, marked or unmarked, may exist within this code.\r;; As a condition of your use of the module, you assume all risk of personal injury, death, or property\r;; loss, and all other bad things that may happen, even if caused by negligence, ignorance or stupidity.\r;; The author is is no way responsible, and besides, does not have "deep pockets" nor any spare change.\r;;\r;; Version: 0.20 alpha (July 26, 2009) - subject to major revisions, so consider yourself warned.\r;; Tested with Macintosh Common LISP 5.1 and 5.2, but is intended to be platform and system independent in the future.\r;;\r;; Email feedback and improvements to <terje@in-progress.com>.\r;; Updated versions will be available from <http://www.in-progress.com/src/>.\r;;\r;; RELATED IMPLEMENTATIONS\r;; There is another kevent.lisp for other platforms by Risto Laakso (merge?).\r;; Also a Scheme kevent.ss by Jose Antonio Ortega.\r;;\r;; SEE ALSO:\r;; http://people.freebsd.org/~jlemon/papers/kqueue.pdf\r;; http://developer.apple.com/samplecode/FileNotification/index.html\r;; The Man page for kqueue() or kevent().\r;; PyKQueue - Python OO interface to KQueue.\r;; LibEvent - an event notification library in C by Niels Provos.\r;; Liboop - another abstract library in C on top of kevent or other kernel notification.\r\r#| HISTORY:\r\r2007-Oct-18 terje version 0.1 released on the Info-MCL mailing list.\r2008-Aug-21 terje load-framework-bundle is not needed under MCL 5.2\r2008-Aug-21 terje rename get-addr to lookup-function-in-bundle (only for pre MCL 5.2)\r2009-Jul-19 terje uses kevent-error condition and strerror.\r2009-Jul-24 terje reports errors unless nil-if-not-found in lookup-function-in-bundle. \r2009-Jul-24 terje kevent :variant for C's intptr_t type for 64bit (and osx 10.5) compatibility.\r2009-Jul-25 terje 64bit support, dynamically determined for PPC. Kudos to Glen Foy for helping out.\r2009-Jul-25 terje make-kevent function.\r|#\r\r#| IMPLEMENTATION NOTES:\r\rkevents are copied into and from the kernel, so the records don't have to be kept in the app!\rkevents does not work in OSX before 10.3.\r*kevent-record* has to be explcitly set to :kevent64 to work on 64bit intel macs.\rConsider using sysctlbyname() to test for 64bit, \r combining hw.cpu64bit_capable, hw.optional.x86_64 and hw.optional.64bitops\r|#\r\r(in-package :ccl)\r\r;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\r\r#-ccl-5.2 ; has been added to MCL 5.2\r(defmethod load-framework-bundle ((framework-name string) &key (load-executable t))\r  ;; FRAMWORK CALL FUNCTIONALITY FROM BSD.LISP\r  ;; (C) 2003 Brendan Burns <bburns@cs.umass.edu>\r  ;; Released under LGPL.\r  (with-cfstrs ((framework framework-name))\r    (let ((err 0)\r          (baseURL nil)\r          (bundleURL nil)\r          (result nil))\r      (rlet ((folder :fsref))\r        ;; Find the folder holding the bundle\r        (setf err (#_FSFindFolder #$kOnAppropriateDisk #$kFrameworksFolderType \r                   t folder))\r        \r        ;; if everything's cool, make a URL for it\r        (when (zerop err)\r          (setf baseURL (#_CFURLCreateFromFSRef (%null-ptr) folder))\r          (if (%null-ptr-p baseURL) \r            (setf err #$coreFoundationUnknownErr)))\r        \r        ;; if everything's cool, make a URL for the bundle\r        (when (zerop err)\r          (setf bundleURL (#_CFURLCreateCopyAppendingPathComponent (%null-ptr) \r                           baseURL framework nil))\r          (if (%null-ptr-p bundleURL) \r            (setf err #$coreFoundationUnknownErr)))\r        \r        ;; if everything's cool, load it\r        (when (zerop err)\r          (setf result (#_CFBundleCreate (%null-ptr) bundleURL))\r          (if (%null-ptr-p result)\r            (setf err #$coreFoundationUnknownErr)))\r        \r        ;; if everything's cool, and the user wants it loaded, load it\r        (when (and load-executable (zerop err))\r          (if (not (#_CFBundleLoadExecutable result))\r            (setf err #$coreFoundationUnknownErr)))\r        \r        ;; if there's an error, but we've got a pointer, free it and clear result\r        (when (and (not (zerop err)) (not (%null-ptr-p result)))\r          (#_CFRelease result)\r          (setf result nil))\r        \r        ;; free the URLs if there non-null\r        (when (not (%null-ptr-p bundleURL))\r          (#_CFRelease bundleURL))\r        (when (not (%null-ptr-p baseURL))\r          (#_CFRelease baseURL))\r        \r        ;; return pointer + error value\r        (values result err)))))\r\r#+ignore\r(defun get-addr (bundle name)\r  (let* ((addr (#_CFBundleGetFunctionPointerForName bundle name)))\r    (rlet ((buf :long))\r      (setf (%get-ptr buf) addr)\r      (ash (%get-signed-long buf) -2))))\r\r#-ccl-5.2\r(defun lookup-function-in-bundle (name bundle &optional nil-if-not-found)\r  (with-cfstrs ((str name))\r    (let* ((addr (#_CFBundleGetFunctionPointerForName bundle str)))\r      (if (%null-ptr-p addr)\r        (unless nil-if-not-found\r          (error "Couldn't resolve address of foreign function ~s" name))\r        (rlet ((buf :long)) ;; mcl 5.2 uses %fixnum-from-macptr here\r          (setf (%get-ptr buf) addr)\r          (ash (%get-signed-long buf) -2))))))\r\r;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\r;; Convenient way to declare BSD system calls\r\r#+ignore\r(defparameter *system-bundle*\r  #+ccl-5.2 (get-bundle-for-framework-name "System.framework")\r  #-ccl-5.2\r  (let ((bundle (load-framework-bundle "System.framework")))\r    (terminate-when-unreachable bundle (lambda (b)(#_CFRelease b)))\r    bundle))\r\r(defmacro declare-bundle-ff (name name-string &rest arglist &aux (fn (gensym (format nil "ff_~A_" (string name)))))\r  ;; Is there an existing define-trap like macro for this? or could one be modified for use with bundles?\r  `(progn\r     (defloadvar ,fn\r       (let* ((bundle #+ccl-5.2 (get-bundle-for-framework-name "System.framework")\r                      #-ccl-5.2\r                      (let ((bundle (load-framework-bundle "System.framework")))\r                        (terminate-when-unreachable bundle (lambda (b)(#_CFRelease b)))\r                        bundle)))\r         (lookup-function-in-bundle ,name-string bundle)))\r     ,(let ((args (do ((arglist arglist (cddr arglist))\r                      (result))\r                     ((not (cdr arglist)) (nreverse result))\r                   (push (second arglist) result))))        \r       `(defun ,name ,args\r          (ppc-ff-call ,fn ,@arglist)))))\r\r;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\r\r(declare-bundle-ff %system-kqueue "kqueue" \r                   :signed-fullword) ;; returns a file descriptor no!\r\r(defun system-kqueue ()\r  (let ((kq (%system-kqueue)))\r    (if (= kq -1)\r      (ecase (%system-errno)\r        (12 (error "The kernel failed to allocate enough memory for the kernel queue")) ; ENOMEM\r        (24 (error "The per-process descriptor table is full")) ; EMFILE\r        (23 (error "The system file table is full"))) ; ENFILE \r      kq)))\r\r(declare-bundle-ff %system-kevent "kevent"\r                  :unsigned-fullword kq\r                  :address ke\r                  :unsigned-fullword nke\r                  :address ko\r                  :unsigned-fullword nko\r                  :address timeout\r                  :signed-fullword)\r\r(declare-bundle-ff %system-open "open" \r                   :address name \r                   :unsigned-fullword mode\r                   :unsigned-fullword arg \r                   :signed-fullword)\r \r(declare-bundle-ff %system-close "close"\r                   :unsigned-fullword fd \r                   :signed-fullword)\r\r(declare-bundle-ff %system-errno* "__error" \r                   :signed-fullword)\r\r(declare-bundle-ff %system-strerror "strerror" \r                   :signed-fullword errno\r                   :address)\r\r(defun %system-errno ()\r  (%get-fixnum (%int-to-ptr (%system-errno*))))\r\r; (%system-errno)\r\r(defconstant $O-EVTONLY #x8000)\r; (defconstant $O-NONBLOCK #x800 "Non blocking mode")\r\r(defun system-open (posix-namestring)\r  "Low level open function, as in C, returns an fd number"\r  (with-cstrs ((name posix-namestring))\r    (%system-open name $O-EVTONLY 0)))\r\r(defun system-close (fd)\r  (%system-close fd))\r\r(defrecord timespec\r  (sec :unsigned-long)\r  (usec :unsigned-long))\r\r(defVar *kevent-record* nil)\r\r(def-ccl-pointers determine-64bit-kevents ()\r  (setf *kevent-record*\r       (if (ccl::gestalt #$gestaltPowerPCProcessorFeatures\r                        #+ccl-5.2 #$gestaltPowerPCHas64BitSupport #-ccl-5.2 6)\r          :kevent32\r          :kevent64)))\r\r(defrecord :kevent32\r  (ident :unsigned-long) ; uintptr_t\r  (filter :short)\r  (flags :unsigned-short)\r  (fflags :unsigned-long)\r  (data :long)  ; intptr_t\r  (udata :pointer))\r\r(defrecord :kevent64\r  (:variant ; uintptr_t\r   ((ident64 :uint64))\r   ((ident :unsigned-long)))\r  (filter :short)\r  (flags :unsigned-short)\r  (fflags :unsigned-long)\r  (:variant  ; intptr_t\r   ((data64 :sint64))\r   ((data :long)))\r  (:variant ; RMCL :pointer is 32bit\r   ((udata64 :uint64))\r   ((udata :pointer))))\r\r(defun make-kevent (&key (ident 0) (filter 0) (flags 0) (fflags 0) (data 0) (udata *null-ptr*))\r   (ecase *kevent-record*\r      (:kevent64   \r       (make-record kevent64\r                    :ident ident\r                    :filter filter \r                    :flags flags\r                    :fflags fflags\r                    :data data \r                    :udata udata))\r      (:kevent32\r       (make-record kevent32\r                    :ident ident\r                    :filter filter \r                    :flags flags\r                    :fflags fflags\r                    :data data \r                    :udata udata))))\r\r(defun kevent-rref (ke field)\r   (ecase *kevent-record*\r      (:kevent32\r       (ecase field\r          (:ident (rref ke :kevent32.ident))\r          (:filter (rref ke :kevent32.filter))\r          (:flags (rref ke :kevent32.flags))\r          (:fflags (rref ke :kevent32.fflags))\r          (:data (rref ke :kevent32.data))\r          (:udata (rref ke :kevent32.udata))))\r      (:kevent64\r       (ecase field\r          (:ident (rref ke :kevent64.ident))\r          (:filter (rref ke :kevent64.filter))\r          (:flags (rref ke :kevent64.flags))\r          (:fflags (rref ke :kevent64.fflags))\r          (:data (rref ke :kevent64.data))\r          (:udata (rref ke :kevent64.udata))))))\r\r(defun kevent-filter (ke)\r   (kevent-rref ke :filter))\r\r(defun kevent-flags (ke)\r   (kevent-rref ke :flags))\r\r(defun kevent-data (ke)\r   (kevent-rref ke :data))\r\r\r;; FILTER TYPES:\r\r(eval-when (:compile-toplevel :load-toplevel :execute) ; added by binghe\r\r(defconstant $kevent-read-filter -1 "Data available to read")\r(defconstant $kevent-write-filter -2 "Writing is possible")\r(defconstant $kevent-aio-filter -3 "AIO system call has been made")\r(defconstant $kevent-vnode-filter -4 "Event occured on a file descriptor")\r(defconstant $kevent-proc-filter -5 "Process performed one or more of the requested events")\r(defconstant $kevent-signal-filter -6 "Attempted to deliver a signal to a process")\r(defconstant $kevent-timer-filter -7 "Establishes an arbitrary timer")\r(defconstant $kevent-netdev-filter -8 "Event occured on a network device")\r(defconstant $kevent-filesystem-filter -9)\r\r) ; eval-when\r\r; FLAGS:\r\r(defconstant $kevent-add #x01)\r(defconstant $kevent-delete #x02)\r(defconstant $kevent-enable #x04)\r(defconstant $kevent-disable #x08)\r(defconstant $kevent-oneshot #x10)\r(defconstant $kevent-clear #x20)\r(defconstant $kevent-error #x4000)\r(defconstant $kevent-eof #x8000 "EV_EOF")\r\r;; FFLAGS:\r\r(defconstant $kevent-file-delete #x01 "The file was unlinked from the file system")\r(defconstant $kevent-file-write #x02 "A write occurred on the file")\r(defconstant $kevent-file-extend #x04 "The file was extended")\r(defconstant $kevent-file-attrib #x08 "The file had its attributes changed")\r(defconstant $kevent-file-link #x10 "The link count on the file changed")\r(defconstant $kevent-file-rename #x20 "The file was renamed")\r(defconstant $kevent-file-revoke #x40 "Access to the file was revoked or the file system was unmounted")\r(defconstant $kevent-file-all (logior $kevent-file-delete $kevent-file-write $kevent-file-extend\r                                      $kevent-file-attrib $kevent-file-link $kevent-file-rename $kevent-file-revoke))\r\r\r(defconstant $kevent-net-linkup #x01 "Link is up")\r(defconstant $kevent-net-linkdown #x02 "Link is down")\r(defconstant $kevent-net-linkinvalid #x04 "Link state is invalid")\r(defconstant $kevent-net-added #x08 "IP adress added")\r(defconstant $kevent-net-deleted #x10 "IP adress deleted")\r\r(define-condition kevent-error (simple-error)\r  ((errno :initform NIL :initarg :errno)\r   (ko :initform nil :type (or null kevent) :initarg :ko)\r   (syserr :initform (%system-errno)))\r  (:report \r   (lambda (c s)\r     (with-slots (errno ko syserr) c\r       (format s "kevent system call error ~A [~A]" errno syserr) \r       (when errno \r          (format s "(~A)" (%get-cstring (%system-strerror errno))))\r       (when ko\r          (format s " for ")\r          (let ((*standard-output* s))\r            (print-record ko *kevent-record*)))))))\r\r(defun %kevent (kq &optional ke ko (timeout 0))\r  (check-type kq integer)\r  (rlet ((&timeout :timespec :sec timeout :usec 1))\r    (let ((num (with-timer ;; does not seem to make a difference...  \r                 (%system-kevent kq (or ke (%null-ptr))(if ke 1 0)(or ko (%null-ptr))(if ko 1 0) &timeout))))\r      ; "If an error occurs while processing an element of the changelist and there \r      ; is enough room in the eventlist, then the event will be placed in the eventlist with \r      ; EV_ERROR set in flags and the system error in data."\r      (when (and ko (plusp (logand $kevent-error (kevent-flags ko))))\r        (error 'kevent-error \r                              :errno (kevent-data ko)\r               :ko ko))\r      ; "Otherwise, -1 will be returned, and errno will be set to indicate the error condition."\r      (when (= num -1)\r        ;; hack - opentransport provides the constants for the errors documented for the call \r        (case (%system-errno)\r          (0 (error "kevent system call failed with an unspecified error")) ;; should not happen!\r          (13 (error "The process does not have permission to register a filter")) \r          (14 (error "There was an error reading or writing the kevent structure"))  ; EFAULT\r          (9 (error "The specified descriptor is invalid")) ; EBADF\r          (4 (error "A signal was delivered before the timeout expired and before any events were placed on the kqueue for return.")) ; EINTR\r          (22 (error "The specified time limit or filter is invalid")) ; EINVAL\r          (2 (error "The event could not be found to be modified or deleted")) ; ENOENT\r          (12 (error "No memory was available to register the event")) ; ENOMEM\r          (78 (error "The specified process to attach to does not exist"))) ; ESRCH\r        ;; shouldn't get here... \r        (errchk (%system-errno))\r        (error "error ~A" (%system-errno)))\r      (unless (zerop num)\r         (values ko num)))))\r\r;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\r;; CLOS INTERFACE\r\r(defclass kqueue ()\r  ((kq :initform (system-kqueue) \r       :documentation "file descriptor referencing the kqueue")\r   (fds :initform NIL)) ;; ## better if kept on top level, perhaps as a hash table...\r  (:documentation "A kernal event notification channel"))\r\r(defmethod initialize-instance :after ((q kqueue) &rest rest)\r  (declare (ignore rest))\r  (terminate-when-unreachable q 'kqueue-close))\r\r(defmethod kqueue-close ((q kqueue))\r  (with-slots (kq fds) q\r    (when (or kq fds) ;; allow repeated close\r      (system-close kq)\r      (setf fds NIL)\r      (setf kq NIL))))\r\r(defmethod kqueue-poll ((q kqueue))\r  "Polls a kqueue for kevents"\r  ;; may not have to be cleared, but just in case:\r  (flet ((kqueue-poll2 (ko)\r           (let ((result (with-slots (kq) q\r                            (without-interrupts \r                             (%kevent kq NIL ko)))))\r             (when result\r                (let ((type  (kevent-filter result)))\r                  (ecase type\r                     (0 (values))\r                     (#.$kevent-read-filter\r                          (values\r                           :read\r                           (kevent-rref result :ident)\r                           (kevent-rref result :flags)\r                           (kevent-rref result :fflags)\r                           (kevent-rref result :data)\r                           (kevent-rref result :udata)))\r                      (#.$kevent-write-filter :write)\r                      (#.$kevent-aio-filter :aio)\r                      (#.$kevent-vnode-filter\r                           (values\r                            :vnode\r                            (cdr (assoc (kevent-rref result :ident) (slot-value q 'fds)))\r                            (kevent-rref result :flags)\r                            (kevent-rref result :fflags)\r                            (kevent-rref result :data)\r                            (kevent-rref result :udata)))\r                      (#.$kevent-filesystem-filter :filesystem)))))))\r    (ecase *kevent-record*\r       (:kevent64\r        (rlet ((ko :kevent64 :ident 0 :filter 0 :flags 0 :fflags 0 :data 0 :udata (%null-ptr)))\r          (kqueue-poll2 ko)))\r       (:kevent32\r        (rlet ((ko :kevent32 :ident 0 :filter 0 :flags 0 :fflags 0 :data 0 :udata (%null-ptr)))\r          (kqueue-poll2 ko))))))\r\r(defmethod kqueue-subscribe ((q kqueue) &key ident filter (flags 0) (fflags 0) (data 0) (udata (%null-ptr)))\r  (let ((ke (make-kevent :ident ident\r                         :filter filter \r                         :flags flags\r                         :fflags fflags\r                         :data data \r                         :udata udata)))\r    (with-slots (kq) q\r       (without-interrupts\r        (%kevent kq ke)))))\r\r(defmethod kqueue-vnode-subscribe ((q kqueue) pathname)\r  "Makes the queue report an event when there is a change to a directory or file" \r  (let* ((namestring (posix-namestring (full-pathname pathname)))\r         (fd (system-open namestring)))\r    (with-slots (fds) q\r      (push (cons fd pathname) fds))\r    (kqueue-subscribe q \r                      :ident fd \r                      :filter $kevent-vnode-filter \r                      :flags (logior $kevent-add $kevent-clear) \r                      :fflags $kevent-file-all)\r    namestring))\r\r;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\r\r#+test\r(defun kevent-d (pathname &optional (*standard-output* (fred)))\r  "Report changes to a file or directory"\r  (loop\r    with kqueue = (make-instance 'kqueue)\r    with sub = (kqueue-vnode-subscribe kqueue pathname) \r    for i from 1 to 60\r    for result = (multiple-value-list (kqueue-poll kqueue))\r    unless (equal result '(NIL))\r    do (progn\r         (format T "~A~%" result)\r         (force-output))\r    ; do (process-allow-schedule)\r    do (sleep 1)\r    finally (write-line "Done")\r    ))\r\r#|\r\r; Report changes to this file in a fred window (save this document to see what happens):\r\r(process-run-function "kevent-d" #'kevent-d *loading-file-source-file*\r                      (fred))\r\r; Reports files added or removed from the directory of this file:\r\r(process-run-function "kevent-d" #'kevent-d \r                      (make-pathname :directory (pathname-directory *loading-file-source-file*))\r                      (fred))\r|#\r\r\r\r\r
\ No newline at end of file
diff --git a/deps/usocket/vendor/spawn-thread.lisp b/deps/usocket/vendor/spawn-thread.lisp
new file mode 100644 (file)
index 0000000..81aa57a
--- /dev/null
@@ -0,0 +1,78 @@
+;;;; $Id$
+;;;; $URL$
+
+;;;; SPWAN-THREAD from GBBopen's PortableThreads.lisp
+
+(in-package :usocket)
+
+#+(and digitool ccl-5.1)
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (pushnew ':digitool-mcl *features*))
+
+;;; ---------------------------------------------------------------------------
+;;; Add clozure feature to legacy OpenMCL:
+
+#+(and openmcl (not clozure))
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (pushnew ':clozure *features*))
+
+;;; ===========================================================================
+;;;  Features & warnings
+
+#+(or (and clisp (not mt))
+      cormanlisp
+      (and cmu (not mp)) 
+      (and ecl (not threads))
+      gcl
+      mocl
+      (and sbcl (not sb-thread)))
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (pushnew ':threads-not-available *features*))
+
+;;; ---------------------------------------------------------------------------
+
+#+threads-not-available
+(defun threads-not-available (operation)
+  (warn "Threads are not available in ~a running on ~a; ~s was used."
+        (lisp-implementation-type) 
+        (machine-type)
+        operation))
+
+;;; ===========================================================================
+;;;   Spawn-Thread
+
+(defun spawn-thread (name function &rest args)
+  #-(or (and cmu mp) cormanlisp (and sbcl sb-thread))
+  (declare (dynamic-extent args))
+  #+abcl
+  (threads:make-thread #'(lambda () (apply function args))
+                      :name name)
+  #+allegro
+  (apply #'mp:process-run-function name function args)
+  #+(and clisp mt)
+  (mt:make-thread #'(lambda () (apply function args)) 
+                  :name name)
+  #+clozure
+  (apply #'ccl:process-run-function name function args)
+  #+(and cmu mp)
+  (mp:make-process #'(lambda () (apply function args)) 
+                   :name name)
+  #+digitool-mcl
+  (apply #'ccl:process-run-function name function args)
+  #+(and ecl threads)
+  (apply #'mp:process-run-function name function args)
+  #+lispworks
+  (apply #'mp:process-run-function name nil function args)
+  #+(and sbcl sb-thread)
+  (sb-thread:make-thread #'(lambda () (apply function args))
+                         :name name)
+  #+scl
+  (mp:make-process #'(lambda () (apply function args))
+                   :name name)
+  #+abcl
+  (threads:make-thread #'(lambda () (apply function args))
+                      :name name)
+  #+threads-not-available
+  (declare (ignore name function args))
+  #+threads-not-available
+  (threads-not-available 'spawn-thread))
diff --git a/deps/usocket/vendor/split-sequence.lisp b/deps/usocket/vendor/split-sequence.lisp
new file mode 100644 (file)
index 0000000..6701b8c
--- /dev/null
@@ -0,0 +1,245 @@
+;;;; SPLIT-SEQUENCE
+;;;
+;;; This code was based on Arthur Lemmens' in
+;;; <URL:http://groups.google.com/groups?as_umsgid=39F36F1A.B8F19D20%40simplex.nl>;
+;;;
+;;; changes include:
+;;;
+;;; * altering the behaviour of the :from-end keyword argument to
+;;; return the subsequences in original order, for consistency with
+;;; CL:REMOVE, CL:SUBSTITUTE et al. (:from-end being non-NIL only
+;;; affects the answer if :count is less than the number of
+;;; subsequences, by analogy with the above-referenced functions).
+;;;   
+;;; * changing the :maximum keyword argument to :count, by analogy
+;;; with CL:REMOVE, CL:SUBSTITUTE, and so on.
+;;;
+;;; * naming the function SPLIT-SEQUENCE rather than PARTITION rather
+;;; than SPLIT.
+;;;
+;;; * adding SPLIT-SEQUENCE-IF and SPLIT-SEQUENCE-IF-NOT.
+;;;
+;;; * The second return value is now an index rather than a copy of a
+;;; portion of the sequence; this index is the `right' one to feed to
+;;; CL:SUBSEQ for continued processing.
+
+;;; There's a certain amount of code duplication here, which is kept
+;;; to illustrate the relationship between the SPLIT-SEQUENCE
+;;; functions and the CL:POSITION functions.
+
+;;; Examples:
+;;;
+;;; * (split-sequence #\; "a;;b;c")
+;;; -> ("a" "" "b" "c"), 6
+;;;
+;;; * (split-sequence #\; "a;;b;c" :from-end t)
+;;; -> ("a" "" "b" "c"), 0
+;;;
+;;; * (split-sequence #\; "a;;b;c" :from-end t :count 1)
+;;; -> ("c"), 4
+;;;
+;;; * (split-sequence #\; "a;;b;c" :remove-empty-subseqs t)
+;;; -> ("a" "b" "c"), 6
+;;;
+;;; * (split-sequence-if (lambda (x) (member x '(#\a #\b))) "abracadabra")
+;;; -> ("" "" "r" "c" "d" "" "r" ""), 11
+;;;
+;;; * (split-sequence-if-not (lambda (x) (member x '(#\a #\b))) "abracadabra")
+;;; -> ("ab" "a" "a" "ab" "a"), 11 
+;;;
+;;; * (split-sequence #\; ";oo;bar;ba;" :start 1 :end 9)
+;;; -> ("oo" "bar" "b"), 9
+
+#+ignore ; comment by usocket
+(defpackage "SPLIT-SEQUENCE"
+  (:use "CL")
+  (:nicknames "PARTITION")
+  (:export "SPLIT-SEQUENCE" "SPLIT-SEQUENCE-IF" "SPLIT-SEQUENCE-IF-NOT"
+          "PARTITION" "PARTITION-IF" "PARTITION-IF-NOT"))
+
+(in-package :usocket #+ignore "SPLIT-SEQUENCE")
+
+(defun split-sequence (delimiter seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (test nil test-supplied) (test-not nil test-not-supplied) (key nil key-supplied))
+  "Return a list of subsequences in seq delimited by delimiter.
+
+If :remove-empty-subseqs is NIL, empty subsequences will be included
+in the result; otherwise they will be discarded.  All other keywords
+work analogously to those for CL:SUBSTITUTE.  In particular, the
+behaviour of :from-end is possibly different from other versions of
+this function; :from-end values of NIL and T are equivalent unless
+:count is supplied. The second return value is an index suitable as an
+argument to CL:SUBSEQ into the sequence indicating where processing
+stopped."
+  (let ((len (length seq))
+        (other-keys (nconc (when test-supplied 
+                             (list :test test))
+                           (when test-not-supplied 
+                             (list :test-not test-not))
+                           (when key-supplied 
+                             (list :key key)))))
+    (unless end (setq end len))
+    (if from-end
+        (loop for right = end then left
+              for left = (max (or (apply #'position delimiter seq 
+                                        :end right
+                                        :from-end t
+                                        other-keys)
+                                 -1)
+                             (1- start))
+              unless (and (= right (1+ left))
+                          remove-empty-subseqs) ; empty subseq we don't want
+              if (and count (>= nr-elts count))
+              ;; We can't take any more. Return now.
+              return (values (nreverse subseqs) right)
+              else 
+              collect (subseq seq (1+ left) right) into subseqs
+              and sum 1 into nr-elts
+              until (< left start)
+              finally (return (values (nreverse subseqs) (1+ left))))
+      (loop for left = start then (+ right 1)
+            for right = (min (or (apply #'position delimiter seq 
+                                       :start left
+                                       other-keys)
+                                len)
+                            end)
+            unless (and (= right left) 
+                        remove-empty-subseqs) ; empty subseq we don't want
+            if (and count (>= nr-elts count))
+            ;; We can't take any more. Return now.
+            return (values subseqs left)
+            else
+            collect (subseq seq left right) into subseqs
+            and sum 1 into nr-elts
+            until (>= right end)
+            finally (return (values subseqs right))))))
+
+(defun split-sequence-if (predicate seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (key nil key-supplied))
+  "Return a list of subsequences in seq delimited by items satisfying
+predicate.
+
+If :remove-empty-subseqs is NIL, empty subsequences will be included
+in the result; otherwise they will be discarded.  All other keywords
+work analogously to those for CL:SUBSTITUTE-IF.  In particular, the
+behaviour of :from-end is possibly different from other versions of
+this function; :from-end values of NIL and T are equivalent unless
+:count is supplied. The second return value is an index suitable as an
+argument to CL:SUBSEQ into the sequence indicating where processing
+stopped."
+  (let ((len (length seq))
+        (other-keys (when key-supplied 
+                     (list :key key))))
+    (unless end (setq end len))
+    (if from-end
+        (loop for right = end then left
+              for left = (max (or (apply #'position-if predicate seq 
+                                        :end right
+                                        :from-end t
+                                        other-keys)
+                                 -1)
+                             (1- start))
+              unless (and (= right (1+ left))
+                          remove-empty-subseqs) ; empty subseq we don't want
+              if (and count (>= nr-elts count))
+              ;; We can't take any more. Return now.
+              return (values (nreverse subseqs) right)
+              else 
+              collect (subseq seq (1+ left) right) into subseqs
+              and sum 1 into nr-elts
+              until (< left start)
+              finally (return (values (nreverse subseqs) (1+ left))))
+      (loop for left = start then (+ right 1)
+            for right = (min (or (apply #'position-if predicate seq 
+                                       :start left
+                                       other-keys)
+                                len)
+                            end)
+            unless (and (= right left) 
+                        remove-empty-subseqs) ; empty subseq we don't want
+            if (and count (>= nr-elts count))
+            ;; We can't take any more. Return now.
+            return (values subseqs left)
+            else
+            collect (subseq seq left right) into subseqs
+            and sum 1 into nr-elts
+            until (>= right end)
+            finally (return (values subseqs right))))))
+
+(defun split-sequence-if-not (predicate seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (key nil key-supplied))
+  "Return a list of subsequences in seq delimited by items satisfying
+(CL:COMPLEMENT predicate).
+
+If :remove-empty-subseqs is NIL, empty subsequences will be included
+in the result; otherwise they will be discarded.  All other keywords
+work analogously to those for CL:SUBSTITUTE-IF-NOT.  In particular,
+the behaviour of :from-end is possibly different from other versions
+of this function; :from-end values of NIL and T are equivalent unless
+:count is supplied. The second return value is an index suitable as an
+argument to CL:SUBSEQ into the sequence indicating where processing
+stopped."
+  (let ((len (length seq))
+       (other-keys (when key-supplied 
+                     (list :key key))))
+    (unless end (setq end len))
+    (if from-end
+        (loop for right = end then left
+              for left = (max (or (apply #'position-if-not predicate seq 
+                                        :end right
+                                        :from-end t
+                                        other-keys)
+                                 -1)
+                             (1- start))
+              unless (and (= right (1+ left))
+                          remove-empty-subseqs) ; empty subseq we don't want
+              if (and count (>= nr-elts count))
+              ;; We can't take any more. Return now.
+              return (values (nreverse subseqs) right)
+              else 
+              collect (subseq seq (1+ left) right) into subseqs
+              and sum 1 into nr-elts
+              until (< left start)
+              finally (return (values (nreverse subseqs) (1+ left))))
+      (loop for left = start then (+ right 1)
+            for right = (min (or (apply #'position-if-not predicate seq 
+                                       :start left
+                                       other-keys)
+                                len)
+                            end)
+            unless (and (= right left) 
+                        remove-empty-subseqs) ; empty subseq we don't want
+            if (and count (>= nr-elts count))
+            ;; We can't take any more. Return now.
+            return (values subseqs left)
+            else
+            collect (subseq seq left right) into subseqs
+            and sum 1 into nr-elts
+            until (>= right end)
+            finally (return (values subseqs right))))))
+
+;;; clean deprecation
+
+(defun partition (&rest args)
+  (apply #'split-sequence args))
+
+(defun partition-if (&rest args)
+  (apply #'split-sequence-if args))
+
+(defun partition-if-not (&rest args)
+  (apply #'split-sequence-if-not args))
+
+(define-compiler-macro partition (&whole form &rest args)
+  (declare (ignore args))
+  (warn "PARTITION is deprecated; use SPLIT-SEQUENCE instead.")
+  form)
+
+(define-compiler-macro partition-if (&whole form &rest args)
+  (declare (ignore args))
+  (warn "PARTITION-IF is deprecated; use SPLIT-SEQUENCE-IF instead.")
+  form)
+
+(define-compiler-macro partition-if-not (&whole form &rest args)
+  (declare (ignore args))
+  (warn "PARTITION-IF-NOT is deprecated; use SPLIT-SEQUENCE-IF-NOT instead")
+  form)
+
+#+ignore ; comment by usocket
+(pushnew :split-sequence *features*)