From bknr at bknr.net Wed Feb 8 06:02:22 2006
From: bknr at bknr.net (bknr at bknr.net)
Date: Wed, 8 Feb 2006 00:02:22 -0600 (CST)
Subject: [bknr-cvs] r1826 - in trunk/bknr: . src/data
Message-ID: <20060208060222.067502A03B@common-lisp.net>
Author: hhubner
Date: 2006-02-08 00:02:22 -0600 (Wed, 08 Feb 2006)
New Revision: 1826
Modified:
trunk/bknr/init.lisp
trunk/bknr/src/data/object-tests.lisp
Log:
>From Tchadvar Roussanov:
Here is a minor patch to make object-tests.lisp working with
sbcl-0.9.9. It also includes sbcl changes to init.lisp for logical
path translations (assuming installation in user's home directory).
Note that in order for datastore to compile I have to replace cxml
and porableaserve with their latest cvs versions.
Modified: trunk/bknr/init.lisp
===================================================================
--- trunk/bknr/init.lisp 2006-02-07 11:18:43 UTC (rev 1825)
+++ trunk/bknr/init.lisp 2006-02-08 06:02:22 UTC (rev 1826)
@@ -1,78 +1,106 @@
-(in-package :cl-user)
-
-;;;;;;;;;;;;;
-;; Tweak this
-(eval-when (:execute :compile-toplevel :load-toplevel)
- #+allegro
- (setf (logical-pathname-translations "bknr")
- `(("**;*.*.*" "bknr/**/"))
- (logical-pathname-translations "bknr-thirdparty")
- `(("**;*.*.*" "thirdparty/**/"))
- (logical-pathname-translations "eboy")
- `(("**;*.*.*" "eboy/**/")))
-
- #+cmu
- (setf (logical-pathname-translations "bknr")
- `(("**;*.*.*" "home:bknr-sputnik/bknr/**/"))
- (logical-pathname-translations "bknr-thirdparty")
- `(("**;*.*.*" "home:bknr-sputnik/thirdparty/**/"))
- (logical-pathname-translations "eboy")
- `(("**;*.*.*" "home:bknr-sputnik/eboy/**/"))))
-
-(eval-when (:execute :compile-toplevel :load-toplevel)
- (load #p"bknr-thirdparty:asdf;asdf"))
-
-(push (translate-logical-pathname #p"bknr:src;") asdf:*central-registry*)
-(push (translate-logical-pathname #p"eboy:src;") asdf:*central-registry*)
-
-(defparameter *patch-directory* "bknr:patches;")
-
-(defun load-patches (&optional (directory *patch-directory*))
- (dolist (file (directory (merge-pathnames directory #p"patch-*.lisp")))
- (warn "; Loading patch from file ~A~%" file)
- (load file)))
-
-(defun fix-dpd ()
- #+cmu
- ;; Die Sache mit dem aktuellen Verzeichnis hat CMUCL noch immer nicht im
- ;; Griff. Nachbessern!
- (setf *default-pathname-defaults*
- (pathname
- (concatenate 'string
- (nth-value 1 (unix:unix-current-directory))
- "/"))))
-
-(defun make-wild-pathname (type directory)
- (merge-pathnames (make-pathname :type type
- :directory '(:relative :wild-inferiors))
- directory))
-
-(defun setup-registry ()
- (mapc #'(lambda (asd-pathname)
- (pushnew (make-pathname :directory (pathname-directory asd-pathname))
- asdf:*central-registry*
- :test #'equal))
- (append (directory #p"bknr-thirdparty:**;*.asd")
- (directory #p"bknr:**;*.asd"))))
-
-(defun clean-registry (&optional (dirs asdf:*central-registry*))
- (let ((files (mapcan #'directory
- (mapcan #'(lambda (dir)
- (when (pathnamep dir)
- (list (make-wild-pathname "fas" dir)
- (make-wild-pathname "lib" dir)
- (make-wild-pathname "x86f" dir)
- (make-wild-pathname "fasl" dir))))
- dirs))))
- (dolist (file files)
- (when (probe-file file)
- (format t "Deleting binary file ~S~%" file)
- (delete-file file)))))
-
-#+cmu
-(load-patches)
-
-(setup-registry)
-(fix-dpd)
-
-(push :cl-gd-gif *features*)
+(in-package :cl-user)
+
+;;;;;;;;;;;;;
+;; Tweak this
+(eval-when (:execute :compile-toplevel :load-toplevel)
+ #+allegro
+ (setf (logical-pathname-translations "bknr")
+ `(("**;*.*.*" "bknr/**/"))
+ (logical-pathname-translations "bknr-thirdparty")
+ `(("**;*.*.*" "thirdparty/**/"))
+ (logical-pathname-translations "eboy")
+ `(("**;*.*.*" "eboy/**/")))
+
+ #+cmu
+ (setf (logical-pathname-translations "bknr")
+ `(("**;*.*.*" "home:bknr-sputnik/bknr/**/"))
+ (logical-pathname-translations "bknr-thirdparty")
+ `(("**;*.*.*" "home:bknr-sputnik/thirdparty/**/"))
+ (logical-pathname-translations "eboy")
+ `(("**;*.*.*" "home:bknr-sputnik/eboy/**/")))
+
+ #+sbcl
+ (setf (logical-pathname-translations "bknr")
+ `(("**;*.*.*"
+ ,(merge-pathnames
+ (make-pathname :directory '(:relative "bknr-svn" "bknr" :wild-inferiors)
+ :name :wild
+ :type :wild
+ :version :wild)
+ (user-homedir-pathname))))
+ (logical-pathname-translations "bknr-thirdparty")
+ `(("**;*.*.*"
+ ,(merge-pathnames
+ (make-pathname :directory '(:relative "bknr-svn" "thirdparty" :wild-inferiors)
+ :name :wild
+ :type :wild
+ :version :wild)
+ (user-homedir-pathname))))
+ (logical-pathname-translations "eboy")
+ `(("**;*.*.*"
+ ,(merge-pathnames
+ (make-pathname :directory '(:relative "bknr-svn" "eboy" :wild-inferiors)
+ :name :wild
+ :type :wild
+ :version :wild)
+ (user-homedir-pathname))))))
+
+#-sbcl
+(eval-when (:execute :compile-toplevel :load-toplevel)
+ (load #p"bknr-thirdparty:asdf;asdf"))
+
+(push (translate-logical-pathname #p"bknr:src;") asdf:*central-registry*)
+(push (translate-logical-pathname #p"eboy:src;") asdf:*central-registry*)
+
+(defparameter *patch-directory* "bknr:patches;")
+
+(defun load-patches (&optional (directory *patch-directory*))
+ (dolist (file (directory (merge-pathnames directory #p"patch-*.lisp")))
+ (warn "; Loading patch from file ~A~%" file)
+ (load file)))
+
+(defun fix-dpd ()
+ #+cmu
+ ;; Die Sache mit dem aktuellen Verzeichnis hat CMUCL noch immer nicht im
+ ;; Griff. Nachbessern!
+ (setf *default-pathname-defaults*
+ (pathname
+ (concatenate 'string
+ (nth-value 1 (unix:unix-current-directory))
+ "/"))))
+
+(defun make-wild-pathname (type directory)
+ (merge-pathnames (make-pathname :type type
+ :name :wild
+ :directory '(:relative :wild-inferiors))
+ directory))
+
+(defun setup-registry ()
+ (mapc #'(lambda (asd-pathname)
+ (pushnew (make-pathname :directory (pathname-directory asd-pathname))
+ asdf:*central-registry*
+ :test #'equal))
+ (append (directory #p"bknr-thirdparty:**;*.asd")
+ (directory #p"bknr:**;*.asd"))))
+
+(defun clean-registry (&optional (dirs asdf:*central-registry*))
+ (let ((files (mapcan #'directory
+ (mapcan #'(lambda (dir)
+ (when (pathnamep dir)
+ (list (make-wild-pathname "fas" dir)
+ (make-wild-pathname "lib" dir)
+ (make-wild-pathname "x86f" dir)
+ (make-wild-pathname "fasl" dir))))
+ dirs))))
+ (dolist (file files)
+ (when (probe-file file)
+ (format t "Deleting binary file ~S~%" file)
+ (delete-file file)))))
+
+#+cmu
+(load-patches)
+
+(setup-registry)
+(fix-dpd)
+
+(push :cl-gd-gif *features*)
Modified: trunk/bknr/src/data/object-tests.lisp
===================================================================
--- trunk/bknr/src/data/object-tests.lisp 2006-02-07 11:18:43 UTC (rev 1825)
+++ trunk/bknr/src/data/object-tests.lisp 2006-02-08 06:02:22 UTC (rev 1826)
@@ -16,7 +16,20 @@
#+allegro
(excl:delete-directory-and-files pathname)
#+cmu
- (unix:unix-rmdir (namestring pathname))))
+ (unix:unix-rmdir (namestring pathname))
+ #+sbcl
+ (loop for file in (directory
+ (merge-pathnames
+ (make-pathname
+ :name :wild
+ :type :wild
+ :version :wild
+ )
+ pathname))
+ when (pathname-name file) do (delete-file file)
+ unless (pathname-name file) do (delete-directory file))
+ #+sbcl
+ (sb-posix:rmdir (namestring pathname))))
(defvar *test-datastore-directory* #p"/tmp/test-datastore/")
(defvar *test-datastore* nil)
From bknr at bknr.net Fri Feb 10 05:22:29 2006
From: bknr at bknr.net (bknr at bknr.net)
Date: Thu, 9 Feb 2006 23:22:29 -0600 (CST)
Subject: [bknr-cvs] r1827 - in trunk/bknr: . src/utils
Message-ID: <20060210052229.456AA2A2F0@common-lisp.net>
Author: hhubner
Date: 2006-02-09 23:22:28 -0600 (Thu, 09 Feb 2006)
New Revision: 1827
Modified:
trunk/bknr/init.lisp
trunk/bknr/src/utils/crypt-md5.lisp
trunk/bknr/src/utils/reader.lisp
trunk/bknr/src/utils/utils.lisp
Log:
Tchadvar Roussanov:
This is a small patch for init.lisp and some fixes to make sbcl compiler
happy with 'defconstant'.
Modified: trunk/bknr/init.lisp
===================================================================
--- trunk/bknr/init.lisp 2006-02-08 06:02:22 UTC (rev 1826)
+++ trunk/bknr/init.lisp 2006-02-10 05:22:28 UTC (rev 1827)
@@ -45,12 +45,14 @@
:version :wild)
(user-homedir-pathname))))))
-#-sbcl
(eval-when (:execute :compile-toplevel :load-toplevel)
- (load #p"bknr-thirdparty:asdf;asdf"))
+ #-sbcl
+ (load #p"bknr-thirdparty:asdf;asdf")
+ #+sbcl
+ (require :asdf))
-(push (translate-logical-pathname #p"bknr:src;") asdf:*central-registry*)
-(push (translate-logical-pathname #p"eboy:src;") asdf:*central-registry*)
+(pushnew (translate-logical-pathname #p"bknr:src;") asdf:*central-registry* :test #'equal)
+(pushnew (translate-logical-pathname #p"eboy:src;") asdf:*central-registry* :test #'equal)
(defparameter *patch-directory* "bknr:patches;")
@@ -103,4 +105,4 @@
(setup-registry)
(fix-dpd)
-(push :cl-gd-gif *features*)
+(pushnew :cl-gd-gif *features*)
Modified: trunk/bknr/src/utils/crypt-md5.lisp
===================================================================
--- trunk/bknr/src/utils/crypt-md5.lisp 2006-02-08 06:02:22 UTC (rev 1826)
+++ trunk/bknr/src/utils/crypt-md5.lisp 2006-02-10 05:22:28 UTC (rev 1827)
@@ -1,6 +1,6 @@
(in-package :bknr.utils)
-(defconstant +itoa64+
+(define-constant +itoa64+
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
(defun itoa64 (int num)
@@ -73,4 +73,4 @@
;; 2 8 14 (4)
;; 3 9 15 (4)
;; 4 10 5 (4)
-;; 11 (2)
\ No newline at end of file
+;; 11 (2)
Modified: trunk/bknr/src/utils/reader.lisp
===================================================================
--- trunk/bknr/src/utils/reader.lisp 2006-02-08 06:02:22 UTC (rev 1826)
+++ trunk/bknr/src/utils/reader.lisp 2006-02-10 05:22:28 UTC (rev 1827)
@@ -1,6 +1,6 @@
(in-package :bknr.utils)
-(defconstant +whitespace-chars+
+(define-constant +whitespace-chars+
'(#\Space #\Newline #\Tab #\Linefeed))
(defun whitespace-char-p (c)
Modified: trunk/bknr/src/utils/utils.lisp
===================================================================
--- trunk/bknr/src/utils/utils.lisp 2006-02-08 06:02:22 UTC (rev 1826)
+++ trunk/bknr/src/utils/utils.lisp 2006-02-10 05:22:28 UTC (rev 1827)
@@ -1,5 +1,9 @@
(in-package :bknr.utils)
+(defmacro define-constant (name value &optional doc)
+ "Macro for use in place of defconstant in order to make SBCL compiler happy"
+ `(defconstant ,name (if (boundp ',name) (symbol-value ',name) ,value)
+ ,@(when doc (list doc))))
;;; date format
;; Zeitzone f?r Mail-Zeitstempel
From bknr at bknr.net Sat Feb 11 09:18:17 2006
From: bknr at bknr.net (bknr at bknr.net)
Date: Sat, 11 Feb 2006 03:18:17 -0600 (CST)
Subject: [bknr-cvs] r1828 - trunk/projects/lisp-ecoop05/src
Message-ID: <20060211091817.53B174B00F@common-lisp.net>
Author: hhubner
Date: 2006-02-11 03:18:16 -0600 (Sat, 11 Feb 2006)
New Revision: 1828
Removed:
trunk/projects/lisp-ecoop05/src/js-drag.lisp
Modified:
trunk/projects/lisp-ecoop05/src/config.lisp
trunk/projects/lisp-ecoop05/src/event.lisp
trunk/projects/lisp-ecoop05/src/handlers.lisp
trunk/projects/lisp-ecoop05/src/init.lisp
trunk/projects/lisp-ecoop05/src/lisp-ecoop05.asd
trunk/projects/lisp-ecoop05/src/load.lisp
trunk/projects/lisp-ecoop05/src/mail.lisp
trunk/projects/lisp-ecoop05/src/packages.lisp
trunk/projects/lisp-ecoop05/src/participant.lisp
trunk/projects/lisp-ecoop05/src/schedule.lisp
trunk/projects/lisp-ecoop05/src/tags.lisp
trunk/projects/lisp-ecoop05/src/webserver.lisp
Log:
Renaming to lisp-ecoop
Modified: trunk/projects/lisp-ecoop05/src/config.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/config.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/config.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,9 +1,9 @@
-(in-package :lisp-ecoop05.config)
+(in-package :lisp-ecoop.config)
;; URL f?r BASE HREFs
-(defparameter *website-url* "http://lisp-ecoop05.bknr.net")
+(defparameter *website-url* "http://lisp-ecoop.bknr.net")
-(defparameter *root-directory* #p"home:bknr-svn/lisp-ecoop05/")
+(defparameter *root-directory* #p"home:bknr-svn/lisp-ecoop/")
(defparameter *store-directory* (merge-pathnames #p"datastore/" *root-directory*))
@@ -11,6 +11,6 @@
(defparameter *webserver-port* 8081)
-(defparameter *default-mail-from* "postmaster at lisp-ecoop05.bknr.net")
+(defparameter *default-mail-from* "postmaster at lisp-ecoop.bknr.net")
(defparameter *default-mail-subject* "Mail from the LISP ECOOP05 Website")
(defparameter *smtp-server* "127.0.0.1")
\ No newline at end of file
Modified: trunk/projects/lisp-ecoop05/src/event.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/event.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/event.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,3 +1,3 @@
-(in-package :lisp-ecoop05)
+(in-package :lisp-ecoop)
(
\ No newline at end of file
Modified: trunk/projects/lisp-ecoop05/src/handlers.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/handlers.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/handlers.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,4 +1,4 @@
-(in-package :lisp-ecoop05)
+(in-package :lisp-ecoop)
(enable-interpol-syntax)
Modified: trunk/projects/lisp-ecoop05/src/init.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/init.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/init.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,4 +1,4 @@
-(in-package :lisp-ecoop05)
+(in-package :lisp-ecoop)
(defun startup ()
(when *store*
@@ -15,4 +15,4 @@
(bknr.cron:start-cron)
- (publish-lisp-ecoop05))
+ (publish-lisp-ecoop))
Deleted: trunk/projects/lisp-ecoop05/src/js-drag.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/js-drag.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/js-drag.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,55 +0,0 @@
-(in-package :lisp-ecoop05)
-
-(defclass js-drag-handler (page-handler)
- ())
-
-(defmethod handle ((handler js-drag-handler) req)
- (with-http-response (req *ent*)
- (with-http-body (req *ent*)
- (html
- (:html
- (:head
- (:title "Drag & Drop Demo")
- ((:script :type "text/javascript" :src "/static/drag-demo.js")))
- ((:body :style (css-inline :font-family "sans-serif")
- :onload (js-inline (init)))
- (:h1 "Drag & Drop Demo")
- ((:div :id "canvas" :style (css-inline :position "absolute"
- :background-color "#f0f0f0"
- :left "50px"
- :top "50px"
- :width "400px"
- :height "400px"
- :border "thin solid #000000"
- :-moz-border-radius "4px"
- :z-order "1"))
- ((:div :id "container1" :style (css-inline :position "absolute"
- :background-color "#c0c0c0"
- :left "10px"
- :top "10px"
- :width "100px"
- :height "380px"
- :border "thin solid #000000"
- :-moz-border-radius "4px"
- :z-order "2"))
- "Container 1")
- ((:div :id "container2" :style (css-inline :position "absolute"
- :background-color "#c0c0c0"
- :right "10px"
- :top "10px"
- :width "100px"
- :height "380px"
- :border "thin solid #000000"
- :-moz-border-radius "4px"
- :z-order "2"))
- "Container 2")
- ((:div :id "icon" :style (css-inline :position "absolute"
- :background-color "#e0e0e0"
- :left "130px"
- :top "20px"
- :border "thin solid #000000"
- :text-align "center"
- :-moz-border-radius "4px"
- :z-order "3"))
- ((:img :border "0" :src "/image/bknr-logo"))
- :br "drag me!"))))))))
\ No newline at end of file
Modified: trunk/projects/lisp-ecoop05/src/lisp-ecoop05.asd
===================================================================
--- trunk/projects/lisp-ecoop05/src/lisp-ecoop05.asd 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/lisp-ecoop05.asd 2006-02-11 09:18:16 UTC (rev 1828)
@@ -2,12 +2,12 @@
(in-package :cl-user)
-(defpackage :lisp-ecoop05.system
+(defpackage :lisp-ecoop.system
(:use :cl :asdf))
-(in-package :lisp-ecoop05.system)
+(in-package :lisp-ecoop.system)
-(defsystem :lisp-ecoop05
+(defsystem :lisp-ecoop
:name "worldpay test"
:author "Hans Huebner "
:version "0"
@@ -16,15 +16,15 @@
:description "BKNR Test Web Server"
:long-description ""
- :depends-on (:bknr-modules :klammerscript)
+ :depends-on (:bknr-modules :cxml :klammerscript)
:components ((:file "packages")
- (:file "config" :depends-on ("packages"))
+ (:file "macros" :depends-on ("packages"))
+ (:file "config" :depends-on ("macros"))
(:file "schedule" :depends-on ("config"))
(:file "participant" :depends-on ("config" "schedule"))
(:file "mail" :depends-on ("participant"))
(:file "tags" :depends-on ("participant"))
(:file "handlers" :depends-on ("participant"))
- (:file "js-drag" :depends-on ("packages"))
(:file "webserver" :depends-on ("handlers"))
(:file "init" :depends-on ("webserver"))))
Modified: trunk/projects/lisp-ecoop05/src/load.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/load.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/load.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,9 +1,9 @@
(push :cl-gd-gif *features*)
-(asdf:oos 'asdf:load-op :lisp-ecoop05)
+(asdf:oos 'asdf:load-op :lisp-ecoop)
(asdf:oos 'asdf:load-op :swank)
(swank::create-swank-server 4005 :spawn #'swank::simple-announce-function t)
-(lisp-ecoop05::startup)
+(lisp-ecoop::startup)
(mp::startup-idle-and-top-level-loops)
Modified: trunk/projects/lisp-ecoop05/src/mail.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/mail.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/mail.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,4 +1,4 @@
-(in-package :lisp-ecoop05)
+(in-package :lisp-ecoop)
(enable-interpol-syntax)
Modified: trunk/projects/lisp-ecoop05/src/packages.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/packages.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/packages.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,6 +1,6 @@
(in-package :cl-user)
-(defpackage :lisp-ecoop05.config
+(defpackage :lisp-ecoop.config
(:use :cl
:cl-user)
(:export #:*website-url*
@@ -11,15 +11,15 @@
#:*default-mail-subject*
#:*smtp-server*))
-(defpackage :lisp-ecoop05.imageproc
+(defpackage :lisp-ecoop.imageproc
(:use :cl
:cl-user
:bknr.web
:cl-gd
- :lisp-ecoop05.config)
+ :lisp-ecoop.config)
(:export))
-(defpackage :lisp-ecoop05
+(defpackage :lisp-ecoop
(:use :cl
:cl-user
:ext
@@ -31,7 +31,7 @@
:bknr.indices
:bknr.user
:bknr.images
- :lisp-ecoop05.config
+ :lisp-ecoop.config
:net.aserve
:net.post-office
:xhtml-generator
@@ -58,7 +58,7 @@
#:submission-timeslot
#:timeslot))
-(defpackage :lisp-ecoop05.tags
+(defpackage :lisp-ecoop.tags
(:use :cl
:cl-user
:cl-ppcre
@@ -71,8 +71,8 @@
:bknr.images
:net.aserve
:xhtml-generator
- :lisp-ecoop05.config
- :lisp-ecoop05)
+ :lisp-ecoop.config
+ :lisp-ecoop)
(:shadowing-import-from :cl-interpol #:quote-meta-chars)
(:shadowing-import-from :acl-compat.mp #:process-kill #:process-wait)
(:export #:hello))
\ No newline at end of file
Modified: trunk/projects/lisp-ecoop05/src/participant.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/participant.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/participant.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,4 +1,4 @@
-(in-package :lisp-ecoop05)
+(in-package :lisp-ecoop)
(enable-interpol-syntax)
@@ -90,7 +90,7 @@
:text (format nil
"Your participant account on the LISP ECOOP05 Workshop website has
been created. Please visit your personal profile page on
-http://lisp-ecoop05.bknr.net/edit-profile/ to change your password
+http://lisp-ecoop.bknr.net/edit-profile/ to change your password
andupdate your profile information.
Your login name is: ~A
@@ -110,7 +110,7 @@
:text (format nil
"Your password on the LISP ECOOP 05 Workshop website has been reset.
Please visit your personal profile page on
-http://lisp-ecoop05.bknr.net/edit-profile/~A to change your password
+http://lisp-ecoop.bknr.net/edit-profile/~A to change your password
and update your profile information.
Your login name is: ~A
Modified: trunk/projects/lisp-ecoop05/src/schedule.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/schedule.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/schedule.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,4 +1,4 @@
-(in-package :lisp-ecoop05)
+(in-package :lisp-ecoop)
(enable-interpol-syntax)
@@ -93,7 +93,7 @@
(print-object-as-html (timeslot-content timeslot))))
(mapc #'emit-template-node children))))))
-(in-package :lisp-ecoop05.tags)
+(in-package :lisp-ecoop.tags)
(define-bknr-tag show-day-schedule (&key day children)
- (lisp-ecoop05::show-day-schedule :day day :children children))
+ (lisp-ecoop::show-day-schedule :day day :children children))
Modified: trunk/projects/lisp-ecoop05/src/tags.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/tags.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/tags.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,4 +1,4 @@
-(in-package :lisp-ecoop05.tags)
+(in-package :lisp-ecoop.tags)
(enable-interpol-syntax)
@@ -293,7 +293,7 @@
(define-bknr-tag submission-list (&key participant-only (type "submission"))
(dolist (submission (sort (copy-list (if participant-only
(participant-submissions *participant*)
- (class-instances (intern (string-upcase type) :lisp-ecoop05))))
+ (class-instances (intern (string-upcase type) :lisp-ecoop))))
#'string-lessp :key #'submission-title))
(html ((:div :class "submission-line")
(when participant-only
Modified: trunk/projects/lisp-ecoop05/src/webserver.lisp
===================================================================
--- trunk/projects/lisp-ecoop05/src/webserver.lisp 2006-02-10 05:22:28 UTC (rev 1827)
+++ trunk/projects/lisp-ecoop05/src/webserver.lisp 2006-02-11 09:18:16 UTC (rev 1828)
@@ -1,4 +1,4 @@
-(in-package :lisp-ecoop05)
+(in-package :lisp-ecoop)
(enable-interpol-syntax)
@@ -6,9 +6,9 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun make-daily-statistics ()
- (bknr.stats::make-yesterdays-stats :delete-events t :remove-referer-hosts '("lisp-ecoop05.bknr.net")))
+ (bknr.stats::make-yesterdays-stats :delete-events t :remove-referer-hosts '("lisp-ecoop.bknr.net")))
-(defun publish-lisp-ecoop05 (&key (port *webserver-port*) (listeners 20))
+(defun publish-lisp-ecoop (&key (port *webserver-port*) (listeners 20))
(unless (bknr.cron:cron-job-with-name "daily webserver statistics")
(bknr.cron:make-cron-job "daily webserver statistics" 'make-daily-statistics
@@ -21,7 +21,7 @@
:to "/home")
("/" template-handler
:destination ,(namestring (merge-pathnames #p"templates/" *website-directory*))
- :command-packages ((:lisp-ecoop05 . :lisp-ecoop05.tags)
+ :command-packages ((:lisp-ecoop . :lisp-ecoop.tags)
(:bknr . :bknr.web)
(:menu . :bknr.site-menu)))
("/static" directory-handler
From bknr at bknr.net Sat Feb 11 09:19:59 2006
From: bknr at bknr.net (bknr at bknr.net)
Date: Sat, 11 Feb 2006 03:19:59 -0600 (CST)
Subject: [bknr-cvs] r1829 - in trunk/projects: . bknr-website/templates
lisp-ecoop
Message-ID: <20060211091959.519894B00F@common-lisp.net>
Author: hhubner
Date: 2006-02-11 03:19:59 -0600 (Sat, 11 Feb 2006)
New Revision: 1829
Added:
trunk/projects/lisp-ecoop/
trunk/projects/lisp-ecoop/src/
Removed:
trunk/projects/lisp-ecoop/src/
trunk/projects/lisp-ecoop05/
Modified:
trunk/projects/bknr-website/templates/generate-html.xsl
Log:
Renamed to lisp-ecoop
Modified: trunk/projects/bknr-website/templates/generate-html.xsl
===================================================================
--- trunk/projects/bknr-website/templates/generate-html.xsl 2006-02-11 09:18:16 UTC (rev 1828)
+++ trunk/projects/bknr-website/templates/generate-html.xsl 2006-02-11 09:19:59 UTC (rev 1829)
@@ -1,4 +1,4 @@
xsltproc --stringparam mode html -o ../html/.html .xml
-
\ No newline at end of file
+
Copied: trunk/projects/lisp-ecoop (from rev 1822, trunk/projects/lisp-ecoop05)
Copied: trunk/projects/lisp-ecoop/src (from rev 1828, trunk/projects/lisp-ecoop05/src)
From bknr at bknr.net Tue Feb 14 05:47:48 2006
From: bknr at bknr.net (bknr at bknr.net)
Date: Mon, 13 Feb 2006 23:47:48 -0600 (CST)
Subject: [bknr-cvs] r1830 - trunk/bknr/src/web
Message-ID: <20060214054748.871502A034@common-lisp.net>
Author: hhubner
Date: 2006-02-13 23:47:48 -0600 (Mon, 13 Feb 2006)
New Revision: 1830
Modified:
trunk/bknr/src/web/handlers.lisp
Log:
Tchadvar Roussanov:
In order to compile bknr/src/web/handlers.lisp with the latest cxml
and sbcl, I either have to apply the change from svn to with-element
macro in cxml/xml/unparse.lisp: [...]
or change the handlers.lisp this way:
In other words move the evaluation of
xml-object-list-handler-toplevel-element-name out of the macro
expansion. I think the two changes are equivalent but the second one
has the advantage not to require to maintain changes to the third
party library.
I asked David Lichteblau whether he could change cxml to support
computed element names in with-element. Meanwhile, this change
restores portability to the current cxml release.
Modified: trunk/bknr/src/web/handlers.lisp
===================================================================
--- trunk/bknr/src/web/handlers.lisp 2006-02-11 09:19:59 UTC (rev 1829)
+++ trunk/bknr/src/web/handlers.lisp 2006-02-14 05:47:48 UTC (rev 1830)
@@ -446,9 +446,10 @@
:string-rod-fn #'cxml::utf8-string-to-rod))
(defmethod handle-object ((handler xml-object-list-handler) object req)
- (cxml:with-element (xml-object-list-handler-toplevel-element-name handler)
- (dolist (object (object-list-handler-get-objects handler object req))
- (object-list-handler-show-object-xml handler object req))))
+ (let ((element-name (xml-object-list-handler-toplevel-element-name handler)))
+ (cxml:with-element element-name
+ (dolist (object (object-list-handler-get-objects handler object req))
+ (object-list-handler-show-object-xml handler object req)))))
(defclass blob-handler (object-handler)
())
From bknr at bknr.net Tue Feb 14 21:48:13 2006
From: bknr at bknr.net (bknr at bknr.net)
Date: Tue, 14 Feb 2006 15:48:13 -0600 (CST)
Subject: [bknr-cvs] r1833 - trunk/projects/lisp-ecoop/website/templates
Message-ID: <20060214214813.98E132A034@common-lisp.net>
Author: hhubner
Date: 2006-02-14 15:48:08 -0600 (Tue, 14 Feb 2006)
New Revision: 1833
Added:
trunk/projects/lisp-ecoop/website/templates/cfp.xml
trunk/projects/lisp-ecoop/website/templates/contact.xml
trunk/projects/lisp-ecoop/website/templates/edit-profile.xml
trunk/projects/lisp-ecoop/website/templates/edit-submission.xml
trunk/projects/lisp-ecoop/website/templates/home.xml
trunk/projects/lisp-ecoop/website/templates/lisp-ecoop.xsl
trunk/projects/lisp-ecoop/website/templates/people.xml
trunk/projects/lisp-ecoop/website/templates/profile.xml
trunk/projects/lisp-ecoop/website/templates/schedule.xml
trunk/projects/lisp-ecoop/website/templates/submission.xml
trunk/projects/lisp-ecoop/website/templates/submissions.xml
trunk/projects/lisp-ecoop/website/templates/toplevel.xml
trunk/projects/lisp-ecoop/website/templates/user-error.xml
Removed:
trunk/projects/lisp-ecoop/website/templates/bknr-arguments.bknr
trunk/projects/lisp-ecoop/website/templates/bknr-style.bknr
trunk/projects/lisp-ecoop/website/templates/bknr-technology.bknr
trunk/projects/lisp-ecoop/website/templates/cfp.bknr
trunk/projects/lisp-ecoop/website/templates/contact.bknr
trunk/projects/lisp-ecoop/website/templates/edit-profile.bknr
trunk/projects/lisp-ecoop/website/templates/edit-submission.bknr
trunk/projects/lisp-ecoop/website/templates/home.bknr
trunk/projects/lisp-ecoop/website/templates/people.bknr
trunk/projects/lisp-ecoop/website/templates/profile.bknr
trunk/projects/lisp-ecoop/website/templates/schedule.bknr
trunk/projects/lisp-ecoop/website/templates/submission.bknr
trunk/projects/lisp-ecoop/website/templates/submissions.bknr
trunk/projects/lisp-ecoop/website/templates/toplevel.bknr
trunk/projects/lisp-ecoop/website/templates/user-error.bknr
Modified:
trunk/projects/lisp-ecoop/website/templates/menu.xml
Log:
XSL based website to ease authoring
Deleted: trunk/projects/lisp-ecoop/website/templates/bknr-arguments.bknr
===================================================================
--- trunk/projects/lisp-ecoop/website/templates/bknr-arguments.bknr 2006-02-14 19:21:08 UTC (rev 1832)
+++ trunk/projects/lisp-ecoop/website/templates/bknr-arguments.bknr 2006-02-14 21:48:08 UTC (rev 1833)
@@ -1,79 +0,0 @@
-
-
-
-
-
Requirements for a persistence mechanism
-
-We want a persistence mechanism which is closely integrated into our
-programming language. Persistence is something we want to assume as
-being available which writing applications, and it should be as
-unintrusive as possible. Applications should use standard programming
-language mechanisms to access all data.
-
-
-The persistence mechanism should ensure that all appplication data in
-the system is safe against system crashes. If the system crashes, the
-application and all it's data should be restartable into a consistent
-state. The amount of work which can be lost due to a unplanned system
-failure should be low.
-
-
-The persistence mechanism should make the amount of data which
-constitutes the persistent state of the application transparent. It
-should provide for mechanisms to duplicate the current state in order
-to move it from one machine or software release to another.
-
-
Does a "Database system" help?
-
-The two most popular database families today are relational databases
-with SQL as query language, and embedded database facilities like
-Berkeley DB which provides for indexed access to tables stored on
-disk. They have in common that they assume that the primary location
-of data is on-disk and that applications have to read and write data
-from and to disk in order to perform their tasks.
-
-
-Database systems, when they were invented, served as an address
-extension to the applications. Applications could operate on data
-sets which were to large for in-memory processing, providing
-persistence at the same time.
-
-
-In addition, SQL databases provide for a language neutral query
-language that allow flexible querying of the application data. This
-facility is useful in multi-language environments and to facilitate
-database queries through third-party applications.
-
-
-Todays main memories and processing speed redefine the environment in
-which applications are created. Memory is no longer a very scarce
-resource, and memory sizes have generally grown much faster than the
-amount of application data that needs to be processed. Thus,
-on-demand loading of application from disk can (and should) be totally
-avoided by always having the complete data set in memory.
-
-
Properties of the BKNR persistence mechanism
-
-All Disk access by the persistence mechanism is sequential. During
-normal operation, the transaction log is sequentially appended to.
-During snapshot, the snapshot file of the object subsystem is
-sequentially written. During restore, the snapshot and transaction
-log files are sequentially read.
-
-
-All files used by BKNR are immutable. A file is never changed, but
-only created, read or deleted.
-
-
-The time needed to restore from a crash consists of the time needed to
-read the sequential log file, to parse it's content and to execute the
-applications' transaction code.
-
-All BKNR application data is handled in main memory. Internally,
-applications do not use secondary store like data bases or files. Are
-application data structures are kept in the data structures provided
-by Common Lisp.
-
-
-All destructive operation on the persistent heap need to be explicit
-and are synchronized. A transaction is never aborted nor may it fail.
-Thus, it is vital to check preconditions before starting a destructive
-operation. Within a transaction, application code must properly
-handle all possible errors and gracefully return. Errors unhandled in
-transaction code are considered fatal and cause the persistence system
-to stop.
-
-
-Applications may either define their own, named transactions or they
-may use the provided CLOS layer which groups accesses to slots of
-persistent objects to atomic transactions.
-
-
-BKNR persistence sequentializes all destructive operations on the
-persistent heap. As all data is in RAM when a transaction starts
-executing, it is never stalled by having to wait for necessary data to
-arrive. Thus, the need for preempting transaction code arises much
-less frequently than in traditional systems in which transactions had
-to access slow disks in order to collect the data to be processed.
-
-
-Some applications require precondition checks which must be processed
-atomically together with the associated transaction code.
-Applications must guard such precondition checks with a store lock in
-order to block other transaction code from executing.
-
-
-Transaction code must be used only to change the persistent state of a
-system. No I/O operations must be performed within transaction code.
-Transactions may produce log output, but it they may not use (possibly
-blocking) communications mechanisms to other processes.
-
-
Areas of research
-
Performance
-
-For high-performance stores, some optimizations will be worthwile.
-The single most time consuming operation while executing transactions
-is the appending to the transaction log file. At the moment, this is
-done using a synchronous write to the opened Unix file, which takes
-significant time.
-
-
-Not all operations need to be absolutely safe, and it might be
-worthwile to change the default execution mode of the store to be as
-follows: Processing other transactions or non-transaction code may
-continue as soon as the transaction log entry of the current operation
-has been constructed in RAM and the transaction application code has
-been executed. The transaction system will then asynchronously write
-the transaction to the transaction log. Non-transactional application
-code can continue executing while the log entry is being commited to
-disk. Depending on the safety needs of the application, log entries
-could also be buffered in a queue to achive higher peak transaction
-rates, trading safety for speed. If safety is required, fast stable
-store (e.g. battery-backed RAM) can increase the speed of synchronous
-log writes.
-
-
Locking API
-
-Current applications do not require sophisticated locking. To support
-such applications, a locking API might make sense.
-
-
Replication
-
-Hot-standby servers using the BKNR datastore are conceptually simple.
-Full commit safety requires a slightly more involved commit
-coordination on the distributed servers than doing the log replication
-after the commit has locally finished. Many applications will be able
-to loose one or even a few transactions should the system crash.
-
-
-This website is constructed using a set of Common Lisp packages which
-is collectively called the BKNR application development environment.
-BKNR integrates several open source components into an environment
-suitable for serving HTTP clients from a persistent Lisp system.
-
-It's major components are:
-
-
Persistence mechanism based on transaction logging, supporting
-immutable binary large objects and CLOS persistence.
-
XML processing facilities
-
HTTP application environment with sessions
-
-
-
-
Persistence
-
-Persistence in BKNR is achived using a transaction logging mechanism.
-All operations which change the persistent image of a system are
-explicitely written to a transaction log file. When the system
-crashes, the persistent state can be recovered by rolling forward the
-transaction log. The transaction log is held in a simple binary
-format in order to get acceptable restart times for larger datasets.
-
-
-The persistence mechanism supports a snapshot API which allows the
-persistent object system to write all currently active objects to a
-sequential file.
-
-
-The Persistent object subsystem provides for ID generation of objects.
-Object IDs of persistent objects are written to the transaction log.
-Persistent objects are never garbage collected and need to be deleted
-by the application if they are no longer in use.
-
-
-BKNR supports automated indices for CLOS instances. They are
-implemented in a MOP metaclass and compatible with the BKNR metaclass
-for persistent objects.
-
-
XML processing
-
-BKNR integrates the Closure XML (CXML) parser using a CLOS metaclass
-to provide for reading and writing XML files of objects. Combined
-metaclasses are provided in order to allow for combinations like
-indexed+persistent or indexed+xml.
-
-
-CXML is also used for XHTML generation and XHTML template processing.
-User-defined element handlers can be added to the XHTML generation to
-support application-specific generation of dynamic content.
-
-
Web application environment
-
-BKNR uses the portable aserve web server implementation as HTTP
-server. It provides for a handler dispatch mechanism based on CLOS
-objects and generic functions to provide for routing requests to
-objects and HTML form handling.
-
-
-Session handling through cookies is supported, as well is a simple
-web-based CMS for maintaining user accounts and other application
-information.
-
-
-A CSS and Javascript generation library supports in-source-code
-generation of complete web pages using a lisp-like syntax.
-
-
Other Libraries
-
-Except for the graphics processing library used, all server-side
-components of BKNR are implemented in Common Lisp. All components are
-Open Source. All software outside of the thirdparty/ tree in the BKNR
-repository are distributed under a BSD-style open source license. The
-thirdparty/ tree contains a number of publicly available Common Lisp
-libraries which are used by BKNR.
-
-BKNR has been co-developed with a number of real-world applications.
-Much of the application code has been kept in the central source code
-repository and evolved through a number of API and framework changes.
-
-
-BKNR started while implementing a
-web site for eboy, a group of graphics
-artists who wanted to have a dynamic web site for publishing their
-works. Their new, fully dynamic web site was implemented in CMU
-Common Lisp
-
-
-
Deleted: trunk/projects/lisp-ecoop/website/templates/cfp.bknr
===================================================================
--- trunk/projects/lisp-ecoop/website/templates/cfp.bknr 2006-02-14 19:21:08 UTC (rev 1832)
+++ trunk/projects/lisp-ecoop/website/templates/cfp.bknr 2006-02-14 21:48:08 UTC (rev 1833)
@@ -1,191 +0,0 @@
-
-
-
-
-
Call for Participation
-
-
2nd European Lisp and Scheme Workshop
-
-
July 26 - Glasgow, Scotland - co-located with ECOOP 2005
-Supported by ALU
-...Please don't assume Lisp is only useful for Animation and Graphics,
-AI, Bioinformatics, B2B and E-Commerce, Data Mining, EDA/Semiconductor
-applications, Expert Systems, Finance, Intelligent Agents, Knowledge
-Management, Mechanical CAD, Modeling and Simulation, Natural Language,
-Optimization, Research, Risk Analysis, Scheduling, Telecom, and Web
-Authoring just because these are the only things they happened to
-list. -- Kent Pitman
-
-
-
Lisp is one of the oldest computer languages still in use today.
-In the decades of its existence, Lisp has been a fruitful basis for
-language design experiments as well as the preferred implementation
-language for applications in diverse fields.
-
-
The structure of Lisp, including Common Lisp and Scheme as its
-major dialects of today, makes it easy to extend the language or even
-to implement entirely new dialects without starting from
-scratch. Common Lisp, with the Common Lisp Object System (CLOS), was
-the first object-oriented programming language to receive an ANSI
-standard at the beginning of the 1990's. It is, arguably, the most
-complete and advanced object system of any programming language, and
-has influenced many other object-oriented programming languages that
-were to follow.
-
-
Despite having somewhat disappeared from the radar of popular
-computer science, Common Lisp and Scheme are still alive and have just
-started to gain momentum again. There is a steadily growing interest
-in Lisp as such, with numerous user groups having formed recently
-worldwide, and Lisp's metaprogramming notions that are being
-transferred to other languages to different extents, as for example in
-AOP, MDA, DSL, and so on.
-
-
This two-day workshop will address the near-future role of
-Lisp-based languages in research, industry and education. We want to
-solicit papers and suggestions for breakout groups that discuss the
-opportunities Lisp provides to capture and enhance the possibilities
-in software engineering. We also want to promote lively discussion
-between researchers proposing new approaches and practitioners
-reporting on their experience with the strengths and limitations of
-current Lisp technologies.
-
-
The workshop will be split into two parts: The first part is for
-talks with a more classroom-like atmosphere, the second will consist
-of a number of sessions for breakout groups discussing or working on
-particular topics.
-
-
Papers
-
-
Presentations on the workshop can take anything between 5 minutes
-and an hour. Additional time will be given for questions and answers.
-Papers may be published on the website in order to provide background
-information in advance.
-
-
Suggested topics for presented papers
-
-
Macro programming
-
Metaprogramming
-
Reflection
-
New language features / abstractions
-
Persistence Systems
-
Hardware support for Lisp systems
-
Case studies
-
Experience reports
-
Industrial applications
-
Object-Oriented Programming
-
Declarative Programming
-
Aspect-Oriented Programming
-
Domain-Oriented Programming
-
Generative Programming
-
Ambient Intelligence
-
Unanticipated Software Evolution
-
Design Patterns
-
Educational Perspectives
-
-
-
Breakout Groups
-
-
The workshop will provide for the opportunity to meet face to face
-and work on focused topics. We will organize these breakout groups
-and provide for rooms and infrastructure.
-
-
Suggested topics for breakout groups
-
-
Discuss the feature set of the next version of Slime
-
Work on details of the CLRFI process
-
Exchange experiences with various macro systems for Scheme
-
Detail a wishlist for R6RS
-
Environments for creating web applications
-
Persistence systems
-
Compiler technology
-
Lisp on bare metal / Lisp hardware / Lisp operating systems
-
Compare and enhance curricula for computer science education
-
-
-
In order to have effective discussions at those breakout groups, we
-are negotiating meeting rooms for an extra day in addition to the
-actual workshop with the ECOOP organizers.
-
-
Submission Guidelines
-
-
Potential attendants are expected to submit
-
-
either a long paper (10 pages) presenting scientific and/or
-empirical results about Lisp- and Scheme-based uses or new approaches
-for software engineering purposes
-
or a short essay (5 pages) defending a position about where
-research and practice based on Lisp and Scheme should be heading in
-the near future
-
a proposal for a breakout group (1-2 pages) describing the theme,
-an agenda and/or expected results
-
-
-
-
Submissions should be mailed as PDF or text to Pascal Costanza (pc at p-cos.net) before the submission
-deadline. Please indicate whether you need an earlier notification of
-acceptance than the official date.
-
-
Registration
-
-
-This workshop is co-located with ECOOP 2005, and this time it is one
-of the official workshops at that conference. Therefore, registration,
-fee and accomodation are all handled through the conference
-organization. See the conference website and especially ecoop - Fees and ecoop - Travel for
-further information.
-
-
-
-Advance registration to the workshop is handled through the primary
-contact person of the Workshop, Pascal Costanza (pc at p-cos.net). Please contact him by
-regular email with your submission or input to the workshop in order
-to receive your login to the workshop website. Include your preferred
-login name for the workshop website with your application. Further
-information for participants will be provided on the workshop website.
-
-
-
Organizing Committee
-
-
The organizing comitee consists of the following people:
+...Please don't assume Lisp is only useful for Animation and Graphics,
+AI, Bioinformatics, B2B and E-Commerce, Data Mining, EDA/Semiconductor
+applications, Expert Systems, Finance, Intelligent Agents, Knowledge
+Management, Mechanical CAD, Modeling and Simulation, Natural Language,
+Optimization, Research, Risk Analysis, Scheduling, Telecom, and Web
+Authoring just because these are the only things they happened to
+list. -- Kent Pitman
+
+
+
Lisp is one of the oldest computer languages still in use today.
+In the decades of its existence, Lisp has been a fruitful basis for
+language design experiments as well as the preferred implementation
+language for applications in diverse fields.
+
+
The structure of Lisp, including Common Lisp and Scheme as its
+major dialects of today, makes it easy to extend the language or even
+to implement entirely new dialects without starting from
+scratch. Common Lisp, with the Common Lisp Object System (CLOS), was
+the first object-oriented programming language to receive an ANSI
+standard at the beginning of the 1990's. It is, arguably, the most
+complete and advanced object system of any programming language, and
+has influenced many other object-oriented programming languages that
+were to follow.
+
+
Despite having somewhat disappeared from the radar of popular
+computer science, Common Lisp and Scheme are still alive and have just
+started to gain momentum again. There is a steadily growing interest
+in Lisp as such, with numerous user groups having formed recently
+worldwide, and Lisp's metaprogramming notions that are being
+transferred to other languages to different extents, as for example in
+AOP, MDA, DSL, and so on.
+
+
This two-day workshop will address the near-future role of
+Lisp-based languages in research, industry and education. We want to
+solicit papers and suggestions for breakout groups that discuss the
+opportunities Lisp provides to capture and enhance the possibilities
+in software engineering. We also want to promote lively discussion
+between researchers proposing new approaches and practitioners
+reporting on their experience with the strengths and limitations of
+current Lisp technologies.
+
+
The workshop will be split into two parts: The first part is for
+talks with a more classroom-like atmosphere, the second will consist
+of a number of sessions for breakout groups discussing or working on
+particular topics.
+
+
Papers
+
+
Presentations on the workshop can take anything between 5 minutes
+and an hour. Additional time will be given for questions and answers.
+Papers may be published on the website in order to provide background
+information in advance.
+
+
Suggested topics for presented papers
+
+
Macro programming
+
Metaprogramming
+
Reflection
+
New language features / abstractions
+
Persistence Systems
+
Hardware support for Lisp systems
+
Case studies
+
Experience reports
+
Industrial applications
+
Object-Oriented Programming
+
Declarative Programming
+
Aspect-Oriented Programming
+
Domain-Oriented Programming
+
Generative Programming
+
Ambient Intelligence
+
Unanticipated Software Evolution
+
Design Patterns
+
Educational Perspectives
+
+
+
Breakout Groups
+
+
The workshop will provide for the opportunity to meet face to face
+and work on focused topics. We will organize these breakout groups
+and provide for rooms and infrastructure.
+
+
Suggested topics for breakout groups
+
+
Discuss the feature set of the next version of Slime
+
Work on details of the CLRFI process
+
Exchange experiences with various macro systems for Scheme
+
Detail a wishlist for R6RS
+
Environments for creating web applications
+
Persistence systems
+
Compiler technology
+
Lisp on bare metal / Lisp hardware / Lisp operating systems
+
Compare and enhance curricula for computer science education
+
+
+
In order to have effective discussions at those breakout groups, we
+are negotiating meeting rooms for an extra day in addition to the
+actual workshop with the ECOOP organizers.
+
+
Submission Guidelines
+
+
Potential attendants are expected to submit
+
+
either a long paper (10 pages) presenting scientific and/or
+empirical results about Lisp- and Scheme-based uses or new approaches
+for software engineering purposes
+
or a short essay (5 pages) defending a position about where
+research and practice based on Lisp and Scheme should be heading in
+the near future
+
a proposal for a breakout group (1-2 pages) describing the theme,
+an agenda and/or expected results
+
+
+
+
Submissions should be mailed as PDF or text to Pascal Costanza (pc at p-cos.net) before the submission
+deadline. Please indicate whether you need an earlier notification of
+acceptance than the official date.
+
+
Registration
+
+
+This workshop is co-located with ECOOP 2005, and this time it is one
+of the official workshops at that conference. Therefore, registration,
+fee and accomodation are all handled through the conference
+organization. See the conference website and especially ecoop - Fees and ecoop - Travel for
+further information.
+
+
+
+Advance registration to the workshop is handled through the primary
+contact person of the Workshop, Pascal Costanza (pc at p-cos.net). Please contact him by
+regular email with your submission or input to the workshop in order
+to receive your login to the workshop website. Include your preferred
+login name for the workshop website with your application. Further
+information for participants will be provided on the workshop website.
+
+
+
Organizing Committee
+
+
The organizing comitee consists of the following people:
-Using this form, you can edit your participant profile for the 2nd
-European LISP and Scheme Workshop. The profile serves as a means of
-communicating the participants interests as well as any submitted
-papers before the actual workshop takes place. It will also be used
-by the workshop coordinators to schedule presentations.
-The workshop web site will stay online after the workshop to
-facilitate further communications.
-
-
-All participant profile data, excluding personal email addresses, can
-be viewed by all visitors of the workshops' web site.
-
-
-
-
-
-Please contact Hans H?bner for
-inquiries relating to the workshop web site. We are interested in
-developing the website into a LISP-based system to coordinate
-distributed development activities and related real-life meetings
-using an incremental development process. See the Website technology blurb for a
-description of the technology used by this web site.
-
Welcome to the Website of the 2nd European LISP and Scheme
-Workshop. Please see the Call for Participation for a
-description of the goals of the workshop.
-
-
News
-
-
May 19, 2005
-
-
-
Richard P. Gabriel has
- agreed to give a keynote talk at the workshop:
-
-
On the Interaction of Life and Machines in Self-Sustaining Systems
-
-Software systems today are produced according to a manufacturing
-model: A finished product is constructed at the factory and shipped to
-its final destination where it is expected to act like any other
-machine-reliably but oblivious to its surroundings and its own
-welfare. Software needs to grow up and become responsible for itself
-and its own future. A promising approach seems to be to separate
-software that does the work from software that keeps the system alive.
-
-
-
-
-
The submission deadline has been extended to June 5,
- 2005.
-
-
The early registration deadline for ECOOP 2005 is June 13,
- 2005. The fee structure for ECOOP 2005 will be announced at the
- conference website soon. See ecoop -
- Important Dates and ecoop - Fees for more information.
Welcome to the Website of the 2nd European LISP and Scheme
+Workshop. Please see the Call for Participation for a
+description of the goals of the workshop.
+
+
News
+
+
May 19, 2005
+
+
+
Richard P. Gabriel has
+ agreed to give a keynote talk at the workshop:
+
+
On the Interaction of Life and Machines in Self-Sustaining Systems
+
+Software systems today are produced according to a manufacturing
+model: A finished product is constructed at the factory and shipped to
+its final destination where it is expected to act like any other
+machine-reliably but oblivious to its surroundings and its own
+welfare. Software needs to grow up and become responsible for itself
+and its own future. A promising approach seems to be to separate
+software that does the work from software that keeps the system alive.
+
+
+
+
+
The submission deadline has been extended to June 5,
+ 2005.
+
+
The early registration deadline for ECOOP 2005 is June 13,
+ 2005. The fee structure for ECOOP 2005 will be announced at the
+ conference website soon. See ecoop -
+ Important Dates and ecoop - Fees for more information.
ECOOP early registration deadline: - not announced yet -
Overview
@@ -31,138 +31,103 @@
list. -- Kent Pitman
-
Lisp is one of the oldest computer languages still in use today.
-In the decades of its existence, Lisp has been a fruitful basis for
+
Lisp is one of the oldest computer languages still in use today. In
+the decades of its existence, Lisp has been a fruitful basis for
language design experiments as well as the preferred implementation
language for applications in diverse fields.
-
The structure of Lisp, including Common Lisp and Scheme as its
-major dialects of today, makes it easy to extend the language or even
-to implement entirely new dialects without starting from
-scratch. Common Lisp, with the Common Lisp Object System (CLOS), was
-the first object-oriented programming language to receive an ANSI
-standard at the beginning of the 1990's. It is, arguably, the most
-complete and advanced object system of any programming language, and
-has influenced many other object-oriented programming languages that
-were to follow.
+
The structure of Lisp makes it easy to extend the language or even to
+implement entirely new dialects without starting from scratch. Common
+Lisp, with the Common Lisp Object System (CLOS), was the first
+object-oriented programming language to receive an ANSI standard and
+retains the most complete and advanced object system of any
+programming language, while influencing many other object-oriented
+programming languages that followed.
-
Despite having somewhat disappeared from the radar of popular
-computer science, Common Lisp and Scheme are still alive and have just
-started to gain momentum again. There is a steadily growing interest
-in Lisp as such, with numerous user groups having formed recently
-worldwide, and Lisp's metaprogramming notions that are being
-transferred to other languages to different extents, as for example in
-AOP, MDA, DSL, and so on.
+
It is clear that Lisp is gaining momentum: there is a
+steadily growing interest in Lisp itself, with numerous user groups
+in existence worldwide, and in Lisp's metaprogramming notions
+which are being transferred to other languages, as
+for example in Aspect-Oriented Programming, support for
+Domain-Specific Languages, and so on.
-
This two-day workshop will address the near-future role of
-Lisp-based languages in research, industry and education. We want to
-solicit papers and suggestions for breakout groups that discuss the
+
This two-day workshop will address the near-future role of Lisp-based
+languages in research, industry and education. We solicit
+papers and suggestions for breakout groups that discuss the
opportunities Lisp provides to capture and enhance the possibilities
-in software engineering. We also want to promote lively discussion
+in software engineering. We want to promote lively discussion
between researchers proposing new approaches and practitioners
reporting on their experience with the strengths and limitations of
current Lisp technologies.
-
The workshop will be split into two parts: The first part is for
-talks with a more classroom-like atmosphere, the second will consist
-of a number of sessions for breakout groups discussing or working on
-particular topics.
+
The workshop will have two components on separate days; there will
+be a day for formally-presented talks, and a day for breakout groups
+discussing or working on particular topics. Additionally, there
+will be opportunities for short, informal talks and demonstrations on
+experience reports, underappreciated results, software under
+development, or other topics of interest.
Papers
-
Presentations on the workshop can take anything between 5 minutes
-and an hour. Additional time will be given for questions and answers.
-Papers may be published on the website in order to provide background
-information in advance.
+
Formal presentations in the workshop should take between 20 minutes
+and half an hour; additional time will be given for questions and
+answers. We encourage that papers be published on the website in order
+to provide background information in advance.
Suggested topics for presented papers
-
Macro programming
-
Metaprogramming
-
Reflection
-
New language features / abstractions
+
New language features or abstractions
+
Experience reports or case studies
+
Protocol Metaprogramming and Libraries
+
Educational approaches
+
Software Evolution
+
Development Aids
Persistence Systems
-
Hardware support for Lisp systems
-
Case studies
-
Experience reports
-
Industrial applications
-
Object-Oriented Programming
-
Declarative Programming
-
Aspect-Oriented Programming
-
Domain-Oriented Programming
-
Generative Programming
-
Ambient Intelligence
-
Unanticipated Software Evolution
-
Design Patterns
-
Educational Perspectives
+
Dynamic Optimization
+
Implementation techniques
+
Innovative Applications
+
Hardware Support for Lisp systems
+
Macro-, reflective-, meta- and/or rule-based development approaches
+
Aspect-Oriented, Domain-Oriented and Generative Programming
Breakout Groups
-
The workshop will provide for the opportunity to meet face to face
-and work on focused topics. We will organize these breakout groups
-and provide for rooms and infrastructure.
+
The workshop will provide for the opportunity to meet face to face and
+work on focused topics. We will organize these breakout groups and
+provide for rooms and infrastructure.
Suggested topics for breakout groups
-
Discuss the feature set of the next version of Slime
-
Work on details of the CLRFI process
-
Exchange experiences with various macro systems for Scheme
-
Detail a wishlist for R6RS
+
Lisp Infrastructure Development and Distribution
+
Language Features (e.g. Predicate Dispatching)
Environments for creating web applications
-
Persistence systems
+
Brainstorming sessions for new or existing open source projects
+
Persistence Systems
Compiler technology
Lisp on bare metal / Lisp hardware / Lisp operating systems
Compare and enhance curricula for computer science education
-
In order to have effective discussions at those breakout groups, we
-are negotiating meeting rooms for an extra day in addition to the
-actual workshop with the ECOOP organizers.
-
Submission Guidelines
-
Potential attendants are expected to submit
+
Potential attendees are encouraged to submit
-
either a long paper (10 pages) presenting scientific and/or
-empirical results about Lisp- and Scheme-based uses or new approaches
-for software engineering purposes
-
or a short essay (5 pages) defending a position about where
-research and practice based on Lisp and Scheme should be heading in
-the near future
-
a proposal for a breakout group (1-2 pages) describing the theme,
-an agenda and/or expected results
+
a long paper (10 pages) presenting scientific and/or
+ empirical results about Lisp-based uses or new approaches for
+ software engineering purposes;
+
a short essay (5 pages) defending a position about where
+ research, practice or education based on Lisp should be heading in
+ the near future;
+
a proposal for a breakout group (1-2 pages) describing the theme, an
+ agenda and/or expected results.
-This workshop is co-located with ECOOP 2005, and this time it is one
-of the official workshops at that conference. Therefore, registration,
-fee and accomodation are all handled through the conference
-organization. See the conference website and especially ecoop - Fees and ecoop - Travel for
-further information.
-
-
-
-Advance registration to the workshop is handled through the primary
-contact person of the Workshop, Pascal Costanza (pc at p-cos.net). Please contact him by
-regular email with your submission or input to the workshop in order
-to receive your login to the workshop website. Include your preferred
-login name for the workshop website with your application. Further
-information for participants will be provided on the workshop website.
-
-
Organizing Committee
The organizing comitee consists of the following people:
\ No newline at end of file
Modified: trunk/projects/lisp-ecoop/website/templates/edit-profile.xml
===================================================================
--- trunk/projects/lisp-ecoop/website/templates/edit-profile.xml 2006-02-17 19:41:42 UTC (rev 1835)
+++ trunk/projects/lisp-ecoop/website/templates/edit-profile.xml 2006-02-17 19:53:13 UTC (rev 1836)
@@ -6,13 +6,13 @@
title="Edit Profile"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:bknr="http://bknr.net"
- xmlns:lisp-ecoop05="http://lisp-ecoop05.bknr.net"
+ xmlns:lisp-ecoop06="http://lisp-ecoop06.bknr.net"
>
Participant Profile Editor
-Using this form, you can edit your participant profile for the 2nd
-European LISP and Scheme Workshop. The profile serves as a means of
+Using this form, you can edit your participant profile for the 3rd
+European Lisp Workshop. The profile serves as a means of
communicating the participants interests as well as any submitted
papers before the actual workshop takes place. It will also be used
by the workshop coordinators to schedule presentations.
@@ -23,7 +23,7 @@
All participant profile data, excluding personal email addresses, can
be viewed by all visitors of the workshops' web site.
-
+
-
+
Please contact Hans H?bner for
inquiries relating to the workshop web site. We are interested in
Modified: trunk/projects/lisp-ecoop/website/templates/edit-submission.xml
===================================================================
--- trunk/projects/lisp-ecoop/website/templates/edit-submission.xml 2006-02-17 19:41:42 UTC (rev 1835)
+++ trunk/projects/lisp-ecoop/website/templates/edit-submission.xml 2006-02-17 19:53:13 UTC (rev 1836)
@@ -6,15 +6,15 @@
title="Edit Submission"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:bknr="http://bknr.net"
- xmlns:lisp-ecoop05="http://lisp-ecoop05.bknr.net"
+ xmlns:lisp-ecoop06="http://lisp-ecoop06.bknr.net"
>
Submission Editor
-Using this form, you can edit your submission to the 2nd
-European LISP and Scheme Workshop.
+Using this form, you can edit your submission to the 3rd
+European Lisp Workshop.
Welcome to the Website of the 2nd European LISP and Scheme
+
Welcome to the Website of the 3rd European Lisp
Workshop. Please see the Call for Participation for a
description of the goals of the workshop.
News
-
May 19, 2005
+
February 20, 2006
-
Richard P. Gabriel has
- agreed to give a keynote talk at the workshop:
-
-
On the Interaction of Life and Machines in Self-Sustaining Systems
-
-Software systems today are produced according to a manufacturing
-model: A finished product is constructed at the factory and shipped to
-its final destination where it is expected to act like any other
-machine-reliably but oblivious to its surroundings and its own
-welfare. Software needs to grow up and become responsible for itself
-and its own future. A promising approach seems to be to separate
-software that does the work from software that keeps the system alive.
-
-
-
+
Launched the workshop website.
+
-
The submission deadline has been extended to June 5,
- 2005.
+
Information for Attendees
-
The early registration deadline for ECOOP 2005 is June 13,
- 2005. The fee structure for ECOOP 2005 will be announced at the
- conference website soon. See ecoop -
- Important Dates and ecoop - Fees for more information.
+
Registration of workshop participants has to be done in three mandatory steps:
+
+
Contact the organizers of the workshop (in order to ensure that the participant limit has not been exceeded).
+
Advance registration to the workshop is handled through the primary
+contact person of the workshop, Pascal Costanza (pc at p-cos.net). Please contact him by
+regular email with your submission or input to the workshop in order
+to receive your login to the workshop website. Include your preferred
+login name for the workshop website with your application.
+
Register on the ECOOP 2006 web site either as a worskhop-only attendee or as a regular attendee. The latter includes access to workshops and to the main conference.
Register on the ECOOP 2006 web site either as a worskhop-only attendee or as a regular attendee. The latter includes access to workshops and to the main conference.
+
Register on the ECOOP 2006 web
+site either as a
+worskhop-only attendee or as a regular attendee. The
+latter includes access to workshops and to the main conference.
+
+"
+ :newline
+ )
+ ((:body :onload "sf()"
+ :bgcolor
+ (if* to-users
+ then *bottom-frames-private*
+ else *bottom-frames-bgcolor*))
+ ((:form :action (concatenate 'string
+ "chatenter?"
+ qstring)
+ :method "POST"
+ :name "f"
+ )
+ (:center
+ (if* (eq kind :multiline)
+ then (html
+ (:table
+ (:tr
+ (:td
+ (:center
+ ((:input :name "send"
+ :value "Send"
+ :type "submit"))
+ " "
+ (if* user
+ then (html
+ (if* to-users
+ then (html
+ "Private msg from: ")
+ else (html "From: "))
+ (:b
+ (:princ-safe
+ (user-handle user)))
+ " to "
+ (:b
+ (if* to-users
+ then (dolist (to-user to-users)
+ (html
+ (:princ-safe
+ (user-handle
+ to-user))
+ " "
+ ))
+ else (html "all"))))
+
+ else (html
+ "Your Name"
+ ((:input :name "handle"
+ :type "text"
+ :tabindex 3
+ :size 20
+ :value (if* handle then handle else "")))))
+ " -- "
+ ((:a :href (format nil "chatlogin?~a" qstring)
+ :target "_top")
+ "Login")
+ " -- "
+
+ ((:input :name "send"
+ :tabindex 2
+ :value "Send"
+ :type "submit"))
+ (if* user
+ then (html " "
+ ((:a :href (format nil "chatenter-pic?~a&pp=~a"
+ qstring pp))
+ "upload picture")))
+ )))
+ (:tr
+ (:td
+ ((:textarea :name "body"
+ :tabindex 1
+ :cols 50
+ :rows 5))
+ ((:input :type "hidden"
+ :name "pp"
+ :value pp))))
+ (:tr
+ (:td
+ (:center
+ ((:input :type "text"
+ :size 40
+ :maxlength 100
+ :value (or purl "")
+ :name "purl"))
+ " Picture Url")))))
+ else ; single line
+ (html
+ (:table
+ (:tr
+ ((:td :colspan 1)
+ (:center
+ "Your Name"
+ ((:input :name "handle"
+ :type "text"
+ :size 20
+ :value (if* handle then handle else "")))
+ ((:input :name "send"
+ :value "Post Message"
+ :type "submit")))))
+ (:tr
+ (:td
+ ((:input :type "text"
+ :name "body"
+ :size 60
+ :maxsize 10000)))))))))
+
+ ))))))))
+
+(defun chatenter-pic (req ent)
+ ;;
+ ;; this is the window where you enter the post and your handle.
+ ;; this version is for when you post a picture
+ ;
+ (let* ((chat (chat-from-req req))
+ (user (user-from-req req))
+ (pp (or (request-query-value "pp" req) "*")) ; who to send to
+ (ppp (request-query-value "ppp" req)) ; add a user to the dest
+ (to-users (users-from-pstring pp))
+ (qstring))
+ (if* (or (null chat) (null user))
+ then (return-from chatenter-pic
+ (ancient-link-error req ent)))
+
+ (if* (eq (request-method req) :post)
+ then (process-incoming-file chat req user to-users)
+ (setf (request-method req) :get)
+ (return-from chatenter-pic (chatenter req ent)))
+
+ (let* ()
+
+ (setq qstring
+ (add-secret req
+ (add-user req
+ (chat-query-string chat))))
+
+
+ ;; user must be true
+ (setf (user-time user) (get-universal-time))
+
+ (if* ppp
+ then ; add this user
+
+ (setq pp (setf (user-to-users user)
+ (concatenate 'string
+ (or (user-to-users user) "")
+ ","
+ ppp)))
+ (setq to-users (users-from-pstring pp))
+ elseif (equal pp "*")
+ then (setf (user-to-users user) nil)
+ else (setf (user-to-users user) pp))
+
+
+ ; now the logged in or not logged in check
+ (if* (redir-check req ent chat nil)
+ then (return-from chatenter-pic))
+
+
+
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:html
+ ((:body :bgcolor
+ (if* to-users
+ then *bottom-frames-private*
+ else *bottom-frames-bgcolor*))
+ ((:form :action (concatenate 'string
+ "chatenter-pic?"
+ (format nil "~a&pp=~a" qstring pp))
+ :method "POST"
+ :enctype "multipart/form-data"
+ )
+ (:center
+
+ (html
+ (:table
+ (:tr
+ (:td
+ (:center
+ ((:input :name "send"
+ :value "Send"
+ :type "submit"))
+ " "
+
+ (html
+ (if* to-users
+ then (html
+ "Private msg from: ")
+ else (html "From: "))
+ (:b
+ (:princ-safe
+ (user-handle user)))
+ " to "
+ (:b
+ (if* to-users
+ then (dolist (to-user to-users)
+ (html
+ (:princ-safe
+ (user-handle
+ to-user))
+ " "
+ ))
+ else (html "all"))))
+ " -- "
+ ((:a :href (format nil "chatlogin?~a" qstring)
+ :target "_top")
+ "Login")
+ " -- "
+
+ ((:input :name "send"
+ :tabindex 2
+ :value "Send"
+ :type "submit")))))
+ (:tr
+ (:td
+ "The picture file to upload (click Browse):" :br
+ ((:input :type "file"
+ :name "thefile"
+ :size 40
+ :value "*.jpg")))
+ (:tr
+ (:td
+ "Add commments about your picture" :br
+ ((:textarea :name "comments"
+ :tabindex 1
+ :cols 50
+ :rows 3)))))))))))))))))
+
+
+
+(defparameter *pic-counter* 0)
+
+(defun process-incoming-file (chat req user to-users)
+ (let ((comment "") type upload-pic)
+ (loop
+ (multiple-value-bind (kind name filename content-type)
+ (parse-multipart-header
+ (get-multipart-header req))
+ (case kind
+ (:eof (return))
+ (:data ; must be contents
+ (if* (equal name "comments")
+ then (setq comment (get-all-multipart-data req))))
+ (:file
+ (let ((contents (get-all-multipart-data req :type :binary
+ :limit 2000000)))
+ ; see if it ends in .jpg or .gif
+ (if* (member content-type '("image/jpeg"
+ "image/pjpeg"
+ "image/jpg")
+ :test #'equal)
+ then (setq type "jpg")
+ elseif (equal content-type "image/gif")
+ then (setq type "gif")
+ else (format t "uploaded type of ~s is ~s~%"
+ filename content-type))
+ (if* type
+ then (let ((filename (concatenate 'string
+ (format nil "~x" (incf *chat-picname* 23))
+ "."
+ type)))
+ (with-open-file (p (concatenate 'string
+ *chat-home-pics*
+ "/"
+ filename)
+ :direction :output
+ :if-exists :supersede)
+ (write-sequence contents p))
+ (setq upload-pic
+ `(:span :br ((:img :src ,(format nil "/chatpics/~a" filename))) :br))))))
+ (t (get-all-multipart-data req :limit 1000)))))
+
+ (if* (or (and comment (> (length comment) 0))
+ upload-pic)
+ then (add-chat-data chat req nil comment user to-users nil
+ upload-pic))))
+
+
+
+
+
+
+#+ignore
+(defun process-incoming-file (chat req user to-users)
+ ;; read the multipart file, publish it
+ ;; create the message referencing it, and then add that to the chat.
+ (let (file content-type comment upload-pic)
+ (loop (let ((h (get-multipart-header req)))
+ (if* (null h) then (return))
+ (pprint h)(force-output)
+ (let ((name (cdr
+ (assoc "name"
+ (cddr (assoc :param
+ (cdr (assoc :content-disposition h :test #'eq))
+ :test #'eq))
+ :test #'equal))))
+ (if* (equal name "thefile")
+ then ; the file we're uploading
+ (setq content-type (cadr (assoc :content-type h :test #'eq)))
+ (setq file (read-multipart-guts req))
+
+ elseif (equal name "comments")
+ then ; read the comments
+ (setq comment (octets-to-string (read-multipart-guts req)))
+ else (read-multipart-guts req)))))
+
+ ;; now we may have a picture
+ (if* (and file content-type)
+ then ; we have guts
+ (let ((picname (format nil "/chatpix/~d~d"
+ (get-universal-time) (incf *pic-counter*))))
+ (publish-multi :path picname
+ :content-type content-type
+ :items (list (list :binary file)))
+
+ (setq upload-pic
+ `(:span :br ((:img :src ,picname)) :br))
+
+ (setq comment (or comment ""))))
+
+ (if* (and comment (> (length comment) 0))
+ then (add-chat-data chat req nil comment user to-users nil
+ upload-pic))
+
+
+ ))
+
+
+
+(defun read-multipart-guts (req)
+ (let ((buffer (make-array 40000 :element-type '(unsigned-byte 8)))
+ (buffs)
+ (total-size 0))
+ (loop (let ((count (get-multipart-sequence req buffer)))
+ (if* count
+ then (incf total-size count)
+ (push (subseq buffer 0 count) buffs)
+ else (return))))
+
+ (setq buffer (make-array total-size :element-type '(unsigned-byte 8)))
+ (let ((count 0))
+ (dolist (buf (nreverse buffs))
+ (replace buffer buf :start1 count)
+ (incf count (length buf))))
+ buffer))
+
+
+
+
+
+
+
+
+
+(defun do-idle-timedout (req ent goback)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head (:title "timed out"))
+ (:body "due to inactivity you have been timed out"
+ :br
+ (if* goback
+ then (html "To return to the chat click "
+ ((:a :href goback
+ :target "_top")
+ "here"))))))))
+
+
+(defun chatcontrol (req ent)
+ ; control the updating
+ (let ((chat (chat-from-req req))
+ (qstring))
+
+ (if* (null chat)
+ then (return-from chatcontrol (ancient-link-error req ent)))
+
+ (let* ((count (or (request-query-value "count" req) *default-count*))
+ (secs (or (request-query-value "secs" req) *default-secs*)))
+
+ (setq qstring
+ (add-lurk
+ req
+ (add-secret req
+ (add-user req (chat-query-string chat)))))
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:html
+ ((:body :bgcolor *bottom-frames-bgcolor*)
+ ((:form :action
+ (concatenate 'string
+ "chattop?"
+ qstring
+ )
+ :target "chattop"
+ :method "POST")
+ ((:input :type "text"
+ :name "secs"
+ :size 3
+ :value secs)
+ "Seconds")
+ :br
+ ((:input :type "text"
+ :name "count"
+ :size 4
+ :value count))
+ "messages"
+ :br
+ ((:input :type "checkbox"
+ :name "rv"
+ :value "1"))
+ " Reversed"
+ :br
+
+ ; use to distinguish a call to chattop from
+ ; a user button click from a refresh one
+
+ ((:input :type "hidden"
+ :name "hitbut"
+ :value "did"))
+
+ ((:input :type "submit"
+ :name "submit"
+ :value "Update Messages"))
+
+
+ ; optional chat transcript link
+ (if* *offer-transcript*
+ then (html
+ :br :hr
+ ((:a :href (format nil "chattranscript?~a" qstring)
+ :target "_blank")
+ "View transcript.")))
+ )))))))))
+
+
+(defun compute-integer-value (string)
+ ;; compute the string to a number
+ ;; if there's any junk return nil if we haven't seen good stuff yet
+ (and (stringp string)
+ (let ((ans 0))
+ (do ((i 0 (1+ i))
+ (end (length string))
+ (seen-digit)
+ )
+ ((>= i end)
+ (if* seen-digit
+ then ans
+ else nil))
+ (let ((digit (- (char-code (schar string i)) #.(char-code #\0))))
+ (if* (<= 0 digit 9)
+ then (setq ans (+ (* ans 10) digit))
+ (setq seen-digit t)
+ else (if* seen-digit
+ then (return ans)
+ else (return nil))))))))
+
+
+
+(defun add-chat-data (chat req handle body user to-users purl upload-pic)
+ ;; chat is chat object
+ ;; req is http request object
+ ;; handle is handle typed by user (only matters if user not logged in)
+ ;; body is the string that's the posting
+ ;; user is the user object if user is logged in
+ ;; to-user is nil or the string naming the private message receipient
+ ;; purl is picture url value
+ (multiple-value-bind (prefix link)
+ (if* (and (stringp purl) (not (equal "" purl)))
+ then (scan-for-http purl))
+ (declare (ignore prefix))
+
+
+
+ (if* (stringp to-users)
+ then ; just one user, turn it into a list
+ (setq to-users (list to-users)))
+
+ (if* link
+ then (if* (and (consp link)
+ (consp (car link))
+ (eq :img (caar link)))
+ thenret ; valid image url
+ else (setq link nil)))
+
+ (if* (null link)
+ then (setq link upload-pic))
+
+ (let* ((cvted-body (html-chk-string-to-lhtml body))
+ (ipaddr (socket:remote-host
+ (request-socket req)))
+ (dns (or #+ignore (socket:ipaddr-to-hostname ipaddr)
+ (socket:ipaddr-to-dotted ipaddr)))
+ (ut (get-universal-time))
+
+ (message
+ (make-message
+ :number (chat-message-number chat)
+ :ipaddr ipaddr
+ :dns dns
+ :handle (if* user then (user-handle user) else handle)
+ :to (if* to-users
+ then (mapcar #'user-handle to-users)
+ else t)
+ :real (if* user then t else nil)
+ :time (let ((time (compute-chat-date ut)))
+ (if* *message-id-hook*
+ then (funcall *message-id-hook* time)
+ else time))
+ :ut ut
+ :body (if* link
+ then (cons link cvted-body)
+ else cvted-body))))
+
+ (mp:with-process-lock ((chat-message-lock chat))
+ (add-chat-message chat message)))))
+
+(defun compute-chat-date (ut)
+ ; return string to use as time for this message
+ ; quick hack - hardwire in pdt
+ (multiple-value-bind (sec min hour day month)
+ (decode-universal-time ut)
+ (format nil "~d:~2,'0d:~2,'0d Pacific Time, ~a ~d" hour min sec
+ (month-name month) day
+ )))
+
+(defun month-name (month)
+ (svref '#("" "Jan" "Feb" "Mar" "Apr" "May" "June" "July"
+ "Aug" "Sep" "Oct" "Nov" "Dec")
+ month))
+
+(defun add-chat-message (chat message)
+ ;; add the message to the messages of the chat.
+ ;; assume that we've got the lock to do this.
+ (let ((messages (chat-messages chat))
+ (message-next (chat-message-next chat)))
+
+ (if* (>= message-next (length messages))
+ then ; must grow messages
+ (let ((nmessages (make-array (+ (length messages)
+ *msg-increment*))))
+ ;; copy only non-deleted messages
+ (let ((to 0))
+ (dotimes (i (length messages))
+ (let ((message (svref messages i)))
+ (if* (message-to message)
+ then (setf (svref nmessages to) message)
+ (incf to))))
+ (setq message-next to)
+ (setf (chat-messages chat) nmessages)
+ (setq messages nmessages))))
+ (setf (svref messages message-next) message)
+ (setf (chat-message-next chat) (1+ message-next))
+ (setf (chat-message-number chat)
+ (1+ (message-number message)))))
+
+
+
+(defun delete-chat-message (chat messagenum is-owner handle)
+ ;; remove the message numbered messagenumy setting the to field to nil
+ (mp:with-process-lock ((chat-message-lock chat))
+ (let ((message (find-chat-message chat messagenum)))
+ (if* (and message
+ (or is-owner ; owner can remove all
+ (and handle
+ (equal handle (message-handle message)))))
+ then (setf (message-to message) nil)
+ (push messagenum (chat-messages-deleted chat))))))
+
+(defun delete-chat-message-by-message (chat message)
+ ;; remove the given message by setting the to field to nil
+ (mp:with-process-lock ((chat-message-lock chat))
+ (if* message
+ then (setf (message-to message) nil)
+ (push (message-number message)
+ (chat-messages-deleted chat)))))
+
+(defun find-chat-message (chat number)
+ ;; find the message with the given number
+ (let* ((messages (chat-messages chat))
+ (len (and messages (chat-message-next chat)))
+ (bottom 0)
+ (top (and len (1- len)))
+ )
+ (if* messages
+ then ; find first message
+ ; do binary search
+ #+ignore (format t "Want message ~s~%" number)
+ (loop
+ (if* (> bottom top)
+ then (return nil) ; no message found
+ else (let ((try (truncate (+ top bottom) 2)))
+ #+ignore (format t "try ~d (~d -> ~d)~%"
+ try bottom top)
+ (let ((message (svref messages try)))
+ (if* message
+ then #+ignore (format t "try msg num is ~s~%"
+ (message-number message))
+ (if* (eql (message-number message) number)
+ then #+ignore (format t "**found~%")
+ (return message)
+ elseif (< (message-number message)
+ number)
+ then ; in top quadrant
+ (setq bottom
+ (max (1+ bottom) try))
+ else (setq top
+ (min (1- top) try)))
+ else (warn "Null chat message at ~d"
+ try)
+ (return nil)))))))))
+
+
+(defun show-message-p (message handle)
+ ;; return true if this message should be shown to someone with
+ ;; the handle 'handle'
+ ;;
+ ;; handle is non-nil iff this person is logged in.
+ ;;
+ ;; message-to is nil if this is a deleted message in which case
+ ;; no one should see it.
+ ;;
+ (or
+ ; show everyone
+ (eq t (message-to message))
+
+ ; message specifically to handle
+ (and handle (member handle (message-to message) :test #'equal))
+
+ ; message from 'handle' and to at least one person
+ (and (equal (message-handle message) handle)
+ (message-to message))))
+
+
+(defun find-nth-message (messages start handle count)
+ ;; count down from start to find the index of the counth
+ ;; message visible to handle. return that index
+
+ (assert (> count 0))
+
+ (loop
+ (if* (<= start 0) then (return 0))
+ (let ((message (svref messages start)))
+ (if* (show-message-p message handle)
+ then (if* (<= (decf count) 0) then (return start)))
+ (decf start))))
+
+
+(defun compute-chat-statistics (chat)
+ ;; compute information about this chat
+ (mp::with-process-lock ((chat-message-lock chat))
+ (let ((messages (chat-messages chat))
+ (message-next (chat-message-next chat)))
+ (let ((total-messages 0)
+ (private-messages 0))
+ (dotimes (i message-next)
+ (let ((message (svref messages i)))
+ (if* message
+ then (if* (message-to message)
+ then (incf total-messages)
+ (if* (not (eq t (message-to message)))
+ then (incf private-messages))))))
+
+ (values total-messages private-messages)))))
+
+
+
+(defun set-saved-chat-messages (chat count)
+ ;; set to save approx 'count' messages
+ (mp::with-process-lock ((chat-message-lock chat))
+ (let ((messages (chat-messages chat))
+ (message-next (chat-message-next chat)))
+ ; count backwards until we've passed 'count' messages
+ (do ((i (1- message-next) (1- i)))
+ ((< i 0)
+ ; no messages to remove
+ nil)
+
+ (let ((message (svref messages i)))
+ (if* message
+ then (if* (<= count 0)
+ then ; remove all messages at this point
+ (delete-chat-message-by-message chat message)
+ else (if* (message-to message)
+ then (decf count)))))))))
+
+
+
+
+
+
+
+
+
+
+(defun show-chat-info (chat count recent-first handle ownerp qstring)
+ ;; show the messages for all and those private ones for handle
+ ;; handle is only non-nil if this is a legit logged in handle
+ (let ((message-next (chat-message-next chat))
+ (messages (chat-messages chat))
+ (first-message)
+ (last-message)
+ (nth-message)
+ (message-increment)
+ )
+
+ ;; if the person is not logged in then minimize the count
+ (if* *restrict-messages*
+ then (if* (null handle)
+ then (setq count (min 5 count))
+ else (let ((user (user-from-handle handle)))
+ (if* (and user (null (user-level user)))
+ then (setq count (min 10 count))))))
+
+
+ (if* (zerop message-next)
+ then (html (:b "There are no messages in this chat"))
+ elseif (<= count 0)
+ thenret ; nothing to show
+ else ; starting at the end find the counth message
+ (setq nth-message
+ (find-nth-message messages (1- message-next) handle count))
+
+ (if* recent-first
+ then (setq first-message (1- message-next)
+ last-message nth-message
+ message-increment -1)
+ else (setq last-message (1- message-next)
+ first-message nth-message
+ message-increment 1))
+
+ (if* recent-first
+ then ; tag most recent message
+ (html ((:div :id "recent"))))
+
+ (do ((i first-message (+ i message-increment)))
+ (nil)
+
+ (let ((message (svref messages i)))
+ (if* (null message)
+ then (warn "null message at index ~d" i)
+ elseif (if* (or (eq t (message-to message))
+ (member handle (message-to message)
+ :test #'equal))
+ then ;; to everyone or us
+ nil ; don't skip
+ elseif (and (equal (message-handle message)
+ handle)
+ (message-to message))
+ then ;; from us to someone, anyone
+ nil ; don't skip
+ else t ; skip
+ )
+ thenret ; skip this message
+ elseif (eq *show-style* 1)
+ then
+ (html :newline
+ ((:font :color
+ (if* (consp (message-to message))
+ then *private-font-color*
+ else *public-font-color*))
+
+ (:b (:i (:princ-safe (message-handle message))))
+ (if* (not (message-real message))
+ then (html " (unverified)"))
+ ((:font :size 1)
+ " -- ("
+ (:princ (message-time message))
+ (if* (consp (message-to message))
+ then (html " to: "
+ (:princ-safe (message-to message))))
+ ")")
+
+ " "
+ (if* (or ownerp
+ (and (message-real message)
+ (equal (message-handle message)
+ handle)))
+ then (html
+ ((:a :href
+ (format nil "chattop?y=~a&~a"
+ (message-number message)
+ (or ownerp qstring)))
+ "Delete")))
+
+ (if* ownerp
+ then
+ (let ((user (and (message-real message)
+ (user-from-handle
+ (message-handle message)))))
+ (if* (and user (null (user-level user)))
+ then ; can upgrade if desired
+ (html " "
+ ((:a :href
+ (format nil
+ "chattop?b=~a&~a"
+ (user-ustring
+ user)
+ ownerp))
+ " Upgrade ")))))
+ :newline
+ :br
+ (html-print-list (message-body message)
+ *html-stream*)
+ :br)
+ :newline)
+ else
+ (html
+ :newline
+ ((:table :border 1 :width "100%" :frame "box")
+ (:tr
+ ((:td :width "10%")
+ (:b (:i (:princ-safe (message-handle message))))
+ :br
+ ((:font :size 1) (:princ (message-time message)))
+ " "
+ )
+ (:td
+ (html-print-list (message-body message)
+ *html-stream*)))))))
+
+ (if* (eql i last-message) then (return)))
+
+ (if* (not recent-first)
+ then ; tag most recent message
+ (html ((:div :id "recent")))))
+
+ (if* (null handle)
+ then (html :br
+ ((:table :border 1)
+ (:tr
+ (:td
+ (if* *restrict-messages*
+ then (html
+
+ "In order to have access to the other facilities of this chat, "
+ "such as private messaging and viewing the history of messages "
+ "you must log in, by clicking on the Login link below.")
+ else (html
+
+ "In order to have access to the other facilities of this chat, "
+ "such as private messaging "
+ "you must log in, by clicking on the Login link below.")
+ ))))))
+
+ ))
+
+
+
+
+(defun chatlogin (req ent)
+ ;; response function for /chatlogin?ucstring"
+ (let ((chat (chat-from-req req)))
+ (if* chat
+ then (do-chat-login req ent
+ (add-secret req
+ (add-user req
+ (chat-query-string chat)))
+ nil)
+ else (ancient-link-error req ent))))
+
+
+(defun do-chat-login (req ent qstring failure)
+ ;; put up a login screen for this chat
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:html
+ (:head (:title "Login to Chat"))
+ (:body
+ (if* failure
+ then (html (:blink
+ (:b "Error: " (:princ-safe failure) :br :br))))
+
+ (:h1 "Login as an existing user")
+ ((:form :action (format nil "chatlogincurrent?~a" qstring)
+ :target "_top"
+ :method "POST")
+ ((:input :type "text" :size "15" :name "handle")) "Your Handle" :br
+ ((:input :type "password" :size "15" :name "password")) "Your password" :br
+ ((:input :type "submit" :name "submit" :value "login")))
+ :hr
+ (:h1 "Create a new account and login")
+ ((:form :action (format nil "chatloginnew?~a" qstring)
+ :method "POST")
+ ((:input :type "text" :size "15" :name "handle")) "Your Handle" :br
+ ((:input :type "password" :size "15" :name "password")) "Your password" :br
+ ((:input :type "password" :size "15" :name "password2")) "Type your password again" :br
+ ((:input :type "submit" :name "submit" :value "New Account")))))))))
+
+
+(defun chat-login-current (req ent)
+ ;; handle a post to chatlogincurrent
+
+ ; guard aginst
+ (if* (not (eq :post (request-method req)))
+ then (return-from chat-login-current (ancient-link-error req ent)))
+
+ (let ((chat (chat-from-req req))
+ (handle (request-query-value "handle" req))
+ (password (request-query-value "password" req)))
+ ; locate the handle
+ (let ((user (find handle (users *master-controller*)
+ :key #'user-handle :test #'equalp)))
+ (if* (null user)
+ then (return-from chat-login-current
+ (do-chat-login req ent
+ (add-secret req
+ (add-user req
+ (chat-query-string chat)))
+ "That user name is unknown")))
+ (if* (not (equal password (user-password user)))
+ then (return-from chat-login-current
+ (do-chat-login req ent
+ (add-secret req
+ (add-user req
+ (chat-query-string chat)))
+ "That password is incorrect")))
+
+ ; worked, do a redirect
+ (with-http-response (req ent :response *response-moved-permanently*)
+ (setf (reply-header-slot-value req :location)
+ (format nil "chat?~a&x=~a"
+ (add-secret req
+ (chat-query-string chat))
+ (user-ustring user)))
+ (set-chat-cookie req (user-cookie user))
+ (with-http-body (req ent)
+ (html "redirect"))))))
+
+
+
+(defun chatloginnew (req ent)
+ ;; response function when a new user is being defined
+
+
+ (if* (not (eq :post (request-method req)))
+ then (return-from chatloginnew (ancient-link-error req ent)))
+
+ (let* ((handle (request-query-value "handle" req))
+ (password (request-query-value "password" req))
+ (password2 (request-query-value "password2" req))
+ (chat (chat-from-req req))
+ (qstring (and chat (chat-query-string chat))))
+
+ (if* (null chat)
+ then (return-from chatloginnew (ancient-link-error req ent)))
+
+
+ (if* (equal "" password)
+ then (return-from chatloginnew
+ (do-chat-login req ent qstring "No password given")))
+
+ (if* (not (equal password password2))
+ then (return-from chatloginnew
+ (do-chat-login req ent qstring "Passwords don't match")))
+
+ (dolist (user (users *master-controller*))
+ (if* (equalp (user-handle user) handle)
+ then (return-from chatloginnew
+ (do-chat-login req ent qstring "That user name exists"))))
+
+ ; add new user
+ (let (new-ustring new-pstring new-cookie)
+ (mp:with-process-lock ((master-lock *master-controller*))
+ (loop
+ (setq new-ustring (make-unique-string))
+ (setq new-pstring (make-unique-string))
+ (if* (dolist (user (users *master-controller*) t)
+ (if* (or (equal new-ustring (user-ustring user))
+ (equal new-ustring (user-pstring user)))
+ then ; already in use
+ (return nil)))
+ then (return)))
+ ; leave the loop with new-ustring being unique among users
+ (push (make-user :handle handle
+ :password password
+ :ustring new-ustring
+ :pstring new-pstring
+ :cookie (setq new-cookie (make-unique-string)))
+ (users *master-controller*))
+ (dump-existing-chat *chat-home*))
+
+ ; go to the chat as the user
+ (with-http-response (req ent :response
+ *response-moved-permanently*)
+ (setf (reply-header-slot-value req :location)
+ (format nil "chat?~a&x=~a"
+ (add-secret req qstring) new-ustring))
+ (set-chat-cookie req new-cookie)
+ (with-http-body (req ent)
+ "move to the chat")))))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+(defun html-chk-string-to-lhtml (form)
+ ;; look for {< to start html and >} to end it.
+ ;;
+ (multiple-value-bind (match full first quoted last)
+ (match-regexp "\\(.*\\){<\\(.*\\)>}\\(.*\\)" form :newlines-special nil)
+ (declare (ignore full))
+ (if* match
+ then ; contains embedded html
+ (append (string-to-lhtml first)
+ (list quoted)
+ (string-to-lhtml last))
+ else (string-to-lhtml form))))
+
+
+
+
+(defun string-to-lhtml (form)
+ ;; convert the string to a list of lhtml forms
+ ;;
+ ;; break the text into lines separated by :br's.
+ ;; look for http://'s in the lines and replace them with
+ ;; links or inline images
+
+ (let (res (i 0) (start 0) (max (length form)))
+ (loop
+ ; we go around one last time when (eq i max) in which
+ ; case we pretent there's a linefeed at the end
+ (let ((ch
+ (if* (>= i max)
+ then #\linefeed
+ else (schar form i))))
+
+ (if* (or (eq ch #\return) (eq ch #\linefeed))
+ then ; end of line
+ (if* (not (eq start i))
+ then (let ((line (subseq form start i)))
+ (loop
+ (if* (or (null line)
+ (equal line ""))
+ then (return))
+ (multiple-value-bind (pref link rest)
+ (scan-for-http line)
+ (if* link
+ then (push (de-angle pref) res)
+ (push link res)
+ (setq line rest)
+ else (push (de-angle pref) res)
+ (setq line nil))))))
+ (push :br res)
+
+ (incf i)
+ (if* (and (eq ch #\return)
+ (< i max)
+ (eq (schar form i) #\linefeed))
+ then (incf i) ; past following linefeed
+ )
+
+ (setq start i)
+ else (incf i))
+
+ (if* (> i max) then (return))))
+ (nreverse res)))
+
+
+(defun de-angle (str)
+ ;; replace < and > in strings by their entity tags
+ (if* (find #\< str)
+ then (setq str (replace-regexp str "<" "<")))
+ (if* (find #\> str)
+ then (setq str (replace-regexp str ">" ">")))
+ str)
+
+
+(defun scan-for-http (line)
+ ;; look for http:// in the line and if found return it as
+ ;; a link or image lhtml
+ ;;
+
+ (multiple-value-bind (ok whole)
+ (match-regexp "http://[^ >]+" line :return :index)
+ (if* ok
+ then ; found one
+ (let (http)
+ (setq http (subseq line (car whole) (cdr whole)))
+
+ (values
+ ; value 1 -- everything before the http
+ (subseq line 0 (car whole))
+
+ ; value 2 - the link
+
+ (do ((i (1- (length http)) (1- i)))
+ ((< i 0)
+ ; didn't find a . .. set to a link
+ `((:a :href ,http :target "_blank") (:princ-safe ,http)))
+
+ (if* (eq (schar http i) #\.)
+ then ; found a period
+ (let ((type (subseq http (1+ i))))
+ (if* (member type '("gif" "jpg" "png")
+ :test #'equalp)
+ then ; an image link
+ (return
+ `((:img :src ,http)))
+ else (setq i 0) ; stop search
+ ))))
+
+ ; value 3 - the rest of the line
+ (subseq line (cdr whole))))
+ else line)))
+
+
+;; chatmaster page
+
+(defun chatmaster (req ent)
+ ;; commands
+ ;;
+ (let* ((chat (chat-from-req req))
+ (is-owner
+ (equal (and chat (secret-key chat))
+ (request-query-value "s" req)))
+ (act (request-query-value "act" req)))
+ (if* (not is-owner)
+ then (illegal-access req ent)
+ (return-from chatmaster nil))
+
+ (if* (equal act "set-msg-count")
+ then ; set the message count to the given value
+ (let ((val (compute-integer-value
+ (request-query-value "val" req))))
+ (if* (>= val 0)
+ then (format t " set msg count to ~d~%" val)
+ (set-saved-chat-messages chat val)))
+ elseif (equal act "set-idle")
+ then (let ((val (compute-integer-value
+ (request-query-value "val" req))))
+ (if* (> val 0)
+ then (format t " set idle timeout ~d mins~%" val)
+ (setq *idle-timeout* (* 60 val))))
+ elseif (equal act "set-redirects")
+ then (set-redirects req chat))
+
+ (if* (equal "yes" (request-query-value "shut" req))
+ then ; shutting down the chat
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:body (:h1 "Shutdown")))))
+ (mp:process-run-function "killer" #'shutdown-chat)
+ (sleep 10)
+ (exit 0)
+ (return-from chatmaster nil))
+
+
+ (multiple-value-bind (total-messages private-messages)
+ (compute-chat-statistics chat)
+
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:html
+ (:head (:title "Chat Master"))
+ (:body
+ (:h2 "Statistics")
+ "There are " (:princ total-messages)
+ " messages in the chat and "
+ (:princ private-messages)
+ " of those are private"
+ :br
+
+ ((:form :method "POST")
+ "Reduce the number of stored messages to "
+ ((:input :type "text" :name "val" :value total-messages
+ :size 6))
+ ((:input :type "hidden" :name "act" :value "set-msg-count"))
+ ((:input :type "submit" :value "do it")))
+ :br
+
+
+
+ (:h2 "Control")
+ ((:form :method "POST")
+ "Idle timeout in minutes: "
+ ((:input :type "text"
+ :name "val"
+ :value (truncate *idle-timeout* 60)
+ :size 4))
+ ((:input :type "hidden"
+ :name "act"
+ :value "set-idle"))
+ ((:input :type "submit"
+ :value "Set It")))
+ :br
+
+ ((:form :method "POST")
+ ((:input :type "checkbox"
+ :name "shut"
+ :value "yes"))
+ "Shut down the chat "
+ ((:input :type "submit"
+ :value "really kill it")))
+ :br
+
+ (show-redirects chat)
+
+ )))))
+
+ )))
+
+
+(defun show-redirects (chat)
+ ;; display redirect dialog
+ (html
+ (:h2 "Redirects")
+
+ ((:form :method "POST")
+ ((:input :type "hidden"
+ :name "act"
+ :value "set-redirects"))
+ ((:table :border 1)
+
+ ; show current ones
+ (dolist (redir (chat-redirects chat))
+ (html
+ :newline
+ (:tr
+ (:td
+ ((:input :type "text" :size 50
+ :name (redir-info-name redir)
+ :value (redirect-info redir)))
+ :br
+ "ipaddr: "
+ ((:input :type "text" :size 50
+ :name (redir-ipaddr-name redir)
+ :value (socket:ipaddr-to-dotted
+ (redirect-ipaddr redir))))
+ ", mask bits: "
+ ((:input :type "text" :size 4
+ :name (redir-maskbits-name redir)
+ :value (redirect-maskbits redir)))
+ :br
+ "to: "
+ ((:input :type "text" :size 50
+ :name (redir-to-name redir)
+ :value (redirect-to redir)))
+
+ :br
+ ((:input :type "checkbox"
+ :if* (redirect-before redir)
+ :checked "checked"
+ :name (redir-before-name redir)
+ :value "xxxx"))
+ "applies only to people not logged on"
+
+
+ :br
+ ((:input :type "radio"
+ :name (redir-state-name redir)
+ :value "active"
+ :if* (redirect-active redir) :checked "checked"))
+ "On, "
+ ((:input :type "radio"
+ :name (redir-state-name redir)
+ :value "disabled"
+ :if* (not (redirect-active redir)) :checked "checked"))
+ "Disabled, "
+ ((:input :type "radio"
+ :name (redir-state-name redir)
+ :value "disrem"))
+
+ "Disable then remove"
+ :br
+ "this rule used " (:princ-safe (redirect-use redir)) " time(s)"
+ :br
+ ((:input :type "checkbox"
+ :name (redir-change-name redir)
+ :value 0))
+ ((:font :color "red") "Make Changes")
+ ))))
+
+ ; show new one
+ (html
+ :newline
+ (:tr
+ (:td
+ "info: " ((:input :type "text" :size 50 :name "newinfo"))
+ :br
+ "ipaddr:" ((:input :type "text" :size 50 :name "newipaddr"))
+ ", mask bits" ((:input :type "text" :size 4 :name "newmask"))
+ :br
+ "redirect to: " ((:input :type "text" :size 50 :name "newto"))
+ :br
+ ((:input :type "checkbox"
+ :name "newredirbefore"
+ :value 0))
+ "applies only to people not logged on"
+ :br
+ ((:input :type "checkbox" :name "newdo" :value "1"))
+ ((:font :color "red") "Add this entry")))))
+
+ ((:input :type "submit" :value "Change Redirects")))))
+
+
+(defun set-redirects (req chat)
+ ;; change the redirect information for this chat
+
+ (let (changed)
+ (dolist (redir (chat-redirects chat))
+ (if* (request-query-value (redir-change-name redir) req)
+ then ; something changed in here
+ (set-redir-info chat
+ redir
+ req
+ (redir-info-name redir)
+ (redir-ipaddr-name redir)
+ (redir-maskbits-name redir)
+ (redir-to-name redir)
+ (redir-before-name redir)
+ (redir-state-name redir))
+ (setq changed t)))
+ (if* (request-query-value "newdo" req)
+ then ; add a new entry
+ (let ((redir (make-redirect)))
+ (setf (redirect-index redir)
+ (incf (redirect-counter chat)))
+ (set-redir-info chat
+ redir
+ req
+ "newinfo"
+ "newipaddr"
+ "newmask"
+ "newto"
+ "newredirbefore"
+ "newxxxxxx")
+ (setf (redirect-active redir) t)
+
+ (setf (chat-redirects chat)
+ (append (chat-redirects chat) (list redir)))
+
+ (setq changed t)
+
+ ))
+
+ (if* changed then (dump-existing-chat *chat-home*))))
+
+
+
+(defun set-redir-info (chat redir req ninfo nipaddr nmask nto nbefore nstate)
+ (setf (redirect-info redir) (request-query-value ninfo req))
+ (let ((ipaddr (or
+ (ignore-errors (socket:lookup-hostname
+ (request-query-value nipaddr req)))
+ 0)))
+ (setf (redirect-ipaddr redir) ipaddr))
+
+ (let ((maskbits (or (compute-integer-value
+ (request-query-value nmask req))
+ 32)))
+ (setf (redirect-maskbits redir) maskbits)
+ (setf (redirect-mask redir)
+ (logand #xffffffff (ash -1 (- 32 maskbits))))
+ )
+
+ (setf (redirect-to redir) (request-query-value nto req))
+ (setf (redirect-before redir) (request-query-value nbefore req))
+
+ (let ((state (request-query-value nstate req)))
+ (if* (equal state "active")
+ then (setf (redirect-active redir) t)
+ elseif (equal state "disabled")
+ then (setf (redirect-active redir) nil)
+ elseif (equal state "disrem")
+ then ; eliminate
+ (setf (chat-redirects chat)
+ (delete redir (chat-redirects chat))))))
+
+
+
+
+
+
+
+
+
+
+;; generate temp names for form objects
+
+(defun redir-info-name (redir)
+ (format nil "~a-info" (redirect-index redir)))
+
+(defun redir-ipaddr-name (redir)
+ (format nil "~a-ipaddr" (redirect-index redir)))
+
+(defun redir-maskbits-name (redir)
+ (format nil "~a-maskbits" (redirect-index redir)))
+
+(defun redir-before-name (redir)
+ (format nil "~a-before" (redirect-index redir)))
+
+(defun redir-to-name (redir)
+ (format nil "~a-to" (redirect-index redir)))
+
+(defun redir-change-name (redir)
+ (format nil "~a-change" (redirect-index redir)))
+
+(defun redir-state-name (redir)
+ (format nil "~a-state" (redirect-index redir)))
+
+
+
+
+
+;; Chat archiver
+;;
+;; The chat archiver stores chat info to files
+
+(let (last-master-controller)
+(defun start-chat-archiver (master-controller)
+ (and t (if* (not (eq master-controller last-master-controller))
+ then ; we've already started the process
+ (setq last-master-controller master-controller)
+ (mp:process-run-function "chat archiver"
+ #'chat-archiver master-controller)))))
+
+(defun chat-archiver (master-controller)
+ (let ((sleep-time 30)
+ (did-work))
+ (loop
+ (if* (not (eq *master-controller* master-controller))
+ then ; chat has been restarted, let this process die
+ (return))
+
+ (format t "Chat archiver awoken~%")
+ (setq did-work nil)
+
+ ; write out the data
+ (dolist (controller (controllers master-controller))
+ (dolist (chat (chats controller))
+ (mp:with-process-lock ((chat-message-lock chat))
+ (format t " arch ~d num ~d~%"
+ (chat-message-archive chat)
+ (chat-message-number chat))
+ (if* (or (< (chat-message-archive chat)
+ (chat-message-number chat))
+ (chat-messages-deleted chat))
+ then ; must do work
+ (archive-chat chat)
+ (setq did-work t)))))
+
+ ; adjust archive time so that we sleep longer when
+ ; the chat is inactive.
+ (if* did-work
+ then (setq sleep-time 30)
+ else (setq sleep-time (min (+ sleep-time 30)
+ (* 30 60) ; 30 minutes
+ )))
+
+ (format t "Chat archiver going to sleep~%")
+ (sleep sleep-time))))
+
+
+
+(defun find-index-of-message (chat number)
+ ;; find index of message 'number' or the first one after that
+ (let ((messages (chat-messages chat))
+ (message-next (chat-message-next chat)))
+ (do ((i (1- message-next) (1- i)))
+ ((< i 0) 0)
+ (let* ((message (svref messages i))
+ (num (message-number message)))
+ (if* (and num
+ (< num number))
+ then (return (1+ i))
+ elseif (eql num number)
+ then (return i))))))
+
+(defun archive-chat (chat)
+ ;; write out new messages for this chat
+ ;; we are inside a process lock for this chat's message lock
+ ;; so we can alter the fields at will
+ (let ((messages (chat-messages chat))
+ (message-next (chat-message-next chat))
+ (message-archive (chat-message-archive chat)))
+
+ ; we have to find the location of the
+ ; message-archive message
+ (if* (> message-next 0)
+ then ; it better be greater than 0 since to be zero
+ ; would be no messages stored
+
+ ; locate the message numbered message-archive
+ (let ((start-to-save
+ (find-index-of-message chat message-archive)))
+
+ (with-open-file (archive-port (archive-filename chat)
+ :direction :output
+ :if-exists :append
+ :if-does-not-exist :create
+ ;:external-foramt :utf-8
+ )
+ (do ((i start-to-save (1+ i)))
+ ((>= i message-next))
+ (if* (eq t (message-to (svref messages i)))
+ then ; a public message, archive it
+ (pprint (svref messages i) archive-port))
+ )
+ (if* (chat-messages-deleted chat)
+ then (pprint `(:delete ,@(chat-messages-deleted chat))
+ archive-port)
+ (setf (chat-messages-deleted chat) nil)))
+
+ (setf (chat-message-archive chat)
+ (1+ (message-number (svref messages (1- message-next)))))))))
+
+(defun archive-filename (chat)
+ (format nil "~a/~a" *chat-home* (chat-filename chat)))
+
+
+
+(defmethod set-style ((style color-style))
+ (setq *top-frame-bgcolor* (color-style-bgcolor style)
+ *top-frame-font-color* (color-style-font-color style)
+ *public-font-color* (color-style-font-color style)
+ *top-frame-vlink-color* (color-style-vlink-color style)
+ *top-frame-link-color* (color-style-link-color style)
+ *top-frame-alink-color* (color-style-alink-color style)))
+
+(if* (not (boundp '*top-frame-bgcolor*))
+ then (set-style *normal-style*))
+
+;; for franz chats uncomment this since some people like this style better
+;(set-style *white-style*)
+;(setq *quick-return-path* "/xyzzy")
+;--------
+
+(defun chat-transcript (uc-string filename)
+ ;; generate a transcript of the chat with the given uc-string
+ ;; to the given filename
+ ;
+ ; find chat
+ (let* ((query-alist (form-urlencoded-to-query uc-string))
+ (u (cdr (assoc "u" query-alist :test #'equalp)))
+ (c (cdr (assoc "c" query-alist :test #'equalp))))
+
+ (let ((chat
+ (dolist (controller (controllers *master-controller*))
+ (if* (equal u (ustring controller))
+ then (return
+ (dolist (chat (chats controller))
+ (if* (equal c (ustring chat))
+ then (return chat))))))))
+ (if* (null chat)
+ then (error "can't find chat with uc-string ~s" uc-string))
+
+ (with-open-file (*html-stream* filename :direction :output
+ :if-exists :supersede
+ ;:external-format :utf-8
+ )
+ (html
+ (:head
+ (:title "Transcript of "
+ (:princ-safe (chat-name chat))))
+ (:body
+ (:h1 "Transcript of "
+ (:princ-safe (chat-name chat)))
+ (show-chat-info chat (chat-message-next chat) nil nil nil nil)))))))
+
+
+
+;; viewer tracking
+
+(defun track-viewer (chat user req)
+ ;; note that this user/req has read the postings for this chat
+ (let* ((time (get-universal-time))
+ (viewers (chat-viewers chat))
+ (ipaddr (socket:remote-host (request-socket req)))
+ (empty-ent))
+
+
+ (mp::with-process-lock ((viewers-lock viewers))
+
+ ;; scan list of viewers.
+ ;; set emptyent to the first viewent with a null time, thus meaning
+ ;; it's a free entry
+ ;; if an entry already exists for this user or ipaddr use it
+ (dolist (viewent (viewers-list viewers)
+ ; not there yet
+ (if* empty-ent
+ then ; replace old one
+ (setf (viewent-time empty-ent) time
+ (viewent-user empty-ent) user
+ (viewent-ipaddr empty-ent) ipaddr
+ (viewent-hostname empty-ent) nil)
+ else
+ (push (setq empty-ent
+ (make-viewent :time time
+ :user user
+ :ipaddr ipaddr))
+ (viewers-list viewers))
+ ))
+ (if* user
+ then (if* (eq user (viewent-user viewent))
+ then ; update this one
+ (setf (viewent-time viewent) time)
+ (if* (not (eql ipaddr (viewent-ipaddr viewent)))
+ then ; hmm, changed ipaddr
+ (setf (viewent-ipaddr viewent) ipaddr
+ (viewent-hostname viewent) nil))
+ (return))
+ else ; ipaddr test
+ (if* (and (null (viewent-user viewent))
+ (eql ipaddr (viewent-ipaddr viewent)))
+ then (setf (viewent-time viewent) time)
+ (return)))
+ (if* (null (viewent-time viewent))
+ then (if* (null empty-ent)
+ then (setf empty-ent viewent))
+ elseif (> (- time (viewent-time viewent)) *max-active-time*)
+ then ; this one is too old
+ (setf (viewent-time viewent) nil)
+ (if* (null empty-ent)
+ then (setq empty-ent viewent)))))))
+
+(defun chatviewers (req ent)
+ ;; display page of chat viewers (except us)
+ (let* ((chat (chat-from-req req))
+ (user (user-from-req req))
+ (time (get-universal-time))
+ (is-owner
+ (equal (and chat (secret-key chat))
+ (request-query-value "s" req)))
+ (qstring)
+ (viewers)
+ (idletime)
+ )
+ (if* (null chat)
+ then (return-from chatviewers (ancient-link-error req ent)))
+
+ (if* (and user (zerop (user-time user)))
+ then (setf (user-time user) (get-universal-time)))
+
+ (if* (> (setq idletime (- (get-universal-time) (user-time user)))
+ (+ 10 *idle-timeout*))
+ then (do-idle-timedout req ent nil)
+ (return-from chatviewers))
+ (setq qstring
+ (add-secret req
+ (add-user req
+ (chat-query-string chat))))
+ (setq viewers (chat-viewers chat))
+
+ (setq idletime (truncate idletime 60)) ; cvt to minutes
+
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:html
+ ((:meta :http-equiv "Refresh"
+ :content
+ (format nil "30;url=chatviewers?~a" qstring)))
+ (:body
+
+ ((:font :size 2)
+ ((:a :href (concatenate 'string
+ "chatenter?pp=*&" qstring)
+ :target "chatenter"
+ )
+ "Send to All")
+ :hr
+ :newline
+ (:pre
+ (mp::with-process-lock ((viewers-lock viewers))
+ (dolist (viewent (viewers-list viewers))
+ (let* ((vtime (viewent-time viewent))
+ (vuser (viewent-user viewent))
+ (alive-time (if* vtime then (- time vtime)))
+ (idle-time (if* vuser
+ then (- time (or (user-time vuser) 0))
+ else 0)))
+
+
+ (if* (and alive-time
+ (> alive-time *max-active-time*))
+ then (setq vtime nil)
+ (setf (viewent-time viewent) nil))
+
+ ; cvt to minutes
+ (setq idle-time (min 120 (truncate idle-time 60)))
+
+ (if* vtime
+ then ; fill in the hostname if it's not there yet
+ #+(and allegro (version>= 6 0))
+ (if* (null (viewent-hostname viewent))
+ then (setf (viewent-hostname
+ viewent)
+ (socket::dns-query
+ (viewent-ipaddr viewent)
+ :type :ptr
+ :repeat 1
+ :timeout 0)))
+
+ (if* (not (eq vuser user))
+ then ; list this one
+ (if* vuser
+ then
+ (html
+ ; link to add a user
+ ((:a :href
+ (format nil
+ "chatenter?ppp=~a&~a"
+ (user-pstring vuser)
+ qstring)
+ :target "chatenter")
+ "(+)")
+
+ " "
+
+
+ ; link to create a private message
+ ((:a :href
+ (format nil
+ "chatenter?pp=~a&~a"
+ (user-pstring vuser)
+ qstring)
+ :target "chatenter"
+ )
+ (:princ-safe
+ (user-handle vuser))))
+
+ else ; ip address
+
+ (html
+ (:princ
+ (or (viewent-hostname viewent)
+ (socket:ipaddr-to-dotted
+ (viewent-ipaddr viewent))))))
+ (html
+ " ("
+ (:princ (- time vtime))
+ "s)")
+
+ (if* (> idle-time 2)
+ then (html
+ " [idle: "
+ (:princ idle-time)
+ "m] "))
+
+ (if* (or *show-machine-name-to-all*
+ is-owner)
+ then ; name then ip address
+ (html
+ " @"
+ (:princ-safe
+ (or (viewent-hostname viewent)
+ (socket:ipaddr-to-dotted
+ (viewent-ipaddr viewent))))))
+ (html :newline)))))))))))))))
+
+
+
+
+(defun chattranscript (req ent)
+ (let* ((chat (or (chat-from-req req)
+ (return-from chattranscript (ancient-link-error req ent))))
+ (title (format nil "full transcript of chat ~a as of ~a"
+ (chat-name chat) (compute-chat-date
+ (get-universal-time)))))
+
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:html
+ (:title (:princ-safe title))
+ (:body
+ (:h1 (:princ-safe title))
+ (let ((*top-frame-bgcolor* "#xffffff") ; white
+ (*public-font-color* "#x000000") ; black
+ )
+ (show-chat-info chat (chat-message-next chat) nil
+ "bogushandle" nil nil))
+ )))))))
+
+(defun redir-check (req ent chat before)
+ ;; check if this request should be redirected
+ ;; before is true if we are in the before login state
+ (let ((redirects (chat-redirects chat)))
+ (if* redirects
+ then (let ((ipaddr (socket:remote-host (request-socket req))))
+ (dolist (redir redirects)
+ (if* (and (redirect-active redir)
+ (eq before (redirect-before redir))
+ (eql (logand (redirect-ipaddr redir)
+ (redirect-mask redir))
+ (logand ipaddr
+ (redirect-mask redir))))
+ then ; a match!
+ (incf (redirect-use redir))
+ (with-http-response (req ent
+ :response
+ *response-moved-permanently*)
+ (setf (reply-header-slot-value req :location)
+ (redirect-to redir))
+ (with-http-body (req ent)
+ (html "redirect")))
+ (return t)))))))
+
+
+
+
+
+
+
+
+
+
+
+
+;;;;; chat test code
+;;
+;;
+
+(defun block-test (testers &rest args)
+ (dotimes (i testers)
+ (let ((name (format nil "tester-~d" i))
+ (delay (max 1 (random 10))))
+
+ (mp:process-run-function name
+ #'(lambda ()
+ (apply #'test-chat
+ :name name
+ :delay delay
+ args))))))
+
+
+
+
+
+
+
+
+(defun test-chat (&key uc-string
+ (count 100)
+ (reads 5)
+ (delay 2)
+ (name "dummy1")
+ (machine "localhost")
+ (port 8000)
+ (protocol :http/1.1))
+ (let ((reader-url
+ (format nil "http://~a:~d/chattop?~a&~a"
+ machine
+ port
+ uc-string
+ (query-to-form-urlencoded
+ `(("count" . 10)
+ ("secs" . 5)))))
+ (post-url
+ (format nil "http://~a:~d/chatenter?~a"
+ machine
+ port
+ uc-string)))
+
+ (dotimes (i count)
+ ; post first
+ (let ((message (format nil "message ~d from ~a~%" i name)))
+ (do-http-request post-url
+ :protocol protocol
+ :method :post
+ :query `(("secs" . 5) ; not used
+ ("handle" . ,name)
+ ("body" . ,message)))
+ (sleep delay)
+ (dotimes (i reads)
+ ; read it now
+ (do-http-request reader-url
+ :method :get
+ :protocol protocol)
+ (sleep delay))))))
+
+
+;;; fix up old chats
+
+(defun fixupchat ()
+ (setf (users *master-controller*) (nreverse (users *master-controller*)))
+ (dolist (user (users *master-controller*))
+ (setf (user-ustring user) (make-unique-string))
+ (setf (user-pstring user) (make-unique-string)))
+ (dump-existing-chat *chat-home*)
+ )
+
+
+
Added: vendor/portableaserve/aserve/examples/examples.cl
===================================================================
--- vendor/portableaserve/aserve/examples/examples.cl 2006-02-18 09:34:15 UTC (rev 1845)
+++ vendor/portableaserve/aserve/examples/examples.cl 2006-02-18 10:02:10 UTC (rev 1846)
@@ -0,0 +1,1240 @@
+;; -*- mode: common-lisp; package: net.aserve.examples -*-
+;;
+;; examples.cl
+;;
+;; copyright (c) 1986-2000 Franz Inc, Berkeley, CA
+;;
+;; This code is free software; you can redistribute it and/or
+;; modify it under the terms of the version 2.1 of
+;; the GNU Lesser General Public License as published by
+;; the Free Software Foundation;
+;;
+;; This code is distributed in the hope that it will be useful,
+;; but without any warranty; without even the implied warranty of
+;; merchantability or fitness for a particular purpose. See the GNU
+;; Lesser General Public License for more details.
+;;
+;; Version 2.1 of the GNU Lesser General Public License is in the file
+;; license-lgpl.txt that was distributed with this file.
+;; If it is not present, you can access it from
+;; http://www.gnu.org/copyleft/lesser.txt (until superseded by a newer
+;; version) or write to the Free Software Foundation, Inc., 59 Temple Place,
+;; Suite 330, Boston, MA 02111-1307 USA
+;;
+;;
+;; $Id: examples.cl,v 1.7 2004/01/27 10:53:44 rudi Exp $
+
+;; Description:
+;; Allegro iServe examples
+
+;;- This code in this file obeys the Lisp Coding Standard found in
+;;- http://www.franz.com/~jkf/coding_standards.html
+;;-
+
+
+
+;; examples of web pages
+(defpackage #:net.aserve.examples ;; aserve example
+ (:use #:common-lisp #:acl-compat.excl #:net.html.generator #:net.aserve))
+
+(in-package #:net.aserve.examples)
+
+;; don't flush all publishing done so far. since we have other
+;; example files this is bad news.
+; (unpublish :all t)
+
+(defparameter *example-pathname* *load-pathname*) ; where this file is
+(defmacro example-file (name)
+ ;; create an absolute address for this file we'll load
+ `(merge-pathnames ,name *example-pathname*))
+
+(defvar *hit-counter* 0)
+
+
+(publish :path "/"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ ;(print (net.aserve::compute-request-headers req))
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:head (:title "Welcome to Portable AllegroServe on "
+ (:princ (lisp-implementation-type))))
+ (:body (:center ((:img :src "aservelogo.gif")))
+ (:h1 "Welcome to Portable AllegroServe on "
+ (:princ (lisp-implementation-type)))
+ (:p "These links show off some of AllegroServe's capabilities. ")
+ (:i "This server's host name is "
+ (:princ-safe (header-slot-value req :host)))
+ #+unix
+ (:i ", the process id is "
+ (:princ (net.aserve::getpid)))
+ :br
+ (:princ (incf *hit-counter*)) " hits"
+ :p
+ (:b "Sample pages") :br
+ #+allegro
+ ((:a :href "gc") "Garbage Collector Stats") :br
+ ((:a :href "apropos") "Apropos")
+ :br
+ ((:a :href "pic") "Sample jpeg") :br
+ ((:a :href "pic-redirect") "Redirect to previous picture") :br
+ ((:a :href "pic-gen") "generated jpeg") "- hit reload to switch images" :br
+ ((:a :href "pic-multi") "test of publish-multi") " - click more than once on this link" :br
+ ((:a :href "cookietest") "test cookies") :br
+ ((:a :href "secret") "Test manual authorization")
+ " (name: " (:b "foo") ", password: " (:b "bar") ")"
+ :br
+ ((:a :href "secret-auth") "Test automatic authorization")
+ " (name: "
+ (:b "foo2")
+ " password: "
+ (:b "bar2") ")"
+ :br
+ ((:a :href "local-secret") "Test source based authorization") " This will only work if you can use "
+ "http:://localhost ... to reach this page"
+ :br
+ ((:a :href "local-secret-auth")
+ "Like the preceding but uses authorizer objects")
+ :br
+ ((:a :href "timeout") "Test timeout")
+ " this will take a while to time out."
+ :br
+ ((:a :href "getfile-old") "Client to server file transfer") " - the old way"
+ :br
+ ((:a :href "getfile") "Client to server file transfer") " - the new way, with 1,000,000 byte file transfer limit"
+ :br
+ ((:a :href "missing-link") "Missing Link")
+ " should get an error when clicked"
+
+ :br
+ #+unix
+ (html
+ ((:a :href "long-slow") "long, slow, cpu-bound")
+ " action to demonstrate how AllegroServe "
+ "in multiple Unix process mode can be responsive"
+ " even if one AllegroServe process is wedged."
+ " You probably do "
+ (:b "not")
+ " want to click on this link if you are running"
+ " AllegroServe is its normal single Unix process"
+ " mode.")
+
+
+ :br
+ ;; run only in an international lisp.
+ ;; test at runtime since we may switch back
+ ;; and forth between international and 8 bit
+ ;; modes
+ (if* (member :ics *features* :test #'eq)
+ then (html
+ :br
+ ((:a :href "ichars")
+ "International Character Display")
+
+ :br
+ ((:a :href "icharcount")
+ "(International) Character Counter")
+ :br
+ ;; published in puzzle.cl
+ ((:a :href "wordpuzzle")
+ "Word Puzzle")
+ :br
+ ;; published in urian.cl
+ ((:a :href "urian")
+ "International Web Page Character Finder")
+ :br
+ ;; published in locale.cl
+ ((:a :href "locale")
+ "Locale Demo")
+ :br
+ ))
+
+ #+(and unix (or (and allegro (version>= 6 1)) mcl))
+ (html
+ "cgi tests: "
+ ((:a :href "cgi0") "show environment")
+ ", "
+ ((:a :href "cgi1") "handle unix-style headers")
+ ", "
+ ((:a :href "cgi2") "redirect")
+ ", "
+ ((:a :href "cgi3") "set status to unauthorized request"))
+ :hr
+ ((:img :src "aservepowered.gif")) " <-- feel free to use this image on your AllegroServe-based web site"
+ ))))))
+
+
+
+;; a very simple page. This is so simple it doesn't put out the required
+;; tags (like ) yet I suspect that most browsers will display it
+;; correctly regardless.
+(publish :path "/hello"
+ :content-type "text/html"
+ :function #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html "Hello World!")))))
+
+;; this is the "/hello" example above modified to put out the correct
+;; html tags around the page.
+(publish :path "/hello2"
+ :content-type "text/html"
+ :function #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:html
+ (:body "Hello World!")))))))
+
+;; display the current gc statistics.
+#+allegro
+(publish :path "/gc"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (macrolet ((build-gsgc-table ()
+ `(html
+ ,@(mapcar
+ #'(lambda (kind)
+ `(:tr (:td (:princ ,kind))
+ (:td (:princ-safe
+ (sys:gsgc-parameter ,kind)))))
+ '(:generation-spread
+ :current-generation
+ :tenure-limit
+ :free-bytes-new-other
+ :free-percent-new
+ :free-bytes-new-pages
+ :expansion-free-percent-new
+ :expansion-free-percent-old
+ :quantum
+ )))))
+
+
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head (:title "Allegro gc parameters"))
+ (:body
+ ((:table :bgcolor "silver" :bordercolor "blue"
+ :border "3" :cellpadding "3"
+ :cellspacing "3")
+ (:tr (:td (:b "gsgc parameter")) (:td (:b "Value")))
+ (build-gsgc-table)))))))))
+
+
+
+;; display a picture from a file.
+(publish-file :path "/pic" :file (example-file "prfile9.jpg")
+ :content-type "image/jpeg")
+
+
+
+(publish-file :path "/aservelogo.gif" :file (example-file "aservelogo.gif")
+ :content-type "image/gif")
+
+(publish-file :path "/aservepowered.gif" :file (example-file "aservepowered.gif")
+ :content-type "image/gif")
+
+;; this is a demonstration of how you can return a jpeg
+;; image that was created on the fly (rather than read from
+;; a file via publish-file).
+;; We don't want to actually create the image here, so we
+;; cheat and read it from a file, but it shows that you can
+;; send any stream of bytes and they will be given the correct
+;; mime type.
+;;
+(publish :path "/pic-gen"
+ :content-type "image/jpeg"
+ :function
+ (let ((selector 0)) ; chose one of two pictures
+ #'(lambda (req ent)
+ (with-http-response (req ent :format :binary)
+ (with-http-body (req ent)
+ ; here is where you would generate the picture.
+ ; we're just reading it from a file in this example
+ (let ((stream (request-reply-stream req)))
+ (with-open-file (p (nth selector
+ `(,(example-file "prfile9.jpg")
+ ,(example-file "fresh.jpg")))
+ :element-type '(unsigned-byte 8))
+
+ (setq selector (mod (1+ selector) 2))
+
+ (loop
+ (let ((val (read-byte p nil nil)))
+ (if* (null val)
+ then ;eof
+ (return))
+ (write-byte val stream)
+ )))))))))
+
+
+
+;; do a redirect to the picture
+
+(publish :path "/pic-redirect"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (with-http-response (req ent
+ :response *response-moved-permanently*)
+ (setf (reply-header-slot-value req :location) "pic")
+ (with-http-body (req ent)
+ ;; this is optional and most likely unnecessary since most
+ ;; browsers understand the redirect response
+ (html
+ (:html
+ (:head (:title "Object Moved"))
+ (:body
+ (:h1 "Object Moved")
+ "The picture you're looking for is now at "
+ ((:a :href "pic") "This location"))))))))
+
+
+
+;; this publish-multi example is simple but really doesn't show
+;; the full power of publish-multi.
+;; It doesn't show that we can include the contents of files
+;; The :function case doesn't make use of the old cached value to
+;; decide if it wants to return the old value or create a new one.
+(publish-multi :path "/pic-multi"
+ :content-type "text/html"
+ :items (list
+ '(:string "The first line is constant ")
+ (let (last-clicked)
+ #'(lambda (req ent old-time cached-value)
+ (declare (ignore req ent old-time cached-value))
+ (if* (null last-clicked)
+ then (setq last-clicked
+ (get-universal-time))
+ "this is your first click "
+ else (let* ((new (get-universal-time))
+ (diff (- new last-clicked)))
+ (setq last-clicked new)
+ (format nil "~d seconds since the last click " diff)))))
+ '(:string "The last line is constant")))
+
+
+
+
+
+
+
+
+
+;;
+;; here's a form using the 'post' method
+;;
+(publish :path "/tform"
+ :content-type "text/html"
+ :function
+ (let ((name "unknown"))
+ #'(lambda (req ent)
+ (let ((body (get-request-body req)))
+ (format t "got body ~s~%" body)
+ (let ((gotname (assoc "username"
+ (form-urlencoded-to-query body)
+ :test #'equal)))
+ (if* gotname
+ then (setq name (cdr gotname)))))
+
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head (:title "test form"))
+ (:body "Hello " (:princ-safe name) ", "
+ "Enter your name: "
+ ((:form :action "/tform"
+ :method "post")
+ ((:input :type "text"
+ :maxlength 10
+ :size 10
+ :name "username"))))))))))
+
+
+
+
+;; example of a form that uses that 'get' method
+;;
+(publish
+ :path "/apropos"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (let ((lookup (assoc "symbol" (request-query req) :test #'equal)))
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head (:title "Allegro Apropos"))
+ ((:body :background "aserveweb/fresh.jpg")
+ "New Apropos of "
+ ((:form :action "apropos"
+ :method "get")
+ ((:input :type "text"
+ :maxlength 40
+ :size 20
+ :name "symbol")))
+ #+allegro
+ " The apropos function in ACL is case sensitive."
+ :p
+
+ (if* lookup
+ then (html :hr (:b "Apropos") " of "
+ (:princ-safe (cdr lookup))
+ :br
+ :br)
+ (let ((ans (apropos-list (cdr lookup))))
+ (if* (null ans)
+ then (html "No Match Found")
+ else (macrolet ((my-td (str)
+ `(html ((:td
+ :bgcolor "blue")
+ ((:font :color "white"
+ :size "+1")
+ (:b ,str))))))
+
+ (html ((:table
+ :bgcolor "silver"
+ :bordercolor "blue"
+ :border 3
+ :cellpadding 3
+ )
+
+ (:tr
+ (my-td "Symbol")
+ (my-td "boundp")
+ (my-td "fboundp"))
+
+
+ (dolist (val ans)
+ (html (:tr
+ (:td (:prin1-safe val))
+ (:td (:prin1 (and (boundp val) t)))
+ (:td (:prin1 (and (fboundp val) t))))
+ :newline)))))))
+ else (html "Enter name and type enter")))
+ :newline))))))
+
+
+;; a preloaded picture file
+(publish-file :path "/aserveweb/fresh.jpg"
+ :file (example-file "fresh.jpg")
+ :content-type "image/jpeg"
+ :preload t)
+
+;; a preloaded text file
+(publish-file :path "/foo"
+ :file (example-file "foo.txt")
+ :content-type "text/plain"
+ :preload t)
+
+(publish-file :path "/foo.txt"
+ :file (example-file "foo.txt")
+ :content-type "text/plain"
+ :preload nil)
+
+
+;; some entries for benchmarking
+(publish-file :path "/file2000"
+ :file (example-file "file2000.txt")
+ :content-type "text/plain"
+ :preload nil)
+
+(publish-file :path "/file2000-preload"
+ :file (example-file "file2000.txt")
+ :content-type "text/plain"
+ :preload t)
+
+(publish :path "/dynamic-page"
+ :content-type "text/plain"
+ :function #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html "This is a dynamic page")))))
+
+;; an example which causes the web browser to put up the
+;; name/password box and if you enter the name "foo" and password "bar"
+;; then you get access to the secret info.
+(publish :path "/secret"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (multiple-value-bind (name password) (get-basic-authorization req)
+ (if* (and (equal name "foo") (equal password "bar"))
+ then (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head (:title "Secret page"))
+ (:body "You made it to the secret page"))))
+ else
+ (with-http-response (req ent :response
+ *response-unauthorized*)
+ (set-basic-authorization req
+ "secretserver")
+ (with-http-body (req ent)
+ (html (:h1 "You Failed")
+ "You failed to enter the correct name/password")
+ ))))))
+
+
+(publish :path "/local-secret"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (let ((net-address (ash (socket:remote-host
+ (request-socket req))
+ -24)))
+ (if* (equal net-address 127)
+ then (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head (:title "Secret page"))
+ (:body (:b "Congratulations. ")
+ "You are on the local network"))))
+ else
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:html (:head (:title "Unauthorized"))
+ (:body
+ "You cannot access this page "
+ "from your location")))))))))
+
+
+(publish :path "/local-secret-auth"
+ :content-type "text/html"
+ :authorizer (make-instance 'location-authorizer
+ :patterns '((:accept "127.0" 8)
+ :deny))
+ :function
+ #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head (:title "Secret page"))
+ (:body (:b "Congratulations. ")
+ "You made it to the secret page"))))))
+
+;; these two urls show how to transfer a user-selected file from
+;; the client browser to the server.
+;;
+;; We use two urls (/getfile to put up the form and /getfile-post to
+;; handle the post action of the form). We could have done it all
+;; with one url but since there's a lot of code it helps in the
+;; presentation to separate the two.
+;;
+(publish :path "/getfile-old"
+ :content-type "text/html; charset=utf-8"
+ :function #'(lambda (req ent) (getfile-function
+ req ent "/getfile-got-old")))
+
+(publish :path "/getfile"
+ :content-type "text/html; charset=utf-8"
+ :function #'(lambda (req ent) (getfile-function
+ req ent "/getfile-got")))
+
+
+(defun getfile-function (req ent posturl)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head "get file")
+ (:body
+ ((:form :enctype "multipart/form-data"
+ :method "post"
+ :action posturl)
+ "Let me know what file to grab"
+ :br
+ ((:input :type "file"
+ :name "thefile"
+ :value "*.txt"))
+ :br
+ ((:input :type "text" :name "textthing"))
+ "Enter some text"
+ :br
+ ((:input :type "checkbox" :name "checkone"))
+ "check box one"
+ :br
+ ((:input :type "checkbox" :name "checktwo"))
+ "check box two"
+ :br
+ ((:input :type "submit"))))))))
+
+
+(publish :path "/secret-auth"
+ :content-type "text/html"
+ :authorizer (make-instance 'password-authorizer
+ :allowed '(("foo2" . "bar2")
+ ("foo3" . "bar3")
+ )
+ :realm "SecretAuth")
+ :function
+ #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head (:title "Secret page"))
+ (:body "You made it to the secret page"))))))
+
+
+
+;;
+;; this demonstrates the use of the low level multipart access functions.
+;; In this code we parse the result of get-multipart-header ourselves
+;; and we use get-multipart-sequence.
+;; In the example that follows (associate with path "/getfile-got")
+;; we show now to use the higher level functions to retrive multipart
+;; data
+(publish :path "/getfile-got-old"
+ :content-type "text/html; charset=utf-8"
+ :function
+ #'(lambda (req ent)
+
+ (with-http-response (req ent)
+ (let ((h nil)
+ (files-written)
+ (text-strings)
+ )
+ (loop
+ ; get headers for the next item
+ (if* (null (setq h (get-multipart-header req)))
+ then ; no more items
+ (return))
+ ; we can get the filename from the header if
+ ; it was an item. If there is
+ ; no filename, we just create one.
+ (pprint h)
+ (pprint (multiple-value-list (parse-multipart-header h)))
+ (let ((cd (assoc :content-disposition h :test #'eq))
+ (filename)
+ (sep))
+ (if* (and cd (consp (cadr cd)))
+ then (setq filename (cdr (assoc "filename"
+ (cddr (cadr cd))
+ :test #'equalp)))
+ (if* filename
+ then ;; locate the part of the filename
+ ;; after the last directory separator.
+ ;; the common lisp pathname functions are
+ ;; no help since the filename syntax
+ ;; may be foreign to the OS on which
+ ;; the server is running.
+ (setq sep
+ (max (or (position #\/ filename
+ :from-end t) -1)
+ (or (position #\\ filename
+ :from-end t) -1)))
+ (setq filename
+ (subseq filename (1+ sep)
+ (length filename)))))
+ (if* (and filename (not (equal filename "")))
+ then (push filename files-written)
+ (with-open-file (pp filename :direction :output
+ :if-exists :supersede
+ :element-type '(unsigned-byte 8))
+ (format t "writing file ~s~%" filename)
+ (let ((buffer (make-array 4096
+ :element-type
+ '(unsigned-byte 8))))
+
+ (loop (let ((count (get-multipart-sequence
+ req
+ buffer)))
+ (if* (null count) then (return))
+ (write-sequence buffer pp :end count)))))
+ elseif (null filename)
+ then ; no filename, just grab as a text
+ ; string
+ (let ((buffer (make-string 1024)))
+ (loop
+ (let ((count (get-multipart-sequence
+ req buffer
+ :external-format :utf8-base)))
+ (if* count
+ then (push (subseq buffer 0 count)
+ text-strings)
+ else (return))))))))
+
+
+
+ ;; now send back a response for the browser
+
+ (with-http-body (req ent
+ :external-format :utf8-base)
+ (html (:html (:head (:title "form example"))
+ (:body "-- processed the form, files written --"
+ (dolist (file (nreverse files-written))
+ (html :br "file: "
+ (:b (:prin1-safe file))))
+ :br
+ "-- Non-file items Returned: -- " :br
+ (dolist (ts (reverse text-strings))
+ (html (:princ-safe ts) :br))))))))))
+
+
+;;
+;; this retrieves data from a multipart form using the high level
+;; functions. You can compare this code to that above to see which
+;; method you prefer
+;;
+(publish :path "/getfile-got"
+ :content-type "text/html; charset=utf-8"
+ :function
+ #'(lambda (req ent)
+ (with-http-response (req ent)
+ (let ((files-written)
+ (text-strings)
+ (overlimit)
+ )
+ (loop
+ (multiple-value-bind (kind name filename content-type)
+ (parse-multipart-header
+ (get-multipart-header req))
+
+ (case kind
+ (:eof (return)) ; no more to read
+ (:data
+ (push (cons name (get-all-multipart-data req))
+ text-strings))
+ (:file
+ (let ((contents (get-all-multipart-data
+ req
+ :type :binary
+ :limit 1000000 ; abitrary limit
+ )))
+ ; find the tail of the filename, can't use
+ ; lisp pathname code since the filename syntax
+ ; may not correspond to this lisp's native os
+ (let ((sep (max (or (position #\/ filename
+ :from-end t) -1)
+ (or (position #\\ filename
+ :from-end t) -1))))
+ (if* sep
+ then (setq filename
+ (subseq filename (1+ sep)))))
+ (if* (eq contents :limit)
+ then ; tried to give us too much
+ (setq overlimit t)
+ elseif (equal filename "") ; no file given
+ thenret ; ignore
+ else
+ (with-open-file (p filename
+ :direction :output
+ :if-exists :supersede
+ :element-type '(unsigned-byte 8))
+ (format
+ t "writing file ~s, content-type ~s~%"
+ filename content-type)
+ (push filename files-written)
+ (write-sequence contents p)))))
+ (t ; all else ignore but read to next header
+ (get-all-multipart-data req :limit 1000)))))
+
+
+
+
+ ;; now send back a response for the browser
+
+ (with-http-body (req ent
+ :external-format :utf8-base)
+ (html (:html (:head (:title "form example"))
+ (:body "-- processed the form, files written --"
+ (dolist (file (nreverse files-written))
+ (html :br "file: "
+ (:b (:prin1-safe file))))
+ (if* overlimit
+ then (html :br
+ "File given was over our "
+ "limit in the size we "
+ "will accept"))
+ :br
+ "-- Non-file items Returned: -- " :br
+ (dolist (ts (reverse text-strings))
+ (html
+ "item name: " (:princ-safe (car ts))
+ ", item value: "
+ (:princ-safe (cdr ts))
+ :br))))))))))
+
+
+
+(publish :path "/cookietest"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (with-http-response (req ent)
+ (set-cookie-header req
+ :name "froba"
+ :value "vala"
+ :path "/"
+ :expires :never)
+ (set-cookie-header req
+ :name "frob2"
+ :value "val2"
+ :path "/"
+ :expires :never)
+ (set-cookie-header req
+ :name "frob3-loooooooooooooong"
+ :value "val3-loooooooooooooong"
+ :path "/"
+ :expires :never)
+ (set-cookie-header req
+ :name "the time"
+ :value (net.aserve::universal-time-to-date
+ (get-universal-time))
+ :path "/cookieverify"
+ :expires (+ (get-universal-time)
+ (* 20 60) ; 20 mins
+ )
+ )
+
+ (with-http-body (req ent)
+ (html (:head (:title "Cookie Test"))
+ (:body "you should have a cookie now."
+ " Go "
+ ((:a :href "cookieverify") "here")
+ " to see if they were saved"))))))
+
+(publish :path "/cookieverify"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (let ((cookie-info (get-cookie-values req)))
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:head (:title "Cookie results"))
+ (:body
+ "The following cookies were returned: "
+ (:prin1-safe cookie-info))))))))
+
+
+
+(publish :path "/timeout"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ ;; do nothing interesting so that the timeout will
+ ;; occur
+ (with-http-response (req ent :timeout 15)
+ (loop (sleep 5)))))
+
+
+
+(publish :path "/long-slow"
+ :content-type "text/plain"
+ :function
+ #'(lambda (req ent)
+ ;; chew up cpu time in a look that blocks
+ ;; the scheduler from running so this aserve
+ ;; won't accept any more connections and we can
+ ;; demo the multiple process version
+ ; takes 50 secs on a 1.2ghz Athlon
+ (locally (declare (optimize (speed 3) (safety 0)))
+ (dotimes (aa 500)
+ (declare (fixnum aa))
+ (dotimes (j 300)
+ (declare (fixnum j))
+ (dotimes (i 10000)
+ (declare (fixnum i))
+ (let ((k (+ i j)))
+ (declare (fixnum k))
+ (setf k (- i j))
+ (setf k (+ i j k))
+ (setf k (- i j k)))))))
+
+
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html "done")))))
+
+
+
+;; cgi publishing, we publish a shell script that only works
+;; on Unix shells:
+#+unix
+(publish :path "/cgi0" :function
+ #'(lambda (req ent)
+ (net.aserve::run-cgi-program req ent
+ "aserve/examples/cgitest.sh"
+ :env '(("HTTP_CONNECTION"
+ . "hack replaced value")
+ ("NewHead" . "NewVal")))))
+
+#+unix
+(publish :path "/cgi1" :function
+ #'(lambda (req ent)
+ (net.aserve::run-cgi-program req ent "aserve/examples/cgitest.sh 1")))
+
+#+unix
+(publish :path "/cgi2" :function
+ #'(lambda (req ent)
+ (net.aserve::run-cgi-program req ent "aserve/examples/cgitest.sh 2")))
+
+#+unix
+(publish :path "/cgi3" :function
+ #'(lambda (req ent)
+ (net.aserve::run-cgi-program req ent "aserve/examples/cgitest.sh 3")))
+
+
+;;;;;; directory publishing. These will only work on a particular
+;; set of machines so you'll have to modify them to point to an
+;; existing tree of pages on your machine if you want to see this work.
+
+;; the franz home page
+#+ignore (publish-directory :prefix "/"
+ :destination "/net/tanya/home/httpd/html/"
+ )
+
+#+ignore
+(publish-directory :prefix "/int"
+ :destination "/net/tanya/www/internal/htdocs/")
+
+
+
+
+;; a separate world:
+
+(defparameter *server2* (make-instance 'wserver))
+
+(publish-directory :server *server2*
+ :prefix "/"
+ :destination "/home/httpd/html/")
+
+;;
+;; International Characters
+;;
+
+(publish
+ :path "/icharcount"
+ :content-type "text/html; charset=utf-8"
+ :function
+
+ #-(and allegro ics (version>= 6 0))
+ #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (princ #.(format nil "~
+This page available only with International Allegro CL post 6.0 beta")
+ *html-stream*))))
+
+ #+(and allegro ics (version>= 6 0))
+ #'(lambda (req ent)
+ (let* ((body (get-request-body req))
+ (text (if* body
+ then (cdr (assoc "quotation"
+ (form-urlencoded-to-query
+ body
+ :external-format :utf8-base)
+ :test #'equal)))))
+
+ (with-http-response (req ent)
+ (with-http-body (req ent
+ :external-format :utf8-base)
+ (if* text
+ then ;; got the quotation, analyze it
+ (let ((results (analyze-text text)))
+ (html (:html (:head
+ (:title "Character Counts"))
+ (:body
+ (html (:pre (:princ-safe text)))
+ (:p "Quote by Character Names:")
+ (:table
+ (dotimes (i (length text))
+ (html (:tr
+ (:td (:princ (schar text i)))
+ (:td (:prin1 (schar text i)))))))
+ (:p "Sorted by occurrence:")
+ ((:table :border 1)
+ (dolist (r results)
+ (html (:tr
+ (:td
+ (:princ
+ (format nil "u+~4,'0x"
+ (char-code (car r)))))
+ (:td (:princ (car r)))
+ (:td (:prin1 (car r)))
+ (:td (:princ (cdr r)))))))))))
+ else ;; ask for quotation
+ (html (:html
+ (:head (:title "Character Counter"))
+ (:body
+ ((:form :action "icharcount"
+ :method "POST")
+ (:h1 "AllegroServe Demo")
+ (:p #.(format nil "~
+Below are links containing international character samples you can use to copy
+and paste into the following form.
+Note that even characters that don't display (due to missing fonts) can still
+be copied and pasted into the form below."))
+ (:ul (:li ((:a href #.(format nil "~
+http://www.columbia.edu/kermit/utf8.html")
+ target "_blank")
+ "UTF-8 Sampler"))
+ (:li ((:a href #.(format nil "~
+http://www.trigeminal.com/samples/provincial.html")
+ target "_blank")
+ #.(format nil "~
+The \"anyone can be provincial!\" page"))))
+ "Enter your favorite quote:"
+ :br
+ ((:textarea :name "quotation" :rows 15
+ :cols 50))
+ :br
+ ((:input :type "submit"
+ :value "count it"))))))))))))
+
+(defun analyze-text (text)
+ (let ((char-ht (make-hash-table))
+ (results nil))
+ (dotimes (i (length text))
+ (let ((ch (schar text i)))
+ (if* (gethash ch char-ht)
+ then (incf (gethash ch char-ht))
+ else (setf (gethash ch char-ht) 1))))
+ (maphash #'(lambda (k v)
+ (push (cons k v) results))
+ char-ht)
+ (sort results #'(lambda (x y) (> (cdr x) (cdr y))))))
+
+(publish
+ :path "/ichars"
+ :content-type "text/html"
+ :function
+
+ #-(and allegro ics (version>= 6 0))
+ #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (princ #.(format nil "~
+This page available only with International Allegro CL post 6.0")
+ *html-stream*))))
+
+ ;; Need pre-final.1's :try-variant change to find-external-format
+ #+(and allegro ics (version>= 6 0))
+ #'(lambda (req ent)
+ (let* ((body (get-request-body req))
+ (query (if* body
+ then (form-urlencoded-to-query body)))
+ (lisp-ef (or (if* query
+ then (cdr (assoc "lisp-ef" query :test #'equal)))
+ ":utf8"))
+ (http-charset (or (if* query
+ then (cdr (assoc "http-charset" query
+ :test #'equal)))
+ "utf-8"))
+ (http-content-type (format nil "text/html; charset=~a"
+ http-charset)))
+
+ (setq lisp-ef
+ (or (read-from-string lisp-ef)
+ :latin1-base))
+ (with-http-response (req ent)
+ (with-http-body (req ent
+ :external-format (crlf-base-ef
+ (find-external-format
+ lisp-ef
+ :try-variant t)))
+ (html
+ (:html
+ (:head (:title (:princ-safe
+ (format nil "Character Display: ~a / ~a"
+ lisp-ef http-charset)))
+ ((:meta http-equiv "content-type"
+ content http-content-type)))
+ (:body
+ ((:form :action "ichars" :method "POST")
+ "HTTP content-type: " (:strong (:prin1 http-content-type))
+ :br
+ "with-http-body's external-format: " (:strong (:prin1 lisp-ef))
+ :br
+ :br
+ "Note that the way characters are displayed depends upon "
+ "the browser's fonts, and how the browser interprets "
+ "the HTTP content-type."
+ :br
+ :br
+ (:center
+ ((:table :border 1
+ :cellpadding 2)
+ (:tr (:th "Charset") (:th "Lisp Character") (:th "Display"))
+ (:tr (:td "Latin-1"))
+ (:tr (:td "Latin-1")
+ (:td (:prin1 #\a))
+ (:td (:princ #\a)))
+ (:tr (:td "Latin-1")
+ (:td (:prin1 #\b))
+ (:td (:princ #\b)))
+ (:tr (:td "Latin-1")
+ (:td (:prin1 #\c))
+ (:td (:princ #\c)))
+ (:tr (:td "Latin-1")
+ (:td (:prin1 #\cent_sign))
+ (:td (:princ #\cent_sign)))
+ (:tr (:td "Latin-1")
+ (:td (:prin1 #\pound_sign))
+ (:td (:princ #\pound_sign)))
+ (:tr (:td "Latin-1")
+ (:td (:prin1 #\latin_small_letter_thorn))
+ (:td (:princ #\latin_small_letter_thorn)))
+ (:tr (:td "Latin-1")
+ (:td (:prin1 #\latin_capital_letter_ae))
+ (:td (:princ #\latin_capital_letter_ae)))
+ (:tr (:td "Latin-1")
+ (:td (:prin1 #\latin_capital_letter_thorn))
+ (:td (:princ #\latin_capital_letter_thorn)))
+ (:tr (:td "Latin-1")
+ (:td (:prin1 #\latin_capital_letter_i_with_circumflex))
+ (:td (:princ #\latin_capital_letter_i_with_circumflex)))
+ (:tr (:td "Latin-2"))
+ (:tr (:td "Latin-2")
+ (:td (:prin1 #\latin_small_letter_u_with_ring_above))
+ (:td (:princ #\latin_small_letter_u_with_ring_above)))
+ (:tr (:td "Latin-2")
+ (:td (:prin1 #\latin_capital_letter_n_with_caron))
+ (:td (:princ #\latin_capital_letter_n_with_caron)))
+ (:tr (:td "Latin-2")
+ (:td (:prin1 #\latin_capital_letter_l_with_stroke))
+ (:td (:princ #\latin_capital_letter_l_with_stroke)))
+ (:tr (:td "Latin-3"))
+ (:tr (:td "Latin-3")
+ (:td (:prin1 #\latin_small_letter_j_with_circumflex))
+ (:td (:princ #\latin_small_letter_j_with_circumflex)))
+ (:tr (:td "Latin-3")
+ (:td (:prin1 #\latin_capital_letter_h_with_stroke))
+ (:td (:princ #\latin_capital_letter_h_with_stroke)))
+ (:tr (:td "Latin-3")
+ (:td (:prin1 #\latin_capital_letter_c_with_circumflex))
+ (:td (:princ #\latin_capital_letter_c_with_circumflex)))
+ (:tr (:td "Latin-4"))
+ (:tr (:td "Latin-4")
+ (:td (:prin1 #\latin_small_letter_u_with_ogonek))
+ (:td (:princ #\latin_small_letter_u_with_ogonek)))
+ (:tr (:td "Latin-4")
+ (:td (:prin1 #\latin_capital_letter_i_with_macron))
+ (:td (:princ #\latin_capital_letter_i_with_macron)))
+ (:tr (:td "Latin-4")
+ (:td (:prin1 #\latin_capital_letter_g_with_cedilla))
+ (:td (:princ #\latin_capital_letter_g_with_cedilla)))
+ (:tr (:td "Latin-5"))
+ (:tr (:td "Latin-5")
+ (:td (:prin1 #\cyrillic_capital_letter_ukrainian_ie))
+ (:td (:princ #\cyrillic_capital_letter_ukrainian_ie)))
+ (:tr (:td "Latin-5")
+ (:td (:prin1 #\cyrillic_small_letter_nje))
+ (:td (:princ #\cyrillic_small_letter_nje)))
+ (:tr (:td "Latin-5")
+ (:td (:prin1 #\cyrillic_capital_letter_ya))
+ (:td (:princ #\cyrillic_capital_letter_ya)))
+ (:tr (:td "Latin-6"))
+ (:tr (:td "Latin-6")
+ (:td (:prin1 #\arabic_letter_feh))
+ (:td (:princ #\arabic_letter_feh)))
+ (:tr (:td "Latin-6")
+ (:td (:prin1 #\arabic_letter_hah))
+ (:td (:princ #\arabic_letter_hah)))
+ (:tr (:td "Latin-6")
+ (:td (:prin1 #\arabic_letter_yeh_with_hamza_above))
+ (:td (:princ #\arabic_letter_yeh_with_hamza_above)))
+ (:tr (:td "Latin-7"))
+ (:tr (:td "Latin-7")
+ (:td (:prin1 #\greek_capital_letter_delta))
+ (:td (:princ #\greek_capital_letter_delta)))
+ (:tr (:td "Latin-7")
+ (:td (:prin1 #\greek_small_letter_eta))
+ (:td (:princ #\greek_small_letter_eta)))
+ (:tr (:td "Latin-7")
+ (:td (:prin1 #\greek_capital_letter_sigma))
+ (:td (:princ #\greek_capital_letter_sigma)))
+ (:tr (:td "Latin-8"))
+ (:tr (:td "Latin-8")
+ (:td (:prin1 #\hebrew_letter_alef))
+ (:td (:princ #\hebrew_letter_alef)))
+ (:tr (:td "Latin-8")
+ (:td (:prin1 #\hebrew_letter_bet))
+ (:td (:princ #\hebrew_letter_bet)))
+ (:tr (:td "Latin-8")
+ (:td (:prin1 #\hebrew_letter_gimel))
+ (:td (:princ #\hebrew_letter_gimel)))
+ (:tr (:td "Latin-15"))
+ (:tr (:td "Latin-15")
+ (:td (:prin1 #\latin_small_ligature_oe))
+ (:td (:princ #\latin_small_ligature_oe)))
+ (:tr (:td "Latin-15")
+ (:td (:prin1 #\latin_capital_ligature_oe))
+ (:td (:princ #\latin_capital_ligature_oe)))
+ (:tr (:td "Japanese"))
+ (:tr (:td "Japanese")
+ (:td (:prin1 #\hiragana_letter_a))
+ (:td (:princ #\hiragana_letter_a)))
+ (:tr (:td "Japanese")
+ (:td (:prin1 #\hiragana_letter_i))
+ (:td (:princ #\hiragana_letter_i)))
+ (:tr (:td "CJK"))
+ (:tr (:td "CJK")
+ (:td (:prin1 #\cjk_compatibility_ideograph-f900))
+ (:td (:princ #\cjk_compatibility_ideograph-f900)))
+ (:tr (:td "CJK")
+ (:td (:prin1 #\cjk_compatibility_ideograph-f901))
+ (:td (:princ #\cjk_compatibility_ideograph-f901)))
+ (:tr (:td "CJK")
+ (:td (:prin1 #\cjk_compatibility_ideograph-f902))
+ (:td (:princ #\cjk_compatibility_ideograph-f902)))
+ (:tr (:td "Ligature"))
+ (:tr (:td "Ligature")
+ (:td (:prin1 #\latin_small_ligature_fi))
+ (:td (:princ #\latin_small_ligature_fi)))
+ (:tr (:td "Ligature")
+ (:td (:prin1 #\latin_small_ligature_fl))
+ (:td (:princ #\latin_small_ligature_fl)))
+ ))
+ :br
+ :br
+ (:princ-safe (format nil "~
+Switch Lisp External-Format (Current is ~s): "
+ (ef-name (find-external-format lisp-ef))))
+ ((:select name "lisp-ef")
+ ((:option value ":utf8-base" :selected "selected")
+ ":utf8-base")
+ ((:option value ":iso8859-1") ":iso8859-1")
+ ((:option value ":iso8859-2") ":iso8859-2")
+ ((:option value ":iso8859-3") ":iso8859-3")
+ ((:option value ":iso8859-4") ":iso8859-4")
+ ((:option value ":iso8859-5") ":iso8859-5")
+ ((:option value ":iso8859-6") ":iso8859-6")
+ ((:option value ":iso8859-7") ":iso8859-7")
+ ((:option value ":iso8859-8") ":iso8859-8")
+ ((:option value ":iso8859-15")":iso8859-15")
+ ((:option value ":shiftjis") ":shiftjis")
+ ((:option value ":euc") ":euc")
+ ((:option value ":932") ":932 (Windows 932)")
+ ((:option value ":1250") ":1250 (Windows 1250)")
+ ((:option value ":1254") ":1254 (Windows 1254)")
+ ((:option value ":1251") ":1251 (Windows 1251)")
+ ((:option value ":1255") ":1255 (Windows 1255)")
+ )
+ :br
+ (:princ-safe (format nil "~
+Switch HTTP Charset: (Current is ~s): "
+ http-charset))
+ ((:select name "http-charset")
+ ((:option value "utf-8" :selected "selected") "utf-8")
+ ((:option value "iso-8859-1") "iso-8859-1")
+ ((:option value "iso-8859-2") "iso-8859-2")
+ ((:option value "iso-8859-3") "iso-8859-3")
+ ((:option value "iso-8859-4") "iso-8859-4")
+ ((:option value "iso-8859-5") "iso-8859-5")
+ ((:option value "iso-8859-6") "iso-8859-6")
+ ((:option value "iso-8859-7") "iso-8859-7")
+ ((:option value "iso-8859-8") "iso-8859-8")
+ ((:option value "iso-8859-15") "iso-8859-15")
+ ((:option value "shift_jis") "shift_jis")
+ ((:option value "euc-jp") "euc-jp")
+ ((:option value "windows-932") "windows-932")
+ ((:option value "windows-1250")
+ "windows-1250")
+ ((:option value "windows-1254")
+ "windows-1254")
+ ((:option value "windows-1251")
+ "windows-1251")
+ ((:option value "windows-1255")
+ "windows-1255")
+ )
+ :br
+ :br
+ ((:input :type "submit" :value "Redisplay")))))))
+ ))))
Added: vendor/portableaserve/aserve/examples/file2000.txt
===================================================================
--- vendor/portableaserve/aserve/examples/file2000.txt 2006-02-18 09:34:15 UTC (rev 1845)
+++ vendor/portableaserve/aserve/examples/file2000.txt 2006-02-18 10:02:10 UTC (rev 1846)
@@ -0,0 +1 @@
+this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this is a sample file of roughly 2000 bytes used to do benchmarks on the retrieval speed of AllegroServe this
Added: vendor/portableaserve/aserve/examples/foo.txt
===================================================================
--- vendor/portableaserve/aserve/examples/foo.txt 2006-02-18 09:34:15 UTC (rev 1845)
+++ vendor/portableaserve/aserve/examples/foo.txt 2006-02-18 10:02:10 UTC (rev 1846)
@@ -0,0 +1,6 @@
+this is a test of
+returning a text
+file and now this is
+it for the file
+next line
+and one more line
Added: vendor/portableaserve/aserve/examples/fresh.jpg
===================================================================
(Binary files differ)
Property changes on: vendor/portableaserve/aserve/examples/fresh.jpg
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: vendor/portableaserve/aserve/examples/prfile9.jpg
===================================================================
(Binary files differ)
Property changes on: vendor/portableaserve/aserve/examples/prfile9.jpg
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: vendor/portableaserve/aserve/examples/puzzle.cl
===================================================================
--- vendor/portableaserve/aserve/examples/puzzle.cl 2006-02-18 09:34:15 UTC (rev 1845)
+++ vendor/portableaserve/aserve/examples/puzzle.cl 2006-02-18 10:02:10 UTC (rev 1846)
@@ -0,0 +1,828 @@
+;; -*- mode: common-lisp; package: net.aserve.examples -*-
+;;
+;; puzzle.cl
+;;
+;; copyright (c) 1986-2000 Franz Inc, Berkeley, CA
+;;
+;; This code is free software; you can redistribute it and/or
+;; modify it under the terms of the version 2.1 of
+;; the GNU Lesser General Public License as published by
+;; the Free Software Foundation;
+;;
+;; This code is distributed in the hope that it will be useful,
+;; but without any warranty; without even the implied warranty of
+;; merchantability or fitness for a particular purpose. See the GNU
+;; Lesser General Public License for more details.
+;;
+;; Version 2.1 of the GNU Lesser General Public License is in the file
+;; license-lgpl.txt that was distributed with this file.
+;; If it is not present, you can access it from
+;; http://www.gnu.org/copyleft/lesser.txt (until superseded by a newer
+;; version) or write to the Free Software Foundation, Inc., 59 Temple Place,
+;; Suite 330, Boston, MA 02111-1307 USA
+;;
+;;
+;; $Id: puzzle.cl,v 1.3 2002/12/26 19:55:44 rudi Exp $
+
+;; Description:
+;; Allegro Serve puzzle example
+
+
+;; Original Author: Charles A. Cox, Franz Inc.
+
+
+
+(defpackage puzzle
+ (:use :common-lisp :acl-compat.excl))
+
+(in-package :puzzle)
+
+(eval-when (compile load eval)
+ (require :aserve))
+
+(defpackage puzzle
+ (:use :net.html.generator :net.aserve))
+
+(defparameter .directions.
+ (make-array
+ 8
+ :initial-contents '((-1 . -1) ; nw
+ (-1 . 0) ; n
+ (-1 . +1) ; ne
+ (0 . -1) ; w
+ (0 . +1) ; e
+ (+1 . -1) ; sw
+ (+1 . 0) ; s
+ (+1 . +1) ; se
+ )))
+
+;; Bitmap of all Unicode characters whose name includes "letter".
+(defparameter .unicode-letters-bm.
+ (let ((a (make-array #.(expt 2 16) :element-type 'bit
+ :initial-element 0)))
+ (dolist
+ (c '(#x0041 #x0042 #x0043 #x0044 #x0045 #x0046 #x0047 #x0048 #x0049
+ #x004a #x004b #x004c #x004d #x004e #x004f #x0050 #x0051 #x0052
+ #x0053 #x0054 #x0055 #x0056 #x0057 #x0058 #x0059 #x005a #x0061
+ #x0062 #x0063 #x0064 #x0065 #x0066 #x0067 #x0068 #x0069 #x006a
+ #x006b #x006c #x006d #x006e #x006f #x0070 #x0071 #x0072 #x0073
+ #x0074 #x0075 #x0076 #x0077 #x0078 #x0079 #x007a #x00c0 #x00c1
+ #x00c2 #x00c3 #x00c4 #x00c5 #x00c6 #x00c7 #x00c8 #x00c9 #x00ca
+ #x00cb #x00cc #x00cd #x00ce #x00cf #x00d0 #x00d1 #x00d2 #x00d3
+ #x00d4 #x00d5 #x00d6 #x00d8 #x00d9 #x00da #x00db #x00dc #x00dd
+ #x00de #x00df #x00e0 #x00e1 #x00e2 #x00e3 #x00e4 #x00e5 #x00e6
+ #x00e7 #x00e8 #x00e9 #x00ea #x00eb #x00ec #x00ed #x00ee #x00ef
+ #x00f0 #x00f1 #x00f2 #x00f3 #x00f4 #x00f5 #x00f6 #x00f8 #x00f9
+ #x00fa #x00fb #x00fc #x00fd #x00fe #x00ff #x0100 #x0101 #x0102
+ #x0103 #x0104 #x0105 #x0106 #x0107 #x0108 #x0109 #x010a #x010b
+ #x010c #x010d #x010e #x010f #x0110 #x0111 #x0112 #x0113 #x0114
+ #x0115 #x0116 #x0117 #x0118 #x0119 #x011a #x011b #x011c #x011d
+ #x011e #x011f #x0120 #x0121 #x0122 #x0123 #x0124 #x0125 #x0126
+ #x0127 #x0128 #x0129 #x012a #x012b #x012c #x012d #x012e #x012f
+ #x0130 #x0131 #x0134 #x0135 #x0136 #x0137 #x0138 #x0139 #x013a
+ #x013b #x013c #x013d #x013e #x013f #x0140 #x0141 #x0142 #x0143
+ #x0144 #x0145 #x0146 #x0147 #x0148 #x0149 #x014a #x014b #x014c
+ #x014d #x014e #x014f #x0150 #x0151 #x0154 #x0155 #x0156 #x0157
+ #x0158 #x0159 #x015a #x015b #x015c #x015d #x015e #x015f #x0160
+ #x0161 #x0162 #x0163 #x0164 #x0165 #x0166 #x0167 #x0168 #x0169
+ #x016a #x016b #x016c #x016d #x016e #x016f #x0170 #x0171 #x0172
+ #x0173 #x0174 #x0175 #x0176 #x0177 #x0178 #x0179 #x017a #x017b
+ #x017c #x017d #x017e #x017f #x0180 #x0181 #x0182 #x0183 #x0184
+ #x0185 #x0186 #x0187 #x0188 #x0189 #x018a #x018b #x018c #x018d
+ #x018e #x018f #x0190 #x0191 #x0192 #x0193 #x0194 #x0195 #x0196
+ #x0197 #x0198 #x0199 #x019a #x019b #x019c #x019d #x019e #x019f
+ #x01a0 #x01a1 #x01a2 #x01a3 #x01a4 #x01a5 #x01a6 #x01a7 #x01a8
+ #x01a9 #x01aa #x01ab #x01ac #x01ad #x01ae #x01af #x01b0 #x01b1
+ #x01b2 #x01b3 #x01b4 #x01b5 #x01b6 #x01b7 #x01b8 #x01b9 #x01ba
+ #x01bb #x01bc #x01bd #x01be #x01bf #x01c0 #x01c1 #x01c2 #x01c3
+ #x01c4 #x01c5 #x01c6 #x01c7 #x01c8 #x01c9 #x01ca #x01cb #x01cc
+ #x01cd #x01ce #x01cf #x01d0 #x01d1 #x01d2 #x01d3 #x01d4 #x01d5
+ #x01d6 #x01d7 #x01d8 #x01d9 #x01da #x01db #x01dc #x01dd #x01de
+ #x01df #x01e0 #x01e1 #x01e2 #x01e3 #x01e4 #x01e5 #x01e6 #x01e7
+ #x01e8 #x01e9 #x01ea #x01eb #x01ec #x01ed #x01ee #x01ef #x01f0
+ #x01f1 #x01f2 #x01f3 #x01f4 #x01f5 #x01fa #x01fb #x01fc #x01fd
+ #x01fe #x01ff #x0200 #x0201 #x0202 #x0203 #x0204 #x0205 #x0206
+ #x0207 #x0208 #x0209 #x020a #x020b #x020c #x020d #x020e #x020f
+ #x0210 #x0211 #x0212 #x0213 #x0214 #x0215 #x0216 #x0217 #x0250
+ #x0251 #x0252 #x0253 #x0254 #x0255 #x0256 #x0257 #x0258 #x0259
+ #x025a #x025b #x025c #x025d #x025e #x025f #x0260 #x0261 #x0262
+ #x0263 #x0264 #x0265 #x0266 #x0267 #x0268 #x0269 #x026a #x026b
+ #x026c #x026d #x026e #x026f #x0270 #x0271 #x0272 #x0273 #x0274
+ #x0275 #x0276 #x0277 #x0278 #x0279 #x027a #x027b #x027c #x027d
+ #x027e #x027f #x0280 #x0281 #x0282 #x0283 #x0284 #x0285 #x0286
+ #x0287 #x0288 #x0289 #x028a #x028b #x028c #x028d #x028e #x028f
+ #x0290 #x0291 #x0292 #x0293 #x0294 #x0295 #x0296 #x0297 #x0298
+ #x0299 #x029a #x029b #x029c #x029d #x029e #x029f #x02a0 #x02a1
+ #x02a2 #x02a3 #x02a4 #x02a5 #x02a6 #x02a7 #x02a8 #x02b0 #x02b1
+ #x02b2 #x02b3 #x02b4 #x02b5 #x02b6 #x02b7 #x02b8 #x02b9 #x02ba
+ #x02bb #x02bc #x02bd #x02be #x02bf #x02c0 #x02c1 #x02c2 #x02c3
+ #x02c4 #x02c5 #x02c6 #x02c8 #x02c9 #x02ca #x02cb #x02cc #x02cd
+ #x02ce #x02cf #x02d0 #x02d1 #x02d2 #x02d3 #x02d4 #x02d5 #x02d6
+ #x02d7 #x02de #x02e0 #x02e1 #x02e2 #x02e3 #x02e4 #x02e5 #x02e6
+ #x02e7 #x02e8 #x02e9 #x0386 #x0388 #x0389 #x038a #x038c #x038e
+ #x038f #x0390 #x0391 #x0392 #x0393 #x0394 #x0395 #x0396 #x0397
+ #x0398 #x0399 #x039a #x039b #x039c #x039d #x039e #x039f #x03a0
+ #x03a1 #x03a3 #x03a4 #x03a5 #x03a6 #x03a7 #x03a8 #x03a9 #x03aa
+ #x03ab #x03ac #x03ad #x03ae #x03af #x03b0 #x03b1 #x03b2 #x03b3
+ #x03b4 #x03b5 #x03b6 #x03b7 #x03b8 #x03b9 #x03ba #x03bb #x03bc
+ #x03bd #x03be #x03bf #x03c0 #x03c1 #x03c2 #x03c3 #x03c4 #x03c5
+ #x03c6 #x03c7 #x03c8 #x03c9 #x03ca #x03cb #x03cc #x03cd #x03ce
+ #x03da #x03dc #x03de #x03e0 #x03e2 #x03e3 #x03e4 #x03e5 #x03e6
+ #x03e7 #x03e8 #x03e9 #x03ea #x03eb #x03ec #x03ed #x03ee #x03ef
+ #x03f3 #x0401 #x0402 #x0403 #x0404 #x0405 #x0406 #x0407 #x0408
+ #x0409 #x040a #x040b #x040c #x040e #x040f #x0410 #x0411 #x0412
+ #x0413 #x0414 #x0415 #x0416 #x0417 #x0418 #x0419 #x041a #x041b
+ #x041c #x041d #x041e #x041f #x0420 #x0421 #x0422 #x0423 #x0424
+ #x0425 #x0426 #x0427 #x0428 #x0429 #x042a #x042b #x042c #x042d
+ #x042e #x042f #x0430 #x0431 #x0432 #x0433 #x0434 #x0435 #x0436
+ #x0437 #x0438 #x0439 #x043a #x043b #x043c #x043d #x043e #x043f
+ #x0440 #x0441 #x0442 #x0443 #x0444 #x0445 #x0446 #x0447 #x0448
+ #x0449 #x044a #x044b #x044c #x044d #x044e #x044f #x0451 #x0452
+ #x0453 #x0454 #x0455 #x0456 #x0457 #x0458 #x0459 #x045a #x045b
+ #x045c #x045e #x045f #x0460 #x0461 #x0462 #x0463 #x0464 #x0465
+ #x0466 #x0467 #x0468 #x0469 #x046a #x046b #x046c #x046d #x046e
+ #x046f #x0470 #x0471 #x0472 #x0473 #x0474 #x0475 #x0476 #x0477
+ #x0478 #x0479 #x047a #x047b #x047c #x047d #x047e #x047f #x0480
+ #x0481 #x0490 #x0491 #x0492 #x0493 #x0494 #x0495 #x0496 #x0497
+ #x0498 #x0499 #x049a #x049b #x049c #x049d #x049e #x049f #x04a0
+ #x04a1 #x04a2 #x04a3 #x04a6 #x04a7 #x04a8 #x04a9 #x04aa #x04ab
+ #x04ac #x04ad #x04ae #x04af #x04b0 #x04b1 #x04b2 #x04b3 #x04b6
+ #x04b7 #x04b8 #x04b9 #x04ba #x04bb #x04bc #x04bd #x04be #x04bf
+ #x04c0 #x04c1 #x04c2 #x04c3 #x04c4 #x04c7 #x04c8 #x04cb #x04cc
+ #x04d0 #x04d1 #x04d2 #x04d3 #x04d6 #x04d7 #x04d8 #x04d9 #x04da
+ #x04db #x04dc #x04dd #x04de #x04df #x04e0 #x04e1 #x04e2 #x04e3
+ #x04e4 #x04e5 #x04e6 #x04e7 #x04e8 #x04e9 #x04ea #x04eb #x04ee
+ #x04ef #x04f0 #x04f1 #x04f2 #x04f3 #x04f4 #x04f5 #x04f8 #x04f9
+ #x0531 #x0532 #x0533 #x0534 #x0535 #x0536 #x0537 #x0538 #x0539
+ #x053a #x053b #x053c #x053d #x053e #x053f #x0540 #x0541 #x0542
+ #x0543 #x0544 #x0545 #x0546 #x0547 #x0548 #x0549 #x054a #x054b
+ #x054c #x054d #x054e #x054f #x0550 #x0551 #x0552 #x0553 #x0554
+ #x0555 #x0556 #x0559 #x0561 #x0562 #x0563 #x0564 #x0565 #x0566
+ #x0567 #x0568 #x0569 #x056a #x056b #x056c #x056d #x056e #x056f
+ #x0570 #x0571 #x0572 #x0573 #x0574 #x0575 #x0576 #x0577 #x0578
+ #x0579 #x057a #x057b #x057c #x057d #x057e #x057f #x0580 #x0581
+ #x0582 #x0583 #x0584 #x0585 #x0586 #x05d0 #x05d1 #x05d2 #x05d3
+ #x05d4 #x05d5 #x05d6 #x05d7 #x05d8 #x05d9 #x05da #x05db #x05dc
+ #x05dd #x05de #x05df #x05e0 #x05e1 #x05e2 #x05e3 #x05e4 #x05e5
+ #x05e6 #x05e7 #x05e8 #x05e9 #x05ea #x0621 #x0622 #x0623 #x0624
+ #x0625 #x0626 #x0627 #x0628 #x0629 #x062a #x062b #x062c #x062d
+ #x062e #x062f #x0630 #x0631 #x0632 #x0633 #x0634 #x0635 #x0636
+ #x0637 #x0638 #x0639 #x063a #x0641 #x0642 #x0643 #x0644 #x0645
+ #x0646 #x0647 #x0648 #x0649 #x064a #x0670 #x0671 #x0672 #x0673
+ #x0674 #x0675 #x0676 #x0677 #x0678 #x0679 #x067a #x067b #x067c
+ #x067d #x067e #x067f #x0680 #x0681 #x0682 #x0683 #x0684 #x0685
+ #x0686 #x0687 #x0688 #x0689 #x068a #x068b #x068c #x068d #x068e
+ #x068f #x0690 #x0691 #x0692 #x0693 #x0694 #x0695 #x0696 #x0697
+ #x0698 #x0699 #x069a #x069b #x069c #x069d #x069e #x069f #x06a0
+ #x06a1 #x06a2 #x06a3 #x06a4 #x06a5 #x06a6 #x06a7 #x06a8 #x06a9
+ #x06aa #x06ab #x06ac #x06ad #x06ae #x06af #x06b0 #x06b1 #x06b2
+ #x06b3 #x06b4 #x06b5 #x06b6 #x06b7 #x06ba #x06bb #x06bc #x06bd
+ #x06be #x06c0 #x06c1 #x06c2 #x06c3 #x06c4 #x06c5 #x06c6 #x06c7
+ #x06c8 #x06c9 #x06ca #x06cb #x06cc #x06cd #x06ce #x06d0 #x06d1
+ #x06d2 #x06d3 #x06d5 #x0905 #x0906 #x0907 #x0908 #x0909 #x090a
+ #x090b #x090c #x090d #x090e #x090f #x0910 #x0911 #x0912 #x0913
+ #x0914 #x0915 #x0916 #x0917 #x0918 #x0919 #x091a #x091b #x091c
+ #x091d #x091e #x091f #x0920 #x0921 #x0922 #x0923 #x0924 #x0925
+ #x0926 #x0927 #x0928 #x0929 #x092a #x092b #x092c #x092d #x092e
+ #x092f #x0930 #x0931 #x0932 #x0933 #x0934 #x0935 #x0936 #x0937
+ #x0938 #x0939 #x0958 #x0959 #x095a #x095b #x095c #x095d #x095e
+ #x095f #x0960 #x0961 #x0985 #x0986 #x0987 #x0988 #x0989 #x098a
+ #x098b #x098c #x098f #x0990 #x0993 #x0994 #x0995 #x0996 #x0997
+ #x0998 #x0999 #x099a #x099b #x099c #x099d #x099e #x099f #x09a0
+ #x09a1 #x09a2 #x09a3 #x09a4 #x09a5 #x09a6 #x09a7 #x09a8 #x09aa
+ #x09ab #x09ac #x09ad #x09ae #x09af #x09b0 #x09b2 #x09b6 #x09b7
+ #x09b8 #x09b9 #x09dc #x09dd #x09df #x09e0 #x09e1 #x09f0 #x09f1
+ #x0a05 #x0a06 #x0a07 #x0a08 #x0a09 #x0a0a #x0a0f #x0a10 #x0a13
+ #x0a14 #x0a15 #x0a16 #x0a17 #x0a18 #x0a19 #x0a1a #x0a1b #x0a1c
+ #x0a1d #x0a1e #x0a1f #x0a20 #x0a21 #x0a22 #x0a23 #x0a24 #x0a25
+ #x0a26 #x0a27 #x0a28 #x0a2a #x0a2b #x0a2c #x0a2d #x0a2e #x0a2f
+ #x0a30 #x0a32 #x0a33 #x0a35 #x0a36 #x0a38 #x0a39 #x0a59 #x0a5a
+ #x0a5b #x0a5c #x0a5e #x0a85 #x0a86 #x0a87 #x0a88 #x0a89 #x0a8a
+ #x0a8b #x0a8f #x0a90 #x0a93 #x0a94 #x0a95 #x0a96 #x0a97 #x0a98
+ #x0a99 #x0a9a #x0a9b #x0a9c #x0a9d #x0a9e #x0a9f #x0aa0 #x0aa1
+ #x0aa2 #x0aa3 #x0aa4 #x0aa5 #x0aa6 #x0aa7 #x0aa8 #x0aaa #x0aab
+ #x0aac #x0aad #x0aae #x0aaf #x0ab0 #x0ab2 #x0ab3 #x0ab5 #x0ab6
+ #x0ab7 #x0ab8 #x0ab9 #x0ae0 #x0b05 #x0b06 #x0b07 #x0b08 #x0b09
+ #x0b0a #x0b0b #x0b0c #x0b0f #x0b10 #x0b13 #x0b14 #x0b15 #x0b16
+ #x0b17 #x0b18 #x0b19 #x0b1a #x0b1b #x0b1c #x0b1d #x0b1e #x0b1f
+ #x0b20 #x0b21 #x0b22 #x0b23 #x0b24 #x0b25 #x0b26 #x0b27 #x0b28
+ #x0b2a #x0b2b #x0b2c #x0b2d #x0b2e #x0b2f #x0b30 #x0b32 #x0b33
+ #x0b36 #x0b37 #x0b38 #x0b39 #x0b5c #x0b5d #x0b5f #x0b60 #x0b61
+ #x0b85 #x0b86 #x0b87 #x0b88 #x0b89 #x0b8a #x0b8e #x0b8f #x0b90
+ #x0b92 #x0b93 #x0b94 #x0b95 #x0b99 #x0b9a #x0b9c #x0b9e #x0b9f
+ #x0ba3 #x0ba4 #x0ba8 #x0ba9 #x0baa #x0bae #x0baf #x0bb0 #x0bb1
+ #x0bb2 #x0bb3 #x0bb4 #x0bb5 #x0bb7 #x0bb8 #x0bb9 #x0c05 #x0c06
+ #x0c07 #x0c08 #x0c09 #x0c0a #x0c0b #x0c0c #x0c0e #x0c0f #x0c10
+ #x0c12 #x0c13 #x0c14 #x0c15 #x0c16 #x0c17 #x0c18 #x0c19 #x0c1a
+ #x0c1b #x0c1c #x0c1d #x0c1e #x0c1f #x0c20 #x0c21 #x0c22 #x0c23
+ #x0c24 #x0c25 #x0c26 #x0c27 #x0c28 #x0c2a #x0c2b #x0c2c #x0c2d
+ #x0c2e #x0c2f #x0c30 #x0c31 #x0c32 #x0c33 #x0c35 #x0c36 #x0c37
+ #x0c38 #x0c39 #x0c60 #x0c61 #x0c85 #x0c86 #x0c87 #x0c88 #x0c89
+ #x0c8a #x0c8b #x0c8c #x0c8e #x0c8f #x0c90 #x0c92 #x0c93 #x0c94
+ #x0c95 #x0c96 #x0c97 #x0c98 #x0c99 #x0c9a #x0c9b #x0c9c #x0c9d
+ #x0c9e #x0c9f #x0ca0 #x0ca1 #x0ca2 #x0ca3 #x0ca4 #x0ca5 #x0ca6
+ #x0ca7 #x0ca8 #x0caa #x0cab #x0cac #x0cad #x0cae #x0caf #x0cb0
+ #x0cb1 #x0cb2 #x0cb3 #x0cb5 #x0cb6 #x0cb7 #x0cb8 #x0cb9 #x0cde
+ #x0ce0 #x0ce1 #x0d05 #x0d06 #x0d07 #x0d08 #x0d09 #x0d0a #x0d0b
+ #x0d0c #x0d0e #x0d0f #x0d10 #x0d12 #x0d13 #x0d14 #x0d15 #x0d16
+ #x0d17 #x0d18 #x0d19 #x0d1a #x0d1b #x0d1c #x0d1d #x0d1e #x0d1f
+ #x0d20 #x0d21 #x0d22 #x0d23 #x0d24 #x0d25 #x0d26 #x0d27 #x0d28
+ #x0d2a #x0d2b #x0d2c #x0d2d #x0d2e #x0d2f #x0d30 #x0d31 #x0d32
+ #x0d33 #x0d34 #x0d35 #x0d36 #x0d37 #x0d38 #x0d39 #x0d60 #x0d61
+ #x0e81 #x0e82 #x0e84 #x0e87 #x0e88 #x0e8a #x0e8d #x0e94 #x0e95
+ #x0e96 #x0e97 #x0e99 #x0e9a #x0e9b #x0e9c #x0e9d #x0e9e #x0e9f
+ #x0ea1 #x0ea2 #x0ea3 #x0ea5 #x0ea7 #x0eaa #x0eab #x0ead #x0eae
+ #x0f40 #x0f41 #x0f42 #x0f43 #x0f44 #x0f45 #x0f46 #x0f47 #x0f49
+ #x0f4a #x0f4b #x0f4c #x0f4d #x0f4e #x0f4f #x0f50 #x0f51 #x0f52
+ #x0f53 #x0f54 #x0f55 #x0f56 #x0f57 #x0f58 #x0f59 #x0f5a #x0f5b
+ #x0f5c #x0f5d #x0f5e #x0f5f #x0f60 #x0f61 #x0f62 #x0f63 #x0f64
+ #x0f65 #x0f66 #x0f67 #x0f68 #x0f69 #x0f90 #x0f91 #x0f92 #x0f93
+ #x0f94 #x0f95 #x0f97 #x0f99 #x0f9a #x0f9b #x0f9c #x0f9d #x0f9e
+ #x0f9f #x0fa0 #x0fa1 #x0fa2 #x0fa3 #x0fa4 #x0fa5 #x0fa6 #x0fa7
+ #x0fa8 #x0fa9 #x0faa #x0fab #x0fac #x0fad #x0fb1 #x0fb2 #x0fb3
+ #x0fb4 #x0fb5 #x0fb6 #x0fb7 #x0fb9 #x10a0 #x10a1 #x10a2 #x10a3
+ #x10a4 #x10a5 #x10a6 #x10a7 #x10a8 #x10a9 #x10aa #x10ab #x10ac
+ #x10ad #x10ae #x10af #x10b0 #x10b1 #x10b2 #x10b3 #x10b4 #x10b5
+ #x10b6 #x10b7 #x10b8 #x10b9 #x10ba #x10bb #x10bc #x10bd #x10be
+ #x10bf #x10c0 #x10c1 #x10c2 #x10c3 #x10c4 #x10c5 #x10d0 #x10d1
+ #x10d2 #x10d3 #x10d4 #x10d5 #x10d6 #x10d7 #x10d8 #x10d9 #x10da
+ #x10db #x10dc #x10dd #x10de #x10df #x10e0 #x10e1 #x10e2 #x10e3
+ #x10e4 #x10e5 #x10e6 #x10e7 #x10e8 #x10e9 #x10ea #x10eb #x10ec
+ #x10ed #x10ee #x10ef #x10f0 #x10f1 #x10f2 #x10f3 #x10f4 #x10f5
+ #x10f6 #x1e00 #x1e01 #x1e02 #x1e03 #x1e04 #x1e05 #x1e06 #x1e07
+ #x1e08 #x1e09 #x1e0a #x1e0b #x1e0c #x1e0d #x1e0e #x1e0f #x1e10
+ #x1e11 #x1e12 #x1e13 #x1e14 #x1e15 #x1e16 #x1e17 #x1e18 #x1e19
+ #x1e1a #x1e1b #x1e1c #x1e1d #x1e1e #x1e1f #x1e20 #x1e21 #x1e22
+ #x1e23 #x1e24 #x1e25 #x1e26 #x1e27 #x1e28 #x1e29 #x1e2a #x1e2b
+ #x1e2c #x1e2d #x1e2e #x1e2f #x1e30 #x1e31 #x1e32 #x1e33 #x1e34
+ #x1e35 #x1e36 #x1e37 #x1e38 #x1e39 #x1e3a #x1e3b #x1e3c #x1e3d
+ #x1e3e #x1e3f #x1e40 #x1e41 #x1e42 #x1e43 #x1e44 #x1e45 #x1e46
+ #x1e47 #x1e48 #x1e49 #x1e4a #x1e4b #x1e4c #x1e4d #x1e4e #x1e4f
+ #x1e50 #x1e51 #x1e52 #x1e53 #x1e54 #x1e55 #x1e56 #x1e57 #x1e58
+ #x1e59 #x1e5a #x1e5b #x1e5c #x1e5d #x1e5e #x1e5f #x1e60 #x1e61
+ #x1e62 #x1e63 #x1e64 #x1e65 #x1e66 #x1e67 #x1e68 #x1e69 #x1e6a
+ #x1e6b #x1e6c #x1e6d #x1e6e #x1e6f #x1e70 #x1e71 #x1e72 #x1e73
+ #x1e74 #x1e75 #x1e76 #x1e77 #x1e78 #x1e79 #x1e7a #x1e7b #x1e7c
+ #x1e7d #x1e7e #x1e7f #x1e80 #x1e81 #x1e82 #x1e83 #x1e84 #x1e85
+ #x1e86 #x1e87 #x1e88 #x1e89 #x1e8a #x1e8b #x1e8c #x1e8d #x1e8e
+ #x1e8f #x1e90 #x1e91 #x1e92 #x1e93 #x1e94 #x1e95 #x1e96 #x1e97
+ #x1e98 #x1e99 #x1e9a #x1e9b #x1ea0 #x1ea1 #x1ea2 #x1ea3 #x1ea4
+ #x1ea5 #x1ea6 #x1ea7 #x1ea8 #x1ea9 #x1eaa #x1eab #x1eac #x1ead
+ #x1eae #x1eaf #x1eb0 #x1eb1 #x1eb2 #x1eb3 #x1eb4 #x1eb5 #x1eb6
+ #x1eb7 #x1eb8 #x1eb9 #x1eba #x1ebb #x1ebc #x1ebd #x1ebe #x1ebf
+ #x1ec0 #x1ec1 #x1ec2 #x1ec3 #x1ec4 #x1ec5 #x1ec6 #x1ec7 #x1ec8
+ #x1ec9 #x1eca #x1ecb #x1ecc #x1ecd #x1ece #x1ecf #x1ed0 #x1ed1
+ #x1ed2 #x1ed3 #x1ed4 #x1ed5 #x1ed6 #x1ed7 #x1ed8 #x1ed9 #x1eda
+ #x1edb #x1edc #x1edd #x1ede #x1edf #x1ee0 #x1ee1 #x1ee2 #x1ee3
+ #x1ee4 #x1ee5 #x1ee6 #x1ee7 #x1ee8 #x1ee9 #x1eea #x1eeb #x1eec
+ #x1eed #x1eee #x1eef #x1ef0 #x1ef1 #x1ef2 #x1ef3 #x1ef4 #x1ef5
+ #x1ef6 #x1ef7 #x1ef8 #x1ef9 #x1f00 #x1f01 #x1f02 #x1f03 #x1f04
+ #x1f05 #x1f06 #x1f07 #x1f08 #x1f09 #x1f0a #x1f0b #x1f0c #x1f0d
+ #x1f0e #x1f0f #x1f10 #x1f11 #x1f12 #x1f13 #x1f14 #x1f15 #x1f18
+ #x1f19 #x1f1a #x1f1b #x1f1c #x1f1d #x1f20 #x1f21 #x1f22 #x1f23
+ #x1f24 #x1f25 #x1f26 #x1f27 #x1f28 #x1f29 #x1f2a #x1f2b #x1f2c
+ #x1f2d #x1f2e #x1f2f #x1f30 #x1f31 #x1f32 #x1f33 #x1f34 #x1f35
+ #x1f36 #x1f37 #x1f38 #x1f39 #x1f3a #x1f3b #x1f3c #x1f3d #x1f3e
+ #x1f3f #x1f40 #x1f41 #x1f42 #x1f43 #x1f44 #x1f45 #x1f48 #x1f49
+ #x1f4a #x1f4b #x1f4c #x1f4d #x1f50 #x1f51 #x1f52 #x1f53 #x1f54
+ #x1f55 #x1f56 #x1f57 #x1f59 #x1f5b #x1f5d #x1f5f #x1f60 #x1f61
+ #x1f62 #x1f63 #x1f64 #x1f65 #x1f66 #x1f67 #x1f68 #x1f69 #x1f6a
+ #x1f6b #x1f6c #x1f6d #x1f6e #x1f6f #x1f70 #x1f71 #x1f72 #x1f73
+ #x1f74 #x1f75 #x1f76 #x1f77 #x1f78 #x1f79 #x1f7a #x1f7b #x1f7c
+ #x1f7d #x1f80 #x1f81 #x1f82 #x1f83 #x1f84 #x1f85 #x1f86 #x1f87
+ #x1f88 #x1f89 #x1f8a #x1f8b #x1f8c #x1f8d #x1f8e #x1f8f #x1f90
+ #x1f91 #x1f92 #x1f93 #x1f94 #x1f95 #x1f96 #x1f97 #x1f98 #x1f99
+ #x1f9a #x1f9b #x1f9c #x1f9d #x1f9e #x1f9f #x1fa0 #x1fa1 #x1fa2
+ #x1fa3 #x1fa4 #x1fa5 #x1fa6 #x1fa7 #x1fa8 #x1fa9 #x1faa #x1fab
+ #x1fac #x1fad #x1fae #x1faf #x1fb0 #x1fb1 #x1fb2 #x1fb3 #x1fb4
+ #x1fb6 #x1fb7 #x1fb8 #x1fb9 #x1fba #x1fbb #x1fbc #x1fc2 #x1fc3
+ #x1fc4 #x1fc6 #x1fc7 #x1fc8 #x1fc9 #x1fca #x1fcb #x1fcc #x1fd0
+ #x1fd1 #x1fd2 #x1fd3 #x1fd6 #x1fd7 #x1fd8 #x1fd9 #x1fda #x1fdb
+ #x1fe0 #x1fe1 #x1fe2 #x1fe3 #x1fe4 #x1fe5 #x1fe6 #x1fe7 #x1fe8
+ #x1fe9 #x1fea #x1feb #x1fec #x1ff2 #x1ff3 #x1ff4 #x1ff6 #x1ff7
+ #x1ff8 #x1ff9 #x1ffa #x1ffb #x1ffc #x207f #x210c #x2111 #x211c
+ #x2128 #x2129 #x212d #x249c #x249d #x249e #x249f #x24a0 #x24a1
+ #x24a2 #x24a3 #x24a4 #x24a5 #x24a6 #x24a7 #x24a8 #x24a9 #x24aa
+ #x24ab #x24ac #x24ad #x24ae #x24af #x24b0 #x24b1 #x24b2 #x24b3
+ #x24b4 #x24b5 #x24b6 #x24b7 #x24b8 #x24b9 #x24ba #x24bb #x24bc
+ #x24bd #x24be #x24bf #x24c0 #x24c1 #x24c2 #x24c3 #x24c4 #x24c5
+ #x24c6 #x24c7 #x24c8 #x24c9 #x24ca #x24cb #x24cc #x24cd #x24ce
+ #x24cf #x24d0 #x24d1 #x24d2 #x24d3 #x24d4 #x24d5 #x24d6 #x24d7
+ #x24d8 #x24d9 #x24da #x24db #x24dc #x24dd #x24de #x24df #x24e0
+ #x24e1 #x24e2 #x24e3 #x24e4 #x24e5 #x24e6 #x24e7 #x24e8 #x24e9
+ #x3041 #x3042 #x3043 #x3044 #x3045 #x3046 #x3047 #x3048 #x3049
+ #x304a #x304b #x304c #x304d #x304e #x304f #x3050 #x3051 #x3052
+ #x3053 #x3054 #x3055 #x3056 #x3057 #x3058 #x3059 #x305a #x305b
+ #x305c #x305d #x305e #x305f #x3060 #x3061 #x3062 #x3063 #x3064
+ #x3065 #x3066 #x3067 #x3068 #x3069 #x306a #x306b #x306c #x306d
+ #x306e #x306f #x3070 #x3071 #x3072 #x3073 #x3074 #x3075 #x3076
+ #x3077 #x3078 #x3079 #x307a #x307b #x307c #x307d #x307e #x307f
+ #x3080 #x3081 #x3082 #x3083 #x3084 #x3085 #x3086 #x3087 #x3088
+ #x3089 #x308a #x308b #x308c #x308d #x308e #x308f #x3090 #x3091
+ #x3092 #x3093 #x3094 #x30a1 #x30a2 #x30a3 #x30a4 #x30a5 #x30a6
+ #x30a7 #x30a8 #x30a9 #x30aa #x30ab #x30ac #x30ad #x30ae #x30af
+ #x30b0 #x30b1 #x30b2 #x30b3 #x30b4 #x30b5 #x30b6 #x30b7 #x30b8
+ #x30b9 #x30ba #x30bb #x30bc #x30bd #x30be #x30bf #x30c0 #x30c1
+ #x30c2 #x30c3 #x30c4 #x30c5 #x30c6 #x30c7 #x30c8 #x30c9 #x30ca
+ #x30cb #x30cc #x30cd #x30ce #x30cf #x30d0 #x30d1 #x30d2 #x30d3
+ #x30d4 #x30d5 #x30d6 #x30d7 #x30d8 #x30d9 #x30da #x30db #x30dc
+ #x30dd #x30de #x30df #x30e0 #x30e1 #x30e2 #x30e3 #x30e4 #x30e5
+ #x30e6 #x30e7 #x30e8 #x30e9 #x30ea #x30eb #x30ec #x30ed #x30ee
+ #x30ef #x30f0 #x30f1 #x30f2 #x30f3 #x30f4 #x30f5 #x30f6 #x30f7
+ #x30f8 #x30f9 #x30fa #x3105 #x3106 #x3107 #x3108 #x3109 #x310a
+ #x310b #x310c #x310d #x310e #x310f #x3110 #x3111 #x3112 #x3113
+ #x3114 #x3115 #x3116 #x3117 #x3118 #x3119 #x311a #x311b #x311c
+ #x311d #x311e #x311f #x3120 #x3121 #x3122 #x3123 #x3124 #x3125
+ #x3126 #x3127 #x3128 #x3129 #x312a #x312b #x312c #x3131 #x3132
+ #x3133 #x3134 #x3135 #x3136 #x3137 #x3138 #x3139 #x313a #x313b
+ #x313c #x313d #x313e #x313f #x3140 #x3141 #x3142 #x3143 #x3144
+ #x3145 #x3146 #x3147 #x3148 #x3149 #x314a #x314b #x314c #x314d
+ #x314e #x314f #x3150 #x3151 #x3152 #x3153 #x3154 #x3155 #x3156
+ #x3157 #x3158 #x3159 #x315a #x315b #x315c #x315d #x315e #x315f
+ #x3160 #x3161 #x3162 #x3163 #x3165 #x3166 #x3167 #x3168 #x3169
+ #x316a #x316b #x316c #x316d #x316e #x316f #x3170 #x3171 #x3172
+ #x3173 #x3174 #x3175 #x3176 #x3177 #x3178 #x3179 #x317a #x317b
+ #x317c #x317d #x317e #x317f #x3180 #x3181 #x3182 #x3183 #x3184
+ #x3185 #x3186 #x3187 #x3188 #x3189 #x318a #x318b #x318c #x318d
+ #x318e #xfb20 #xfb21 #xfb22 #xfb23 #xfb24 #xfb25 #xfb26 #xfb27
+ #xfb28 #xfb29 #xfb2a #xfb2b #xfb2c #xfb2d #xfb2e #xfb2f #xfb30
+ #xfb31 #xfb32 #xfb33 #xfb34 #xfb35 #xfb36 #xfb38 #xfb39 #xfb3a
+ #xfb3b #xfb3c #xfb3e #xfb40 #xfb41 #xfb43 #xfb44 #xfb46 #xfb47
+ #xfb48 #xfb49 #xfb4a #xfb4b #xfb4c #xfb4d #xfb4e #xfb50 #xfb51
+ #xfb52 #xfb53 #xfb54 #xfb55 #xfb56 #xfb57 #xfb58 #xfb59 #xfb5a
+ #xfb5b #xfb5c #xfb5d #xfb5e #xfb5f #xfb60 #xfb61 #xfb62 #xfb63
+ #xfb64 #xfb65 #xfb66 #xfb67 #xfb68 #xfb69 #xfb6a #xfb6b #xfb6c
+ #xfb6d #xfb6e #xfb6f #xfb70 #xfb71 #xfb72 #xfb73 #xfb74 #xfb75
+ #xfb76 #xfb77 #xfb78 #xfb79 #xfb7a #xfb7b #xfb7c #xfb7d #xfb7e
+ #xfb7f #xfb80 #xfb81 #xfb82 #xfb83 #xfb84 #xfb85 #xfb86 #xfb87
+ #xfb88 #xfb89 #xfb8a #xfb8b #xfb8c #xfb8d #xfb8e #xfb8f #xfb90
+ #xfb91 #xfb92 #xfb93 #xfb94 #xfb95 #xfb96 #xfb97 #xfb98 #xfb99
+ #xfb9a #xfb9b #xfb9c #xfb9d #xfb9e #xfb9f #xfba0 #xfba1 #xfba2
+ #xfba3 #xfba4 #xfba5 #xfba6 #xfba7 #xfba8 #xfba9 #xfbaa #xfbab
+ #xfbac #xfbad #xfbae #xfbaf #xfbb0 #xfbb1 #xfbd3 #xfbd4 #xfbd5
+ #xfbd6 #xfbd7 #xfbd8 #xfbd9 #xfbda #xfbdb #xfbdc #xfbdd #xfbde
+ #xfbdf #xfbe0 #xfbe1 #xfbe2 #xfbe3 #xfbe4 #xfbe5 #xfbe6 #xfbe7
+ #xfbe8 #xfbe9 #xfbfc #xfbfd #xfbfe #xfbff #xfe80 #xfe81 #xfe82
+ #xfe83 #xfe84 #xfe85 #xfe86 #xfe87 #xfe88 #xfe89 #xfe8a #xfe8b
+ #xfe8c #xfe8d #xfe8e #xfe8f #xfe90 #xfe91 #xfe92 #xfe93 #xfe94
+ #xfe95 #xfe96 #xfe97 #xfe98 #xfe99 #xfe9a #xfe9b #xfe9c #xfe9d
+ #xfe9e #xfe9f #xfea0 #xfea1 #xfea2 #xfea3 #xfea4 #xfea5 #xfea6
+ #xfea7 #xfea8 #xfea9 #xfeaa #xfeab #xfeac #xfead #xfeae #xfeaf
+ #xfeb0 #xfeb1 #xfeb2 #xfeb3 #xfeb4 #xfeb5 #xfeb6 #xfeb7 #xfeb8
+ #xfeb9 #xfeba #xfebb #xfebc #xfebd #xfebe #xfebf #xfec0 #xfec1
+ #xfec2 #xfec3 #xfec4 #xfec5 #xfec6 #xfec7 #xfec8 #xfec9 #xfeca
+ #xfecb #xfecc #xfecd #xfece #xfecf #xfed0 #xfed1 #xfed2 #xfed3
+ #xfed4 #xfed5 #xfed6 #xfed7 #xfed8 #xfed9 #xfeda #xfedb #xfedc
+ #xfedd #xfede #xfedf #xfee0 #xfee1 #xfee2 #xfee3 #xfee4 #xfee5
+ #xfee6 #xfee7 #xfee8 #xfee9 #xfeea #xfeeb #xfeec #xfeed #xfeee
+ #xfeef #xfef0 #xfef1 #xfef2 #xfef3 #xfef4 #xff21 #xff22 #xff23
+ #xff24 #xff25 #xff26 #xff27 #xff28 #xff29 #xff2a #xff2b #xff2c
+ #xff2d #xff2e #xff2f #xff30 #xff31 #xff32 #xff33 #xff34 #xff35
+ #xff36 #xff37 #xff38 #xff39 #xff3a #xff41 #xff42 #xff43 #xff44
+ #xff45 #xff46 #xff47 #xff48 #xff49 #xff4a #xff4b #xff4c #xff4d
+ #xff4e #xff4f #xff50 #xff51 #xff52 #xff53 #xff54 #xff55 #xff56
+ #xff57 #xff58 #xff59 #xff5a #xff66 #xff67 #xff68 #xff69 #xff6a
+ #xff6b #xff6c #xff6d #xff6e #xff6f #xff71 #xff72 #xff73 #xff74
+ #xff75 #xff76 #xff77 #xff78 #xff79 #xff7a #xff7b #xff7c #xff7d
+ #xff7e #xff7f #xff80 #xff81 #xff82 #xff83 #xff84 #xff85 #xff86
+ #xff87 #xff88 #xff89 #xff8a #xff8b #xff8c #xff8d #xff8e #xff8f
+ #xff90 #xff91 #xff92 #xff93 #xff94 #xff95 #xff96 #xff97 #xff98
+ #xff99 #xff9a #xff9b #xff9c #xff9d #xffa1 #xffa2 #xffa3 #xffa4
+ #xffa5 #xffa6 #xffa7 #xffa8 #xffa9 #xffaa #xffab #xffac #xffad
+ #xffae #xffaf #xffb0 #xffb1 #xffb2 #xffb3 #xffb4 #xffb5 #xffb6
+ #xffb7 #xffb8 #xffb9 #xffba #xffbb #xffbc #xffbd #xffbe #xffc2
+ #xffc3 #xffc4 #xffc5 #xffc6 #xffc7 #xffca #xffcb #xffcc #xffcd
+ #xffce #xffcf #xffd2 #xffd3 #xffd4 #xffd5 #xffd6 #xffd7 #xffda
+ #xffdb #xffdc))
+ (setf (aref a c) 1))
+ a))
+
+(defmacro cjk-p (code)
+ `(or
+ ;; CJK Ideographs
+ (<= #x4e00 ,code #x9fff)
+ ;; Hangul Syllables
+ (<= #xac00 ,code #xd7a3)))
+
+(defmacro puzzle-rows (puzzle)
+ `(first (array-dimensions ,puzzle)))
+
+(defmacro puzzle-cols (puzzle)
+ `(second (array-dimensions ,puzzle)))
+
+(defun get-random-dir ()
+ (aref .directions. (random 8)))
+
+(defun get-random-start (puzzle)
+ (cons (random (puzzle-rows puzzle))
+ (random (puzzle-cols puzzle))))
+
+;; Insert a word into a puzzle.
+(defun insert (word puzzle &key (install nil)
+ (dir (get-random-dir))
+ (start (get-random-start puzzle))
+ (attempt 0)
+ (extend-limit 0)
+ (attempt-limit 100)
+ &aux (length (length word))
+ (roff 0)
+ (coff 0))
+ (macrolet ((retry ()
+ `(progn
+ (incf attempt)
+ (setq start (get-random-start puzzle)
+ dir (get-random-dir))
+ (go :restart))))
+ (tagbody
+ :restart
+ (do ((row (car start) (+ row (car dir)))
+ (col (cdr start) (+ col (cdr dir)))
+ (i 0 (1+ i)))
+ ((>= i (length word))
+ ;; if we're not already installing, then we arrive here when
+ ;; we've passed all the tests and can begin installing.
+ (if* (not install)
+ then (setq install t)
+ (go :restart)))
+ ;; If we're installing, then just slap in the letter. Otherwise,
+ ;; check if the letter fits and/or if the puzzle needs extending.
+ (if* install
+ then (setf (aref puzzle row col) (schar word i))
+ else (if* (or (< row 0)
+ (< col 0)
+ (>= row (first (array-dimensions puzzle)))
+ (>= col (second (array-dimensions puzzle)))
+ (>= attempt attempt-limit))
+ then ;; Don't allow puzzle size to extend unless we've tried
+ ;; several attempts.
+ (if* (>= attempt attempt-limit)
+ then (incf extend-limit)
+ (setq attempt 0))
+ (multiple-value-bind (npuzzle nroff ncoff)
+ ;; We add 1 randomly to the row extension and to
+ ;; the column extension to work around the problem where
+ ;; the puzzle may already be completely full.
+ (extend-puzzle puzzle
+ extend-limit
+ (+ (car start) (* (car dir)
+ (- length (random 2))))
+ (+ (cdr start) (* (cdr dir)
+ (- length (random 2)))))
+ (if* npuzzle
+ then (setq puzzle npuzzle)
+ (incf roff nroff)
+ (incf coff ncoff)
+ (incf row nroff) (incf (car start) nroff)
+ (incf col ncoff) (incf (cdr start) ncoff)
+ else ;; extend-puzzle rejected because of
+ ;; extend-limit, so we just loop around to
+ ;; try again...
+ (retry))))
+ (if* (and (aref puzzle row col)
+ (not (eq (aref puzzle row col)
+ (schar word i))))
+ then ;; existing letters in puzzle didn't match. So we
+ ;; try again...
+ (retry)))))
+ (values puzzle start dir roff coff)))
+
+(defun extend-puzzle (puzzle extend-limit erow ecol
+ &aux (prows (puzzle-rows puzzle))
+ (pcols (puzzle-cols puzzle)))
+ (let* ((shift-rows (if* (minusp erow)
+ then (- erow)))
+ (shift-cols (if* (minusp ecol)
+ then (- ecol)))
+ (new-rows (+ prows (or shift-rows (max 0 (- (1+ erow) prows)))))
+ (new-cols (+ pcols (or shift-cols (max 0 (- (1+ ecol) pcols))))))
+ (if* (or (> new-rows extend-limit)
+ (> new-cols extend-limit))
+ then ;; reject
+ (return-from extend-puzzle nil))
+ (setq shift-rows (or shift-rows 0))
+ (setq shift-cols (or shift-cols 0))
+ (setq puzzle (adjust-array puzzle (list new-rows new-cols)
+ :initial-element nil))
+ (if* (or (minusp erow) (minusp ecol))
+ then (do ((r (- new-rows shift-rows 1) (1- r)))
+ ((< r 0))
+ (do ((c (- new-cols shift-cols 1) (1- c)))
+ ((< c 0))
+ (setf (aref puzzle (+ r shift-rows) (+ c shift-cols))
+ (aref puzzle r c)))
+ (do ((c 0 (1+ c)))
+ ((>= c shift-cols))
+ (setf (aref puzzle (+ r shift-rows) c) nil)))
+ (do ((r 0 (1+ r)))
+ ((>= r shift-rows))
+ (do ((c 0 (1+ c)))
+ ((>= c new-cols))
+ (setf (aref puzzle r c) nil))))
+ (values puzzle shift-rows shift-cols)))
+
+(defun make-puzzle (word-list fill)
+ ;; We actually make the puzzle twice, throwing away the first one after
+ ;; getting its size. The idea is that words are likely to be more evenly
+ ;; distributed in the second puzzle.
+ (if* (not word-list)
+ then (return-from make-puzzle nil))
+ (let ((puzzle (make-puzzle-1 word-list
+ (make-array '(1 1)
+ :initial-element nil
+ :adjustable t)
+ "none")))
+ (make-puzzle-1 word-list
+ (make-array (array-dimensions puzzle)
+ :initial-element nil
+ :adjustable t)
+ fill)))
+
+(defun make-puzzle-1 (word-list puzzle fill
+ &aux (answers nil)
+ (fill-sym (intern fill :keyword)))
+ (dolist (word word-list)
+ (multiple-value-bind (npuzzle start dir roff coff) (insert word puzzle)
+ (setq puzzle npuzzle)
+ (dolist (a answers)
+ (incf (car (second a)) roff)
+ (incf (cdr (second a)) coff))
+ (push (list word start dir) answers)))
+ (dotimes (i (apply #'* (array-dimensions puzzle)))
+ (if* (not (row-major-aref puzzle i))
+ then (setf (row-major-aref puzzle i)
+
+ (ecase fill-sym
+ (:|ascii-lc| (code-char (+ (random 26) #.(char-code #\a))))
+ (:|none| #\space)
+ (:|unicode-nocjk| (loop (let ((c (random #.(expt 2 16))))
+ (if* (= 1 (aref .unicode-letters-bm. c))
+ then (return (code-char c))))))
+ (:|unicode-cjk| (loop (let ((c (random #.(expt 2 16))))
+ (if* (= 1 (aref .unicode-letters-bm. c))
+ then (return (code-char c)))
+ (if* (cjk-p c)
+ then (return (code-char c))))))))))
+ (values puzzle
+ (coerce
+ (sort answers #'(lambda (x y)
+ (string< (car x) (car y))))
+ 'array)))
+
+
+(defun mark-puzzle (puzzle index answers)
+ (let* ((answer (aref answers index))
+ (start (second answer))
+ (dir (third answer))
+ (length (length (car answer)))
+ (row (car start))
+ (col (cdr start)))
+ (dotimes (i length)
+ (setf (aref puzzle row col) (cons (aref puzzle row col) nil))
+ (incf row (car dir))
+ (incf col (cdr dir)))))
+
+
+(defun unmark (puzzle row col)
+ (if* (consp (aref puzzle row col))
+ then (setf (aref puzzle row col) (car (aref puzzle row col)))
+ t))
+
+(defun words-list (words-string)
+ (do ((words nil)
+ (words-chars (coerce words-string 'list)))
+ ((null words-chars) (nreverse words))
+ (let ((word nil))
+ (loop
+ (let ((char (pop words-chars)))
+ (if* (or (null char)
+ (member char '(#\space #\newline #\tab #\return
+ #\linefeed)))
+ then (push (coerce (nreverse word) 'string) words)
+ (return)
+ else (push char word)))))))
+
+
+(defun cannot-do-puzzle (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (princ #.(format nil "~
+This page available only with International Allegro CL post 6.0 beta")
+ *html-stream*))))
+
+
+(defun can-do-puzzle (req ent)
+ (let ((puzzle-url (symbol-name (gensym "/wordpuzzle")))
+ (puzzle nil)
+ (answers nil))
+ ;; publish new url on the fly.
+ ;; Enhancement To Do: Allow puzzles to be deallocated, either by timeout
+ ;; or some other mechanism.
+
+ (publish
+ :path puzzle-url
+ :content-type "text/html; charset=utf-8"
+ :function
+ #'(lambda (req ent &aux (marked nil))
+ (let ((lookup
+ (assoc "index" (request-query req :external-format
+ :utf8-base)
+ :test #'string=)))
+ (if* lookup
+ then (setq marked t)
+ (mark-puzzle puzzle (read-from-string (cdr lookup))
+ answers)))
+ (let* ((rq (request-query req :external-format :utf8-base))
+ (words-string (cdr (assoc "words" rq :test #'string=)))
+ (fill (cdr (assoc "fill" rq :test #'string=))))
+ (if* words-string
+ then (multiple-value-setq (puzzle answers)
+ (make-puzzle (words-list words-string) fill))))
+ (with-http-response (req ent)
+ (with-http-body (req ent
+ :external-format :utf8-base)
+ (html
+ (:html
+ (:head (:title "Puzzle"))
+ (:body
+ (:p #.(format nil "~
+Characters that appear as dots or empty boxes or question-marks likely look
+that way because your browser is missing the needed font(s)."))
+ (if* puzzle
+ then (html
+ (:center
+ ((:table border 0 width "75%")
+ (:tr (:td #.(format nil "~
+Click on letter in puzzle to see its character description."))
+ (:td #.(format nil "~
+Click on word to see its puzzle location.")))
+ (:tr
+ (:td
+ ((:table border 0)
+ (dotimes (r (puzzle-rows puzzle))
+ (html
+ (:tr
+ (dotimes (c (puzzle-cols puzzle))
+ (html
+ ((:td :if* (unmark puzzle r c)
+ :bgcolor "lime")
+ ((:a href
+ (format nil "/wp_echo?char=~a"
+ (uriencode-string
+ (format
+ nil "u+~4,'0x:~s"
+ (char-code
+ (aref puzzle r c))
+ (aref puzzle r c)))))
+ (:tt (:princ
+ (aref puzzle r c))))))))))))
+ (:td
+ ((:table border 0)
+ (dotimes (i (length answers))
+ (let ((url (format nil "~a?index=~a"
+ puzzle-url i)))
+ (html
+ (:tr
+ (:td
+ ((:a href url)
+ (:princ
+ (car
+ (aref answers i)))))))))))))))
+ else (html
+ (:p "No words entered")))
+ (:p ((:a :href "/wordpuzzle") "New Puzzle"))
+ (if* marked
+ then (html
+ (:p ((:a :href puzzle-url) "Clear Answer")))))))))))
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html (:html
+ (:head (:title "Enter Words"))
+ (:body
+ (:p
+ #.(format nil "~
+Enter words separated by spaces or newlines. Click on `make puzzle' button ~
+below to generate the puzzle."))
+ ((:form :action puzzle-url
+ :method "POST")
+ ((:textarea :name "words" :rows 15 :cols 50))
+ (:dl
+ (:dt "Please select category of fill letters:")
+ (:dd ((:input :type "radio"
+ :name "fill"
+ :value "ascii-lc"
+ :checked "checked")
+ "English Lower Case Only."))
+ (:dd ((:input :type "radio"
+ :name "fill"
+ :value "unicode-nocjk")
+ "All Unicode Letters "
+ (:em "except ")
+ "Chinese-Japanese-Korean ideographs."))
+ (:dd ((:input :type "radio"
+ :name "fill"
+ :value "unicode-cjk")
+ "All Unicode Letters "
+ (:em "including ")
+ "Chinese-Japanese-Korean ideographs."))
+ (:dd ((:input :type "radio"
+ :name "fill"
+ :value "none")
+ "No fill characters.")))
+ ((:input :type "submit"
+ :value "make puzzle"))
+ (:p #.(format nil "~
+Below are links containing international character samples you can use to copy
+and paste into the word list.
+Note that even characters that don't display (due to missing fonts) can still
+be copied and pasted."))
+ (:ul (:li ((:a href #.(format nil "~
+http://www.columbia.edu/kermit/utf8.html")
+ target "_blank")
+ "UTF-8 Sampler"))
+ (:li ((:a href #.(format nil "~
+http://www.trigeminal.com/samples/provincial.html")
+ target "_blank")
+ #.(format nil "~
+The \"anyone can be provincial!\" page"))))))))))))
+
+
+;;
+;; the entry link to this demo:
+;;
+(publish
+ :path "/wordpuzzle"
+ :content-type "text/html; charset=utf-8"
+ :function
+ #-(and allegro ics (version>= 6 0 pre-final 1))
+ #'(lambda (req ent)
+ (cannot-do-puzzle req ent))
+
+
+ #+(and allegro ics (version>= 6 0 pre-final 1))
+ #'(lambda (req ent)
+ ; test at runtime in case we compiled with an international lisp
+ ; and are running in an 8bit lisp
+ (if* (member :ics *features* :test #'eq)
+ then (can-do-puzzle req ent)
+ else (cannot-do-puzzle req ent))))
+
+
+(publish
+ :path "/wp_echo"
+ :content-type "text/html; charset=utf-8"
+ :function
+ #'(lambda (req ent)
+ (let ((lookup
+ (assoc "char" (request-query req)
+ :test #'string=)))
+ (if* lookup
+ then (setq lookup
+ (let ((*read-base* 16))
+ (read-from-string
+ (subseq (cdr lookup)
+ #.(length "u+")
+ #.(length "u+xxxx"))))))
+ (with-http-response (req ent)
+ (with-http-body (req ent
+ :external-format :utf8-base)
+ (html
+ (:html
+ (:head (:title "Character Description"))
+ (:body
+ (:p
+ (:princ (format nil "Unicode value: U+~4,'0x"
+ lookup)))
+ (:p
+ "Lisp Character Name: "
+ ((:font :size "+3")
+ (:prin1 (code-char lookup))))
+ (:p
+ "Browser Font Display: "
+ ((:font :size "+3")
+ (:princ (code-char lookup)))
+ :br
+ #.(format nil "~
+Characters that appear as dots or empty boxes or question-marks likely look
+that way because your browser is missing the needed font(s)."))
+ (let ((uglyph (format nil "~
+http://charts.unicode.org/Glyphs/~2,'0x/U~4,'0x.gif"
+ (ldb (byte 8 8) lookup)
+ lookup)))
+ (html ((:table border 0)
+ (:tr
+ (:td #.(format nil "~
+Glyph GIF (from Unicode web site -- not all characters have gifs):")
+ :br
+ (:princ (format nil "[Loading from `~a'.]"
+ uglyph)))
+ (:td
+ ((:img :src uglyph
+ :alt (format nil "~s" (code-char lookup))
+ :border 2)))))))
+ (if* (cjk-p lookup)
+ then (if* (<= #xac00 lookup #xd7a3)
+ then (html (:p "Character is a Hangul Syllable."))
+ else (html (:p #.(format nil "~
+Character is an ideograph from Chinese, Japanese, or Korean.")))))
+ (:p #.(format nil "~
+Use browser `Back' button to return to puzzle."))))))))))
Added: vendor/portableaserve/aserve/examples/tutorial.cl
===================================================================
--- vendor/portableaserve/aserve/examples/tutorial.cl 2006-02-18 09:34:15 UTC (rev 1845)
+++ vendor/portableaserve/aserve/examples/tutorial.cl 2006-02-18 10:02:10 UTC (rev 1846)
@@ -0,0 +1,156 @@
+;; -*- mode: common-lisp; package: tutorial -*-
+;;
+;; turorial.cl
+;;
+;; copyright (c) 1986-2000 Franz Inc, Berkeley, CA
+;;
+;; This code is free software; you can redistribute it and/or
+;; modify it under the terms of the version 2.1 of
+;; the GNU Lesser General Public License as published by
+;; the Free Software Foundation;
+;;
+;; This code is distributed in the hope that it will be useful,
+;; but without any warranty; without even the implied warranty of
+;; merchantability or fitness for a particular purpose. See the GNU
+;; Lesser General Public License for more details.
+;;
+;; Version 2.1 of the GNU Lesser General Public License is in the file
+;; license-lgpl.txt that was distributed with this file.
+;; If it is not present, you can access it from
+;; http://www.gnu.org/copyleft/lesser.txt (until superseded by a newer
+;; version) or write to the Free Software Foundation, Inc., 59 Temple Place,
+;; Suite 330, Boston, MA 02111-1307 USA
+;;
+;;
+;; $Id: tutorial.cl,v 1.3 2002/12/26 19:55:44 rudi Exp $
+
+;; Description:
+;; iserver tutorial examples
+
+;;- This code in this file obeys the Lisp Coding Standard found in
+;;- http://www.franz.com/~jkf/coding_standards.html
+;;-
+
+(defpackage :tutorial
+ (:use :common-lisp :acl-compat.excl :net.aserve :net.html.generator))
+
+(in-package :tutorial)
+
+
+(publish :path "/hello"
+ :content-type "text/plain"
+ :function
+ #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (princ "Hello World!" *html-stream*)))))
+
+(publish :path "/hello2"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:html (:head (:title "Hello World Test"))
+ (:body
+ ((:font :color "red") "Hello ")
+ ((:font :color "blue") "World!"))))))))
+
+
+
+(publish :path "/hello-count"
+ :content-type "text/html"
+ :function
+ (let ((count 0))
+ #'(lambda (req ent)
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (html
+ (:html
+ (:head (:title "Hello Counter"))
+ (:body
+ ((:font :color (nth (random 5)
+ '("red" "blue"
+ "green" "purple" "black")))
+ "Hello World had been called "
+ (:princ (incf count))
+ " times")))))))))
+
+
+(publish :path "/queryform"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (let ((name (cdr (assoc "name" (request-query req)
+ :test #'equal))))
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (if* name
+ then ; form was filled out, just say it
+ (html (:html
+ (:head (:title "Hi to " (:princ-safe name)))
+ (:body "Your name is "
+ (:b (:princ-safe name)))))
+ else ; put up the form
+ (html (:html
+ (:head (:title "Tell me your name"))
+ (:body
+ ((:form :action "queryform")
+ "Your name is "
+ ((:input :type "text"
+ :name "name"
+ :maxlength "20"))))))))))))
+
+
+(publish :path "/charcount"
+ :content-type "text/html"
+ :function
+ #'(lambda (req ent)
+ (let* ((body (get-request-body req))
+ (text (if* body
+ then (cdr (assoc "quotation"
+ (form-urlencoded-to-query body)
+ :test #'equal)))))
+ (with-http-response (req ent)
+ (with-http-body (req ent)
+ (if* text
+ then ; got the quotation, analyze it
+ (html
+ (:html
+ (:head (:title "Character Counts")
+ (:body
+ (:table
+ (do ((i #.(char-code #\a) (1+ i)))
+ ((> i #.(char-code #\z)))
+ (html (:tr
+ (:td (:princ (code-char i)))
+ (:td (:princ
+ (count (code-char i)
+ text)))))))))))
+ else ; ask for quotation
+ (html
+ (:html
+ (:head (:title "quote character counter")
+ (:body
+ ((:form :action "charcount"
+ :method "POST")
+ "Enter your favorite quote "
+ :br
+ ((:textarea
+ :name "quotation"
+ :rows 30
+ :cols 50))
+ :br
+ ((:input :type "submit"
+ :name "submit"
+ :value "count it")))))))))))))
+
+
+
+
+
+
+
+
+
Added: vendor/portableaserve/aserve/examples/urian.cl
===================================================================
--- vendor/portableaserve/aserve/examples/urian.cl 2006-02-18 09:34:15 UTC (rev 1845)
+++ vendor/portableaserve/aserve/examples/urian.cl 2006-02-18 10:02:10 UTC (rev 1846)
@@ -0,0 +1,453 @@
+;; -*- mode: common-lisp; package: net.aserve.examples -*-
+;;
+;; urian.cl
+;;
+;; copyright (c) 1986-2000 Franz Inc, Berkeley, CA
+;;
+;; This code is free software; you can redistribute it and/or
+;; modify it under the terms of the version 2.1 of
+;; the GNU Lesser General Public License as published by
+;; the Free Software Foundation;
+;;
+;; This code is distributed in the hope that it will be useful,
+;; but without any warranty; without even the implied warranty of
+;; merchantability or fitness for a particular purpose. See the GNU
+;; Lesser General Public License for more details.
+;;
+;; Version 2.1 of the GNU Lesser General Public License is in the file
+;; license-lgpl.txt that was distributed with this file.
+;; If it is not present, you can access it from
+;; http://www.gnu.org/copyleft/lesser.txt (until superseded by a newer
+;; version) or write to the Free Software Foundation, Inc., 59 Temple Place,
+;; Suite 330, Boston, MA 02111-1307 USA
+;;
+;;
+;; $Id: urian.cl,v 1.3 2003/12/02 14:20:39 rudi Exp $
+
+;; Description:
+;; urian example
+
+
+;;
+;; Web page character analyzer.
+;; This example retrieves a web page associated with a url, parses it with
+;; parse-html, and then displays all texts found to have non-ascii characters.
+;; Each character is a link. Clicking on one of these links displays a
+;; description of the linked character.
+;;
+;; Original Author: Charles A. Cox, Franz Inc., October 2000
+;;
+;; To use, compile/load this file into Allegro CL 6.0. Then,
+;; start allegroserve, eg, (net.aserve:start :port 8000) starts on port 8000.
+;; The main published page for this example is "/urian".
+
+(defpackage :urian
+ (:use :common-lisp :excl))
+
+(in-package :urian)
+
+(eval-when (compile load eval)
+ #+allegro
+ (if* (not (featurep '(:version>= 6 0)))
+ then (error "This file not supported in Allegro CL releases earlier than 6.0"))
+ #-allegro (error "This file not supported on non-Allegro platforms"))
+
+(eval-when (compile load eval)
+ (require :aserve)
+ (handler-case (require :phtml)
+ ; didn't find it, check to see if it's where it would be in
+ ; a non-user file layout
+ (error (c)
+ (declare (ignore c))
+ (let (name)
+ (if* (or (probe-file (setq name (concatenate 'string
+ (directory-namestring *load-pathname*)
+ "../xmlutils/phtml.fasl")))
+ (probe-file (setq name (concatenate 'string
+ (directory-namestring *load-pathname*)
+ "../../xmlutils/phtml.fasl"))))
+
+ then (load name)
+ else (format t " not at ~s~%, tn is ~s~%" name
+ *load-pathname*)
+ (error "can't locate phtml module"))))))
+
+(defpackage :urian
+ (:use :net.html.generator :net.aserve :net.html.parser))
+
+(pushnew :x-sjis (ef-nicknames (find-external-format :shiftjis)))
+(pushnew :shift-jis (ef-nicknames (find-external-format :shiftjis)))
+(pushnew :iso-8859-1 (ef-nicknames (find-external-format :latin1)))
+(pushnew :windows-1252 (ef-nicknames (find-external-format :1252)))
+
+(defparameter *blocks*
+ '((#x0000 #x007f "Basic Latin")
+ (#x0080 #x00ff "Latin-1 Supplement")
+ (#x0100 #x017f "Latin Extended-A")
+ (#x0180 #x024f "Latin Extended-B")
+ (#x0250 #x02af "IPA Extensions")
+ (#x02b0 #x02ff "Spacing Modifier Letters")
+ (#x0300 #x036f "Combining Diacritical Marks")
+ (#x0370 #x03ff "Greek")
+ (#x0400 #x04ff "Cyrillic")
+ (#x0530 #x058f "Armenian")
+ (#x0590 #x05ff "Hebrew")
+ (#x0600 #x06ff "Arabic")
+ (#x0700 #x074f "Syriac ")
+ (#x0780 #x07bf "Thaana")
+ (#x0900 #x097f "Devanagari")
+ (#x0980 #x09ff "Bengali")
+ (#x0a00 #x0a7f "Gurmukhi")
+ (#x0a80 #x0aff "Gujarati")
+ (#x0b00 #x0b7f "Oriya")
+ (#x0b80 #x0bff "Tamil")
+ (#x0c00 #x0c7f "Telugu")
+ (#x0c80 #x0cff "Kannada")
+ (#x0d00 #x0d7f "Malayalam")
+ (#x0d80 #x0dff "Sinhala")
+ (#x0e00 #x0e7f "Thai")
+ (#x0e80 #x0eff "Lao")
+ (#x0f00 #x0fff "Tibetan")
+ (#x1000 #x109f "Myanmar ")
+ (#x10a0 #x10ff "Georgian")
+ (#x1100 #x11ff "Hangul Jamo")
+ (#x1200 #x137f "Ethiopic")
+ (#x13a0 #x13ff "Cherokee")
+ (#x1400 #x167f "Unified Canadian Aboriginal Syllabics")
+ (#x1680 #x169f "Ogham")
+ (#x16a0 #x16ff "Runic")
+ (#x1780 #x17ff "Khmer")
+ (#x1800 #x18af "Mongolian")
+ (#x1e00 #x1eff "Latin Extended Additional")
+ (#x1f00 #x1fff "Greek Extended")
+ (#x2000 #x206f "General Punctuation")
+ (#x2070 #x209f "Superscripts and Subscripts")
+ (#x20a0 #x20cf "Currency Symbols")
+ (#x20d0 #x20ff "Combining Marks for Symbols")
+ (#x2100 #x214f "Letterlike Symbols")
+ (#x2150 #x218f "Number Forms")
+ (#x2190 #x21ff "Arrows")
+ (#x2200 #x22ff "Mathematical Operators")
+ (#x2300 #x23ff "Miscellaneous Technical")
+ (#x2400 #x243f "Control Pictures")
+ (#x2440 #x245f "Optical Character Recognition")
+ (#x2460 #x24ff "Enclosed Alphanumerics")
+ (#x2500 #x257f "Box Drawing")
+ (#x2580 #x259f "Block Elements")
+ (#x25a0 #x25ff "Geometric Shapes")
+ (#x2600 #x26ff "Miscellaneous Symbols")
+ (#x2700 #x27bf "Dingbats")
+ (#x2800 #x28ff "Braille Patterns")
+ (#x2e80 #x2eff "CJK Radicals Supplement")
+ (#x2f00 #x2fdf "Kangxi Radicals")
+ (#x2ff0 #x2fff "Ideographic Description Characters")
+ (#x3000 #x303f "CJK Symbols and Punctuation")
+ (#x3040 #x309f "Hiragana")
+ (#x30a0 #x30ff "Katakana")
+ (#x3100 #x312f "Bopomofo")
+ (#x3130 #x318f "Hangul Compatibility Jamo")
+ (#x3190 #x319f "Kanbun")
+ (#x31a0 #x31bf "Bopomofo Extended")
+ (#x3200 #x32ff "Enclosed CJK Letters and Months")
+ (#x3300 #x33ff "CJK Compatibility")
+ (#x3400 #x4db5 "CJK Unified Ideographs Extension A")
+ (#x4e00 #x9fff "CJK Unified Ideographs")
+ (#xa000 #xa48f "Yi Syllables")
+ (#xa490 #xa4cf "Yi Radicals")
+ (#xac00 #xd7a3 "Hangul Syllables")
+ (#xd800 #xdb7f "High Surrogates")
+ (#xdb80 #xdbff "High Private Use Surrogates")
+ (#xdc00 #xdfff "Low Surrogates")
+ (#xe000 #xf8ff "Private Use")
+ (#xf900 #xfaff "CJK Compatibility Ideographs")
+ (#xfb00 #xfb4f "Alphabetic Presentation Forms")
+ (#xfb50 #xfdff "Arabic Presentation Forms-A")
+ (#xfe20 #xfe2f "Combining Half Marks")
+ (#xfe30 #xfe4f "CJK Compatibility Forms")
+ (#xfe50 #xfe6f "Small Form Variants")
+ (#xfe70 #xfefe "Arabic Presentation Forms-B")
+ (#xfeff #xfeff "Specials")
+ (#xff00 #xffef "Halfwidth and Fullwidth Forms")
+ (#xfff0 #xfffd "Specials")))
+
+(publish
+ :path "/urian"
+ :content-type "text/html; charset=utf-8"
+ :function
+ #'(lambda (req ent)
+ (let* ((uri (cdr (assoc "uri" (request-query req) :test #'equal)))
+ (results nil))
+ (when uri
+ (unless (find #\: uri)
+ (setq uri (concatenate 'string "http://" uri)))
+ (setq results (chanal uri)))
+ (with-http-response (req ent)
+ (with-http-body (req ent
+ :external-format :utf8-base)
+ (html
+ (:html
+ (:head (:title (:princ-safe
+ (format nil "String Analysis~@[ for `~a'~]"
+ uri))))
+ (:body
+ (if* (stringp results)
+ then (html (:p "AllegroServe got error: "
+ (:b (:princ-safe results))))
+ else (when results
+ (when (first results)
+ (html
+ (:p (:princ-safe
+ (format nil "Server set charset to `~s'."
+ (car (first results))))
+ :br
+ (:princ-safe
+ (format nil "Switched to External-Format `~s'."
+ (ef-name (cdr (first results))))))))
+ (when (second results)
+ (html
+ (:p (:princ-safe
+ (format
+ nil
+ "A page meta tag specified charset as `~s'."
+ (car (second results))))
+ :br
+ (:princ-safe
+ (format
+ nil "Switched to external-format: `~s'."
+ (ef-name (cdr (second results))))))))
+ (html (:p "Scanned URL: " ((:a :href uri
+ target "_blank")
+ (:princ-safe uri))))
+ (if* (cddr results)
+ then (html
+ (:p
+ "The following texts were found to contain "
+ "non-ASCII characters. "
+ :br
+ "Click on a character for its description."))
+ "Strings found on URL: "
+ (dolist (result (cddr results))
+ (html
+ :hr
+ (san-html result *html-stream*)))
+ else (html
+ (:p
+ "No texts containing non-ASCII characters "
+ "were found on the page.")))))
+ :hr
+ (macrolet ((item (title url)
+ ;; Assumes title and url are string literals
+ (let ((ref (format nil "/urian?uri=~a"
+ (uriencode-string url))))
+ `(html
+ (:ul (:li (:princ-safe ,title)
+ " ("
+ (:princ-safe ,url)
+ ")"
+ :br
+ ((:a href ,url
+ target "_blank")
+ "View Page (new browser window)")
+ :br
+ ((:a href ,ref) "Analyze")))))))
+ (html
+ (:p
+ "Select a sample page:"
+ (item "UTF-8 Sampler"
+ "http://www.columbia.edu/kermit/utf8.html")
+ (item "The \"anyone can be provincial!\" page"
+ "http://www.trigeminal.com/samples/provincial.html")
+ (item "The Japan Netscape Netcenter Page"
+ "http://home.netscape.com/ja")
+ (item "The Spain Yahoo! Page"
+ "http://es.yahoo.com"))))
+ :br
+ ((:form :action "urian"
+ :method "get")
+ "Or Enter New URL to analyze: "
+ ((:input :type "text" :name "uri" :size 50)))))))))))
+
+(defun san-html (string stream)
+ (net.html.generator:html-stream
+ stream
+ (net.html.generator:html
+ (:p "\""
+ (dotimes (i (length string))
+ (net.html.generator:html
+ ((:a href
+ (format nil "/chdescribe?char=~a"
+ (net.aserve:uriencode-string
+ (format nil "u+~4,'0x:~s"
+ (char-code
+ (schar string i))
+ (schar string i)))))
+ (:princ (schar string i)))))
+ "\""))))
+
+(defun chanal (uri
+ &aux (server-ef nil)
+ (lhtml nil)
+ (metatag-ef nil))
+ (handler-case
+ (multiple-value-bind (body response-code headers ruri)
+ (net.aserve.client:do-http-request uri :external-format :latin1-base)
+ (declare (ignore response-code ruri))
+ (setq server-ef (let ((content-type (cdr (assoc :content-type
+ headers))))
+ (find-charset-from-content-type content-type)))
+ (setq lhtml (net.html.parser:parse-html body))
+ (setq metatag-ef (update-ef lhtml))
+ (cons server-ef
+ (cons metatag-ef
+ (delete-duplicates
+ (chanal-body lhtml (or (cdr metatag-ef)
+ (cdr server-ef)
+ ;; www.yahoo.co.jp uses euc without
+ ;; specifying it. Let's try using
+ ;; euc, then, as default.
+ (crlf-base-ef
+ (find-external-format :latin1))))
+ :test #'string=))))
+ (error (c)
+ (format nil "~a" c))))
+
+(defun chanal-body (body ef)
+ (if* (stringp body)
+ then (let ((s (octets-to-string
+ (string-to-octets body :external-format :latin1-base)
+ :external-format ef)))
+ (dotimes (i (length s))
+ (when (> (char-code (schar s i)) #x7f)
+ ;; non-ascii
+ (return-from chanal-body (list s))))
+ nil)
+ elseif (consp body)
+ then ;; skip unparsed
+
+
From bknr at bknr.net Sun Feb 19 19:05:08 2006
From: bknr at bknr.net (bknr at bknr.net)
Date: Sun, 19 Feb 2006 13:05:08 -0600 (CST)
Subject: [bknr-cvs] r1864 -
branches/xml-class-rework/projects/lisp-ecoop/website/templates
Message-ID: <20060219190508.6171D2A01A@common-lisp.net>
Author: hhubner
Date: 2006-02-19 13:05:05 -0600 (Sun, 19 Feb 2006)
New Revision: 1864
Modified:
branches/xml-class-rework/projects/lisp-ecoop/website/templates/toplevel.xml
Log:
yet another absolute path fixed
Modified: branches/xml-class-rework/projects/lisp-ecoop/website/templates/toplevel.xml
===================================================================
--- branches/xml-class-rework/projects/lisp-ecoop/website/templates/toplevel.xml 2006-02-19 18:59:00 UTC (rev 1863)
+++ branches/xml-class-rework/projects/lisp-ecoop/website/templates/toplevel.xml 2006-02-19 19:05:05 UTC (rev 1864)
@@ -19,7 +19,7 @@
Co-located with ECOOP 2006 July 3-4 - Nantes - France
\ No newline at end of file
From bknr at bknr.net Mon Feb 20 19:42:24 2006
From: bknr at bknr.net (bknr at bknr.net)
Date: Mon, 20 Feb 2006 13:42:24 -0600 (CST)
Subject: [bknr-cvs] r1866 -
branches/xml-class-rework/projects/lisp-ecoop/website/templates
Message-ID: <20060220194224.3643F7B011@common-lisp.net>
Author: hhubner
Date: 2006-02-20 13:42:23 -0600 (Mon, 20 Feb 2006)
New Revision: 1866
Added:
branches/xml-class-rework/projects/lisp-ecoop/website/templates/registration.xml
Removed:
branches/xml-class-rework/projects/lisp-ecoop/website/templates/home.xml
Modified:
branches/xml-class-rework/projects/lisp-ecoop/website/templates/menu.xml
Log:
Rename home -> registration.
Deleted: branches/xml-class-rework/projects/lisp-ecoop/website/templates/home.xml
===================================================================
--- branches/xml-class-rework/projects/lisp-ecoop/website/templates/home.xml 2006-02-20 05:47:13 UTC (rev 1865)
+++ branches/xml-class-rework/projects/lisp-ecoop/website/templates/home.xml 2006-02-20 19:42:23 UTC (rev 1866)
@@ -1,48 +0,0 @@
-
-
-
-
-
Welcome to the Website of the 3rd European Lisp
-Workshop. Please see the Call for Participation for a
-description of the goals of the workshop.
-
-
News
-
-
February 20, 2006
-
-
-
Launched the workshop website.
-
-
-
Information for Attendees
-
-
Registration of workshop participants has to be done in three
-mandatory steps:
-
-
Contact the organizers of the workshop (in order to
-ensure that the participant limit has not been exceeded).
-
Advance registration to the workshop is handled through the
-primary contact person of the workshop, Pascal Costanza (pc at p-cos.net). Please contact him by
-regular email with your submission or input to the workshop in order
-to receive your login to the workshop website. Include your preferred
-login name for the workshop website with your application.
-
Register on the ECOOP 2006 web
-site either as a
-worskhop-only attendee or as a regular attendee. The
-latter includes access to workshops and to the main conference.