[bknr-cvs] hans changed trunk/libraries/yason/index.html
BKNR Commits
bknr at bknr.net
Sat Nov 15 10:12:08 UTC 2008
Revision: 4050
Author: hans
URL: http://bknr.net/trac/changeset/4050
Add HTML rendered version of the documentation.
A trunk/libraries/yason/index.html
Added: trunk/libraries/yason/index.html
===================================================================
--- trunk/libraries/yason/index.html (rev 0)
+++ trunk/libraries/yason/index.html 2008-11-15 10:12:08 UTC (rev 4050)
@@ -0,0 +1,477 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Strict//EN">
+<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>YASON - A JSON encoder/decoder for Common Lisp</title><meta name="description" content="
+ YASON is a JSON encoding and decoding library for Common Lisp.
+ It provides for functions to read JSON strings into Lisp data
+ structures and for serializing Lisp data structures as JSON
+ strings.
+ "></meta><style type="text/css">
+ body { background-color: #ffffff; max-width: 50em; margin-left: 2em; font-family: Georgia, 'Times New Roman', serif; }
+ pre { padding:5px; background-color:#e0e0e0; margin: 1em 2em 1em 2em; }
+ pre.none { padding:5px; background-color:#ffffff; }
+ h3, h4, h5 { text-decoration: underline; }
+ a { text-decoration: none; padding: 1px 2px 1px 2px; }
+ a:visited { text-decoration: none; padding: 1px 2px 1px 2px; }
+ a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; }
+ a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; }
+ a.none { text-decoration: none; padding: 0; }
+ a.none:visited { text-decoration: none; padding: 0; }
+ a.none:hover { text-decoration: none; border: none; padding: 0; }
+ a.none:focus { text-decoration: none; border: none; padding: 0; }
+ a.noborder { text-decoration: none; padding: 0; }
+ a.noborder:visited { text-decoration: none; padding: 0; }
+ a.noborder:hover { text-decoration: none; border: none; padding: 0; }
+ a.noborder:focus { text-decoration: none; border: none; padding: 0; }
+ table { margin: 1em; }
+ </style></head><body>
+
+ <h2 xmlns="">YASON - A JSON encoder/decoder for Common Lisp</h2>
+
+
+
+ <h3 xmlns="">Abstract</h3>
+<blockquote xmlns="">
+ YASON is a Common Lisp library for encoding and decoding data in
+ the <a xmlns="http://www.w3.org/1999/xhtml" href="http://json.org/">JSON</a> interchange format.
+ JSON is used in AJAX applications as a lightweight alternative
+ to XML. YASON has the sole purpose of encoding and decoding
+ data and does not impose any object model on the Common Lisp
+ application that uses it.
+ </blockquote>
+
+ <h3 xmlns="">Contents</h3>
+<ol xmlns="">
+<li><a href="#intro">Introduction</a></li>
+<li><a href="#install">Installation</a></li>
+<li><a href="#mapping">Mapping between JSON and CL datatypes</a></li>
+<li>
+<a href="#parsing">Parsing JSON data</a><ol><li><a href="#parser-dict">Parser dictionary</a></li></ol>
+</li>
+<li>
+<a href="#encoding">Encoding JSON data</a><ol>
+<li><a href="#dom-encoder">Encoding a JSON DOM</a></li>
+<li><a href="#stream-encoder">Encoding JSON in streaming mode</a></li>
+<li><a href="#app-encoders">Application specific encoders</a></li>
+</ol>
+</li>
+<li><a href="#index">Symbol index</a></li>
+<li><a href="#license">License</a></li>
+<li><a href="#ack">Acknowledgements</a></li>
+</ol>
+
+ <h3 xmlns=""><a class="none" name="intro">Introduction</a></h3>
+ <a href="http://json.org/">JSON</a> is an established alternative
+ to XML as a data interchange format for web applications. YASON
+ implements reading and writing of JSON formatted data in Common
+ Lisp. It does not attempt to provide a mapping between CLOS
+ objects and YASON, but can be used to implement such mappings.
+
+
+ <h3 xmlns=""><a class="none" name="install">Installation</a></h3>
+ <p>
+ YASON has its permanent home
+ at <a href="http://common-lisp.net/project/yason/">common-lisp.net</a>.
+ It can be obtained either by downloading
+ the <a href="http://common-lisp.net/project/yason/files/yason.tar.gz">release
+ tarball</a> or by checking out the current development version
+ from its subversion repository:
+ <pre>svn co svn://bknr.net/svn/trunk/libraries/yason/</pre>
+ </p>
+ <p>
+ YASON is written in ANSI Common Lisp and does not depend on
+ other libraries.
+ </p>
+ <p>
+ YASON lives in the <b>:yason</b> package and creates a package nickname
+ <b>:json</b>. Applications will not normally <b>:use</b> this
+ package, but rather use qualified names to access YASON's
+ symbols. For that reason, YASON's symbols do not contain the
+ string "JSON" themselves. See below for usage samples.
+ </p>
+
+
+ <h3 xmlns=""><a class="none" name="mapping">Mapping between JSON and CL datatypes</a></h3>
+ By default, YASON performs the following mappings between JSON and
+ CL datatypes:
+ <table border="1">
+ <thead>
+ <tr>
+ <th>JSON<br></br>datatype</th>
+ <th>CL<br></br>datatype</th>
+ <th>Notes</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>object</td>
+ <td>hash-table<br></br>:test #'equal</td>
+ <td>
+ Keys are strings by default,
+ see <code xmlns=""><a href="#*parse-object-key-fn*">*parse-object-key-fn*</a></code>
+ </td>
+ </tr>
+ <tr>
+ <td>array</td>
+ <td>list</td>
+ <td>
+ Can be changed to read to vectors,
+ see <code xmlns=""><a href="#*parse-json-arrays-as-vectors*">*parse-json-arrays-as-vectors*</a></code>
+ </td>
+ </tr>
+ <tr>
+ <td>string</td>
+ <td>string</td>
+ <td>
+ JSON escape characters are recognized upon reading.
+ Upon writing, known escape characters are used, but
+ non-ASCII Unicode characters are written as is.
+ </td>
+ </tr>
+ <tr>
+ <td>number</td>
+ <td>number</td>
+ <td>
+ Parsed with READ, printed with PRINC. This is not a
+ faithful implementation of the specification.
+ </td>
+ </tr>
+ <tr>
+ <td>true</td>
+ <td>t</td>
+ <td>Can be changed to read as TRUE, see <code xmlns=""><a href="#*parse-json-booleans-as-symbols*">*parse-json-booleans-as-symbols*</a></code></td>
+ </tr>
+ <tr>
+ <td>false</td>
+ <td>nil</td>
+ <td>Can be changed to read as FALSE, see <code xmlns=""><a href="#*parse-json-booleans-as-symbols*">*parse-json-booleans-as-symbols*</a></code></td>
+ </tr>
+ <tr>
+ <td>null</td>
+ <td>nil</td>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+
+
+ <h3 xmlns=""><a class="none" name="parsing">Parsing JSON data</a></h3>
+ <p>
+ JSON data is always completely parsed into an equivalent
+ in-memory representation. Upon reading, some translations are
+ performed by default to make it easier for the Common Lisp
+ program to work with the data; see <code xmlns=""><a href="#mapping">mapping</a></code>
+ for details. If desired, the parser can be configured to
+ preserve the full semantics of the JSON data read.
+ </p>
+
+ For example
+
+ <pre>CL-USER> (defvar *json-string* "[{\"foo\":1,\"bar\":[7,8,9]},2,3,4,[5,6,7],true,null]")
+*JSON-STRING*
+CL-USER> (let* ((result (json:parse *json-string*)))
+ (print result)
+ (alexandria:hash-table-plist (first result)))
+
+(#<HASH-TABLE :TEST EQUAL :COUNT 2 {5A4420F1}> 2 3 4 (5 6 7) T NIL)
+("bar" (7 8 9) "foo" 1)
+CL-USER> (defun maybe-convert-to-keyword (js-name)
+ (or (find-symbol (string-upcase js-name) :keyword)
+ js-name))
+MAYBE-CONVERT-TO-KEYWORD
+CL-USER> :FOO ; intern the :FOO keyword
+:FOO
+CL-USER> (let* ((json:*parse-json-arrays-as-vectors* t)
+ (json:*parse-json-booleans-as-symbols* t)
+ (json:*parse-object-key-fn* #'maybe-convert-to-string)
+ (result (json:parse *json-string*)))
+ (print result)
+ (alexandria:hash-table-plist (aref result 0)))
+
+#(#<HASH-TABLE :TEST EQUAL :COUNT 2 {59B4EAD1}> 2 3 4 #(5 6 7) YASON:TRUE NIL)
+("bar" #(7 8 9) :FOO 1)</pre>
+
+ <p>
+ The second example modifies the parser's behaviour so that JSON
+ arrays are read as CL vectors, JSON booleans will be read as the
+ symbols TRUE and FALSE and JSON object keys will be looked up in
+ the <b>:keyword</b> package. Interning strings coming from an
+ external source is not recommended practice.
+ </p>
+
+ <h4 xmlns=""><a name="parser-dict">Parser dictionary</a></h4>
+ <p xmlns="">[Generic function]<br><a class="none" name="parse"><b>parse</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">input</clix:lambda-list></i>
+ =>
+ <i>object</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Parse <code><i>input</i></code>, which needs to be a string
+ or a stream, as JSON. Returns the lisp representation of the
+ JSON structure parsed.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">
+ [Special variable]<br><a class="none" name="*parse-json-arrays-as-vectors*"><b>*parse-json-arrays-as-vectors*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ If set to a true value, JSON arrays will be parsed as vectors,
+ not as lists.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">
+ [Special variable]<br><a class="none" name="*parse-json-booleans-as-symbols*"><b>*parse-json-booleans-as-symbols*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ If set to a true value, JSON booleans will be read as the
+ symbols TRUE and FALSE, not as T and NIL, respectively.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">
+ [Special variable]<br><a class="none" name="*parse-object-key-fn*"><b>*parse-object-key-fn*</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Function to call to convert a key string in a JSON array to a
+ key in the CL hash produced.
+ </clix:description></blockquote></p>
+
+
+
+ <h3 xmlns=""><a class="none" name="encoding">Encoding JSON data</a></h3>
+ YASON provides for two distinct modes to encode JSON data:
+ Applications can either create an in memory representation of the
+ data to be serialized, then have YASON convert it to JSON in one
+ go, or they can use a set of macros to serialze the JSON data
+ element-by-element, thereby having fine grained control over the
+ layout of the generated data.
+
+ <h4 xmlns=""><a name="dom-encoder">Encoding a JSON DOM</a></h4>
+ <p>
+ In this mode, an in-memory structure is encoded in JSON format.
+ The structure must consist of objects that are serializable
+ using the <code xmlns=""><a href="#encode">ENCODE</a></code> function. YASON defines a
+ number of encoders for standard data types
+ (see <code xmlns=""><a href="#mapping">MAPPING</a></code>), but the application can
+ define additional methods, e.g. for encoding CLOS objects.
+ </p>
+ For example:
+ <pre>CL-USER> (json:encode
+ (list (alexandria:plist-hash-table
+ '("foo" 1 "bar" (7 8 9))
+ :test #'equal)
+ 2 3 4
+ '(5 6 7)
+ t nil)
+ *standard-output*)
+[{"foo":1,"bar":[7,8,9]},2,3,4,[5,6,7],true,null]
+(#<HASH-TABLE :TEST EQUAL :COUNT 2 {59942D21}> 2 3 4 (5 6 7) T NIL)</pre>
+
+ <h4 xmlns=""><a name="dom-encoder-dict">DOM encoder dictionary</a></h4>
+ <p xmlns="">[Generic function]<br><a class="none" name="encode"><b>encode</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object &optional stream</clix:lambda-list></i>
+ =>
+ <i>object</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Encode <code><i>object</i></code>
+ to <code><i>stream</i></code> in JSON format. May be
+ specialized by applications to perform specific
+ rendering. <code><i>stream</i></code> defaults to
+ *STANDARD-OUTPUT*.
+ </clix:description></blockquote></p>
+
+
+
+ <h4 xmlns=""><a name="stream-encoder">Encoding JSON in streaming mode</a></h4>
+ <p>
+ In this mode, the JSON structure is generated in a stream.
+ The application makes explicit calls to the encoding library
+ in order to generate the JSON structure. It provides for more
+ control over the generated output, and can be used to generate
+ arbitary JSON without requiring that there exists a directly
+ matching Lisp datastructure. The streaming API uses
+ the <code xmlns=""><a href="#encode">encode</a></code> function, so it is possible to
+ intermix the two. See <code xmlns=""><a href="#app-encoders">app-encoders</a></code> for
+ an example.
+ </p>
+ For example:
+ <pre>CL-USER> (json:with-output (*standard-output*)
+ (json:with-array ()
+ (dotimes (i 3)
+ (json:encode-array-element i))))
+[0,1,2]
+NIL
+CL-USER> (json:with-output (*standard-output*)
+ (json:with-object ()
+ (json:encode-object-element "hello" "hu hu")
+ (json:with-object-element ("harr")
+ (json:with-array ()
+ (dotimes (i 3)
+ (json:encode-array-element i))))))
+{"hello":"hu hu","harr":[0,1,2]}
+NIL</pre>
+
+ <h4 xmlns=""><a name="stream-encoder-dict">Streaming encoder dictionary</a></h4>
+ <p xmlns="">[Macro]<br><a class="none" name="with-output"><b>with-output</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">(stream) &body body</clix:lambda-list></i>
+ =>
+ <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Set up a JSON streaming encoder context
+ on <code><i>stream</i></code>, then
+ evaluate <code><i>body</i></code>.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">[Macro]<br><a class="none" name="with-output-to-string*"><b>with-output-to-string*</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">() &body body</clix:lambda-list></i>
+ =>
+ <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Set up a JSON streaming encoder context, then
+ evaluate <code><i>body</i></code>. Return a string with the
+ generated JSON output.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">
+ [Condition type]<br><a class="none" name="no-json-output-context"><b>no-json-output-context</b></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ This condition is signalled when one of the stream encoding
+ function is used outside the dynamic context of a
+ <code><a href="#with-output">WITH-OUTPUT</a></code> or
+ <code><a href="#with-output-to-string*">WITH-OUTPUT-TO-STRING*</a></code> body.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">[Macro]<br><a class="none" name="with-array"><b>with-array</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">() &body body</clix:lambda-list></i>
+ =>
+ <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Open a JSON array, then run <code><i>body</i></code>. Inside
+ the body, <code><a href="#encode-array-element">ENCODE-ARRAY-ELEMENT</a></code> must be
+ called to encode elements to the opened array. Must be called
+ within an existing JSON encoder context, see
+ <code><a href="#with-output">WITH-OUTPUT</a></code> and
+ <code><a href="#with-output-to-string*">WITH-OUTPUT-TO-STRING*</a></code>.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">[Function]<br><a class="none" name="encode-array-element"><b>encode-array-element</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">object</clix:lambda-list></i>
+ =>
+ <i>object</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Encode <code><i>object</i></code> as next array element to
+ the last JSON array opened
+ with <code><a href="#with-array">WITH-ARRAY</a></code> in the dynamic
+ context. <code><i>object</i></code> is encoded using the
+ <code><a href="#encode">ENCODE</a></code> generic function, so it must be of
+ a type for which an <code><a href="#encode">ENCODE</a></code> method is
+ defined.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">[Macro]<br><a class="none" name="with-object"><b>with-object</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">() &body body</clix:lambda-list></i>
+ =>
+ <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Open a JSON object, then run <code><i>body</i></code>. Inside the body,
+ <code><a href="#encode-object-element">ENCODE-OBJECT-ELEMENT</a></code>
+ or <code><a href="#with-object-element">WITH-OBJECT-ELEMENT</a></code> must be called to
+ encode elements to the object. Must be called within an
+ existing JSON encoder context,
+ see <code><a href="#with-output">WITH-OUTPUT</a></code>
+ and <code><a href="#with-output-to-string*">WITH-OUTPUT-TO-STRING*</a></code>.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">[Macro]<br><a class="none" name="with-object-element"><b>with-object-element</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">(key) &body body</clix:lambda-list></i>
+ =>
+ <i>result*</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Open a new encoding context to encode a JSON object
+ element. <code><i>key</i></code> is the key of the element.
+ The value will be whatever <code><i>body</i></code>
+ serializes to the current JSON output context using one of the
+ stream encoding functions. This can be used to stream out
+ nested object structures.
+ </clix:description></blockquote></p>
+
+ <p xmlns="">[Function]<br><a class="none" name="encode-object-element"><b>encode-object-element</b> <i><clix:lambda-list xmlns:clix="http://bknr.net/clixdoc">key value</clix:lambda-list></i>
+ =>
+ <i>value</i></a><blockquote><clix:description xmlns:clix="http://bknr.net/clixdoc">
+ Encode <code><i>key</i></code> and <code><i>value</i></code>
+ as object element to the last JSON object opened
+ with <code><a href="#with-object">WITH-OBJECT</a></code> in the dynamic
+ context. <code><i>key</i></code>
+ and <code><i>value</i></code> are encoded using
+ the <code><a href="#encode">ENCODE</a></code> generic function, so they both
+ must be of a type for which an <code><a href="#encode">ENCODE</a></code>
+ method is defined.
+ </clix:description></blockquote></p>
+
+
+
+ <h4 xmlns=""><a name="app-encoders">Application specific encoders</a></h4>
+
+ Suppose your application uses structs to represent its data, and
+ you want to encode such structs using JSON in order to send it
+ to a client application. Suppose further that your structs also
+ include internal information that you do not want to send. Here
+ is some code that illustrates how one could implement a
+ serialization function:
+
+ <pre>CL-USER> (defstruct user name age password)
+USER
+CL-USER> (defmethod json:encode ((user user) &optional (stream *standard-output*))
+ (json:with-output (stream)
+ (json:with-object ()
+ (json:encode-object-element "name" (user-name user))
+ (json:encode-object-element "age" (user-age user)))))
+#<STANDARD-METHOD YASON:ENCODE (USER) {5B40A591}>
+CL-USER> (json:encode (list (make-user :name "horst" :age 27 :password "puppy")
+ (make-user :name "uschi" :age 28 :password "kitten")))
+[{"name":"horst","age":27},{"name":"uschi","age":28}]
+(#S(USER :NAME "horst" :AGE 27 :PASSWORD "puppy")
+ #S(USER :NAME "uschi" :AGE 28 :PASSWORD "kitten"))</pre>
+
+ As you can see, the streaming API and the DOM encoder can be
+ used together. <code xmlns=""><a href="#encode">ENCODE</a></code> invokes itself
+ recursively, so any application defined method will be called
+ while encoding in-memory objects as appropriate.
+
+
+
+
+ <h3 xmlns=""><a class="none" name="index">Symbol index</a></h3>
+ <ul xmlns="">
+<li><code><a href="#*parse-json-arrays-as-vectors*">*parse-json-arrays-as-vectors*</a></code></li>
+<li><code><a href="#*parse-json-booleans-as-symbols*">*parse-json-booleans-as-symbols*</a></code></li>
+<li><code><a href="#*parse-object-key-fn*">*parse-object-key-fn*</a></code></li>
+<li><code><a href="#encode">encode</a></code></li>
+<li><code><a href="#encode-array-element">encode-array-element</a></code></li>
+<li><code><a href="#encode-object-element">encode-object-element</a></code></li>
+<li><code><a href="#no-json-output-context">no-json-output-context</a></code></li>
+<li><code><a href="#parse">parse</a></code></li>
+<li><code><a href="#with-array">with-array</a></code></li>
+<li><code><a href="#with-object">with-object</a></code></li>
+<li><code><a href="#with-object-element">with-object-element</a></code></li>
+<li><code><a href="#with-output">with-output</a></code></li>
+<li><code><a href="#with-output-to-string*">with-output-to-string*</a></code></li>
+</ul>
+
+
+ <h3 xmlns=""><a class="none" name="license">License</a></h3>
+ <pre class="none">Copyright (c) 2008 Hans Hübner
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ - Neither the name BKNR nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+</pre>
+
+
+ <h3 xmlns=""><a class="none" name="ack">Acknowledgements</a></h3>
+ Thanks go to Edi Weitz for being a great inspiration. This
+ documentation as been generated with a hacked-up version of
+ his <a href="http://weitz.de/documentation-template/">DOCUMENTATION-TEMPLATE</a>
+ software.
+
+
+</body></html>
More information about the Bknr-cvs
mailing list