index.html
  1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
2 <html>
3
4 <head>
5 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
6 <title>CL-WHO - Yet another Lisp markup language</title>
7 <style type="text/css">
8 pre { padding:5px; background-color:#e0e0e0 }
9 h3, h4 { text-decoration: underline; }
10 a { text-decoration: none; padding: 1px 2px 1px 2px; }
11 a:visited { text-decoration: none; padding: 1px 2px 1px 2px; }
12 a:hover { text-decoration: none; padding: 1px 1px 1px 1px; border: 1px solid #000000; }
13 a:focus { text-decoration: none; padding: 1px 2px 1px 2px; border: none; }
14 a.none { text-decoration: none; padding: 0; }
15 a.none:visited { text-decoration: none; padding: 0; }
16 a.none:hover { text-decoration: none; border: none; padding: 0; }
17 a.none:focus { text-decoration: none; border: none; padding: 0; }
18 a.noborder { text-decoration: none; padding: 0; }
19 a.noborder:visited { text-decoration: none; padding: 0; }
20 a.noborder:hover { text-decoration: none; border: none; padding: 0; }
21 a.noborder:focus { text-decoration: none; border: none; padding: 0; }
22 pre.none { padding:5px; background-color:#ffffff }
23 </style>
24 </head>
25
26 <body bgcolor=white>
27
28 <h2>CL-WHO - Yet another Lisp markup language</h2>
29
30 <blockquote>
31 <br>&nbsp;<br><h3><a name=abstract class=none>Abstract</a></h3>
32
33 There are plenty of <a
34 href="http://www.cliki.net/Lisp%20Markup%20Languages">Lisp Markup
35 Languages</a> out there - every Lisp programmer seems to write at
36 least one during his career - and CL-WHO (where <em>WHO</em> means
37 &quot;with-html-output&quot; for want of a better acronym) is probably
38 just as good or bad as the next one. They are all more or less similar
39 in that they provide convenient means to convert S-expressions
40 intermingled with code into (X)HTML, XML, or whatever but differ with
41 respect to syntax, implementation, and API. So, if you haven't made a
42 choice yet, check out the alternatives as well before you begin to use
43 CL-WHO just because it was the first one you came across. (Was that
44 repelling enough?) If you're looking for a slightly different approach
45 you might also want to look at <a
46 href="http://weitz.de/html-template/">HTML-TEMPLATE</a>.
47 <p>
48 I wrote this one in 2002 although at least Tim Bradshaw's <a
49 href="http://www.cliki.net/htout">htout</a> and <a
50 href="http://opensource.franz.com/aserve/aserve-dist/doc/htmlgen.html">AllegroServe's
51 HTML generation facilities</a> by John Foderaro of Franz Inc. were
52 readily available. Actually, I don't remember why I had to write my
53 own library - maybe just because it was fun and didn't take very long. The
54 syntax was obviously inspired by htout although it is slightly
55 different.
56 <p>
57 CL-WHO tries to create efficient code in that it makes constant
58 strings as long as possible. In other words, the code generated by the
59 CL-WHO macros will usually be a sequence of <code>WRITE-STRING</code>
60 forms for constant parts of the output interspersed with arbitrary
61 code inserted by the user of the macro. CL-WHO will make sure that
62 there aren't two adjacent <code>WRITE-STRING</code> forms with
63 constant strings. CL-WHO's output is
64 either XHTML (default), 'plain' (SGML) HTML or HTML5 (using HTML syntax) &mdash; depending on
65 what you've set <a href="#html-mode"><code>HTML-MODE</code></a> to.
66 <p>
67 CL-WHO is intended to be portable and should work with all
68 conforming Common Lisp implementations. <a
69 href="#mail">Let us know</a> if you encounter any
70 problems.
71 <p>
72 It comes with a <a
73 href="http://www.opensource.org/licenses/bsd-license.php">BSD-style
74 license</a> so you can basically do with it whatever you want.
75 <p>
76 CL-WHO is for example used by <a href="http://clutu.com/">clutu</a> and <a href="http://heikestephan.de/">Heike Stephan</a>.
77
78 <p>
79 <font color=red>Download shortcut:</font> <a href="http://weitz.de/files/cl-who.tar.gz">http://weitz.de/files/cl-who.tar.gz</a>.
80 </blockquote>
81
82 <br>&nbsp;<br><h3><a class=none name="contents">Contents</a></h3>
83 <ol>
84 <li><a href="#example">Example usage</a>
85 <li><a href="#install">Download and installation</a>
86 <li><a href="#support">Support</a>
87 <li><a href="#syntax">Syntax and Semantics</a>
88 <li><a href="#dictionary">The CL-WHO dictionary</a>
89 <ol>
90 <li><a href="#with-html-output"><code>with-html-output</code></a>
91 <li><a href="#with-html-output-to-string"><code>with-html-output-to-string</code></a>
92 <li><a href="#*attribute-quote-char*"><code>*attribute-quote-char*</code></a>
93 <li><a href="#*downcase-tokens-p*"><code>*downcase-tokens-p*</code></a>
94 <li><a href="#*html-empty-tag-aware-p*"><code>*html-empty-tag-aware-p*</code></a>
95 <li><a href="#*html-empty-tags*"><code>*html-empty-tags*</code></a>
96 <li><a href="#*html-no-indent-tags*"><code>*html-no-indent-tags*</code></a>
97 <li><a href="#*prologue*"><code>*prologue*</code></a>
98 <li><a href="#esc"><code>esc</code></a>
99 <li><a href="#fmt"><code>fmt</code></a>
100 <li><a href="#htm"><code>htm</code></a>
101 <li><a href="#str"><code>str</code></a>
102 <li><a href="#html-mode"><code>html-mode</code></a>
103 <li><a href="#escape-string"><code>escape-string</code></a>
104 <li><a href="#escape-char"><code>escape-char</code></a>
105 <li><a href="#*escape-char-p*"><code>*escape-char-p*</code></a>
106 <li><a href="#escape-string-minimal"><code>escape-string-minimal</code></a>
107 <li><a href="#escape-string-minimal-plus-quotes"><code>escape-string-minimal-plus-quotes</code></a>
108 <li><a href="#escape-string-iso-8859-1"><code>escape-string-iso-8859-1</code></a>
109 <li><a href="#escape-string-all"><code>escape-string-all</code></a>
110 <li><a href="#escape-char-minimal"><code>escape-char-minimal</code></a>
111 <li><a href="#escape-char-minimal-plus-quotes"><code>escape-char-minimal-plus-quotes</code></a>
112 <li><a href="#escape-char-iso-8859-1"><code>escape-char-iso-8859-1</code></a>
113 <li><a href="#escape-char-all"><code>escape-char-all</code></a>
114 <li><a href="#conc"><code>conc</code></a>
115 <li><a href="#convert-tag-to-string-list"><code>convert-tag-to-string-list</code></a>
116 <li><a href="#convert-attributes"><code>convert-attributes</code></a>
117 </ol>
118 <li><a href="#ack">Acknowledgements</a>
119 </ol>
120
121 <br>&nbsp;<br><h3><a name="example" class=none>Example usage</a></h3>
122
123 Let's assume that <code>*HTTP-STREAM*</code> is the stream your web
124 application is supposed to write to. Here are some contrived code snippets
125 together with the Lisp code generated by CL-WHO and the resulting HTML output.
126
127 <table border=0 cellspacing=10 width="100%">
128
129 <tr>
130 <td bgcolor="#e0e0e0" valign=top><pre>
131 (<a class=noborder href="#with-html-output">with-html-output</a> (*http-stream*)
132 (loop for (link . title) in '((&quot;http://zappa.com/&quot; . &quot;Frank Zappa&quot;)
133 (&quot;http://marcusmiller.com/&quot; . &quot;Marcus Miller&quot;)
134 (&quot;http://www.milesdavis.com/&quot; . &quot;Miles Davis&quot;))
135 do (<a class=noborder href="#htm">htm</a> (:a :href link
136 (:b (str title)))
137 :br)))
138 </pre></td>
139
140 <td valign=top rowspan=2>
141 <a href='http://zappa.com/'><b>Frank Zappa</b></a><br /><a href='http://marcusmiller.com/'><b>Marcus Miller</b></a><br /><a href='http://www.milesdavis.com/'><b>Miles Davis</b></a><br />
142 </td>
143 </tr>
144
145 <tr>
146 <td bgcolor="#e0e0e0" valign=top><pre>
147 <font color="orange">;; code generated by CL-WHO (simplified)</font>
148
149 (let ((*http-stream* *http-stream*))
150 (progn
151 nil
152 (loop for (link . title) in '((&quot;http://zappa.com/&quot; . &quot;Frank Zappa&quot;)
153 (&quot;http://marcusmiller.com/&quot; . &quot;Marcus Miller&quot;)
154 (&quot;http://www.milesdavis.com/&quot; . &quot;Miles Davis&quot;))
155 do (progn
156 (write-string &quot;&lt;a href='&quot; *http-stream*)
157 (princ link *http-stream*)
158 (write-string &quot;'&gt;&lt;b&gt;&quot; *http-stream*)
159 (princ title *http-stream*)
160 (write-string &quot;&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;&quot; *http-stream*)))))
161 </pre></td>
162 </tr>
163
164 <tr>
165 <td bgcolor="#e0e0e0" valign=top><pre>
166 (<a class=noborder href="#with-html-output">with-html-output</a> (*http-stream*)
167 (:table :border 0 :cellpadding 4
168 (loop for i below 25 by 5
169 do (<a class=noborder href="#htm">htm</a>
170 (:tr :align &quot;right&quot;
171 (loop for j from i below (+ i 5)
172 do (<a class=noborder href="#htm">htm</a>
173 (:td :bgcolor (if (oddp j)
174 &quot;pink&quot;
175 &quot;green&quot;)
176 (fmt &quot;~@R&quot; (1+ j))))))))))
177 </pre></td>
178
179 <td valign=top rowspan=2>
180 <table border='0' cellpadding='4'><tr align='right'><td bgcolor='green'>I</td><td bgcolor='pink'>II</td><td bgcolor='green'>III</td><td bgcolor='pink'>IV</td><td bgcolor='green'>V</td></tr><tr align='right'><td bgcolor='pink'>VI</td><td bgcolor='green'>VII</td><td bgcolor='pink'>VIII</td><td bgcolor='green'>IX</td><td bgcolor='pink'>X</td></tr><tr align='right'><td bgcolor='green'>XI</td><td bgcolor='pink'>XII</td><td bgcolor='green'>XIII</td><td bgcolor='pink'>XIV</td><td bgcolor='green'>XV</td></tr><tr align='right'><td bgcolor='pink'>XVI</td><td bgcolor='green'>XVII</td><td bgcolor='pink'>XVIII</td><td bgcolor='green'>XIX</td><td bgcolor='pink'>XX</td></tr><tr align='right'><td bgcolor='green'>XXI</td><td bgcolor='pink'>XXII</td><td bgcolor='green'>XXIII</td><td bgcolor='pink'>XXIV</td><td bgcolor='green'>XXV</td></tr></table>
181 </td>
182 </tr>
183
184 <tr>
185 <td bgcolor="#e0e0e0" valign=top><pre>
186 <font color="orange">;; code generated by CL-WHO (simplified)</font>
187
188 (let ((*http-stream* *http-stream*))
189 (progn
190 nil
191 (write-string &quot;&lt;table border='0' cellpadding='4'&gt;&quot; *http-stream*)
192 (loop for i below 25 by 5
193 do (progn
194 (write-string &quot;&lt;tr align='right'&gt;&quot; *http-stream*)
195 (loop for j from i below (+ i 5)
196 do (progn
197 (write-string &quot;&lt;td bgcolor='&quot; *http-stream*)
198 (princ (if (oddp j) &quot;pink&quot; &quot;green&quot;) *http-stream*)
199 (write-string &quot;'&gt;&quot; *http-stream*)
200 (format *http-stream* &quot;~@r&quot; (1+ j))
201 (write-string &quot;&lt;/td&gt;&quot; *http-stream*)))
202 (write-string &quot;&lt;/tr&gt;&quot; *http-stream*)))
203 (write-string &quot;&lt;/table&gt;&quot; *http-stream*)))
204 </pre></td>
205 </tr>
206
207 <tr>
208 <td bgcolor="#e0e0e0" valign=top><pre>
209 (<a class=noborder href="#with-html-output">with-html-output</a> (*http-stream*)
210 (:h4 "Look at the character entities generated by this example")
211 (loop for i from 0
212 for string in '("Fete" "Sorensen" "naive" "Huhner" "Strasse")
213 do (<a class=noborder href="#htm">htm</a>
214 (:p :style (<a href="#conc">conc</a> "background-color:" (case (mod i 3)
215 ((0) "red")
216 ((1) "orange")
217 ((2) "blue")))
218 (<a class=noborder href="#htm">htm</a> (<a href="#esc">esc</a> string))))))
219 </pre></td>
220 <td valign=top rowspan=2>
221 <h4>Look at the character entities generated by this example</h4><p style='background-color:red'>F&#xEA;te</p><p style='background-color:orange'>S&#xF8;rensen</p><p style='background-color:blue'>na&#xEF;ve</p><p style='background-color:red'>H&#xFC;hner</p><p style='background-color:orange'>Stra&#xDF;e</p>
222 </td>
223 </tr>
224
225 <tr>
226 <td bgcolor="#e0e0e0" valign=top><pre>
227 <font color="orange">;; code generated by CL-WHO (simplified)</font>
228
229 (let ((*http-stream* *http-stream*))
230 (progn
231 nil
232 (write-string
233 &quot;&lt;h4&gt;Look at the character entities generated by this example&lt;/h4&gt;&quot;
234 *http-stream*)
235 (loop for i from 0 for string in '(&quot;Fete&quot; &quot;Sorensen&quot; &quot;naive&quot; &quot;Huhner&quot; &quot;Strasse&quot;)
236 do (progn
237 (write-string &quot;&lt;p style='&quot; *http-stream*)
238 (princ (<a class=noborder href="#conc">conc</a> &quot;background-color:&quot;
239 (case (mod i 3)
240 ((0) &quot;red&quot;)
241 ((1) &quot;orange&quot;)
242 ((2) &quot;blue&quot;)))
243 *http-stream*)
244 (write-string &quot;'&gt;&quot; *http-stream*)
245 (progn (write-string (<a class=noborder href="#escape-string">escape-string</a> string) *http-stream*))
246 (write-string &quot;&lt;/p&gt;&quot; *http-stream*)))))
247 </pre></td>
248 </tr>
249
250
251 </table>
252
253 <br>&nbsp;<br><h3><a name="install" class=none>Download and installation</a></h3>
254
255 CL-WHO together with this documentation can be downloaded from <a
256 href="http://weitz.de/files/cl-who.tar.gz">http://weitz.de/files/cl-who.tar.gz</a>. The
257 current version is 1.1.3.
258 <p>
259 The preferred method to fetch, compile and load CL-WHO is via <a href="http://beta.quicklisp.org/">Quicklisp</a>. Install
260 Quicklisp, then run
261 <pre>(ql:quickload :cl-who)</pre>
262 <p>
263 The current development version of CL-WHO can be found
264 at <a href="https://github.com/edicl/cl-who">https://github.com/edicl/cl-who</a>.
265 This is the one to send <a href="#mail">patches</a> against. Use at
266 your own risk.
267 <p>
268 Lu&iacute;s Oliveira maintains an
269 unofficial <a href="http://darcs.net/">darcs</a> repository of CL-WHO
270 at <a href="http://common-lisp.net/~loliveira/ediware/">http://common-lisp.net/~loliveira/ediware/</a>.
271 <p>
272 You can run a test suite which tests <em>some</em> (but
273 not <em>all</em>) aspects of the library with
274 <pre>
275 (asdf:oos 'asdf:test-op :cl-who)
276 </pre>
277
278 <br>&nbsp;<br><h3><a name="mail" class=none>Support and mailing lists</a></h3>
279
280 The development version of cl-who can be
281 found <a href="https://github.com/edicl/cl-who" target="_new">on
282 github</a>. Please use the github issue tracking system to submit bug
283 reports. Patches are welcome, please
284 use <a href="https://github.com/edicl/cl-who/pulls">GitHub pull
285 requests</a>. If you want to make a change,
286 please <a href="http://weitz.de/patches.html" target="_new">read this
287 first</a>.
288
289 <br>&nbsp;<br><h3><a name="syntax" class=none>Syntax and Semantics</a></h3>
290
291 CL-WHO is essentially just one <a
292 href="http://cl-cookbook.sourceforge.net/macros.html">macro</a>, <a
293 href="#with-html-output"><code>WITH-HTML-OUTPUT</code></a>, which
294 transforms the body of code it encloses into something else obeying the
295 following rules (which we'll call <em>transformation rules</em>) for the body's forms:
296
297 <ul>
298
299 <li>A string will be printed verbatim. To be
300 more precise, it is transformed into a form which'll print this
301 string to the stream the user provides.
302
303 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>&quot;foo&quot; <font color="red">=&gt;</font> (write-string &quot;foo&quot; s)</pre></td></tr></table>
304
305 (Here and for the rest of this document the <em>red arrow</em> means '...&nbsp;will be converted to code equivalent to&nbsp;...' where <em>equivalent</em> means that all output is sent to the &quot;right&quot; stream.)
306
307 <li>Each list beginning with a <a
308 href="http://www.lispworks.com/reference/HyperSpec/Body/t_kwd.htm"><em>keyword</em></a>
309 is transformed into an (X)HTML <b>tag</b> of the same (usually <href="#*downcase-tokens-p*">downcased</a>) name by the following rules:
310
311 <ul>
312
313 <li>If the list contains nothing but the keyword, the resulting tag
314 will be empty.
315
316 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:br) <font color="red">=&gt;</font> (write-string &quot;&lt;br /&gt;&quot; s)</pre></td></tr></table>
317 With <a href="#html-mode"><code>HTML-MODE</code></a> set to <code>:SGML</code> an empty element is written this way:
318 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:br) <font color="red">=&gt;</font> (write-string &quot;&lt;br&gt;&quot; s)</pre></td></tr></table>
319
320 <li>The initial keyword can be followed by another keyword which will be interpreted as the name of an <b>attribute</b>. The next form which will be taken as the attribute's <b>value</b>. (If there's no next form it'll be as if the next form had been <code>NIL</code>.) The form denoting the attribute's value will be treated as follows. (Note that the behaviour with respect to attributes is <em>incompatible</em> with versions earlier than&nbsp;0.3.0!)
321 <ul>
322 <li>If it is a string it will be printed literally.
323
324 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :bgcolor &quot;red&quot;) <font color="red">=&gt;</font> (write-string &quot;&lt;td bgcolor='red' /&gt;&quot; s)</pre></td></tr></table>
325
326 <li>If it is <code>T</code> and <a href="#html-mode"><code>HTML-MODE</code></a> is <code>:XML</code> (default) the attribute's value will be the attribute's name (following XHTML convention to denote attributes which don't have a value in HTML).
327
328 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :nowrap t) <font color="red">=&gt;</font> (write-string &quot;&lt;td nowrap='nowrap' /&gt;&quot; s)</pre></td></tr></table>
329
330 With <a href="#html-mode"><code>HTML-MODE</code></a> set to <code>:SGML</code>:
331
332 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :nowrap t) <font color="red">=&gt;</font> (write-string &quot;&lt;td nowrap&gt;&quot; s)</pre></td></tr></table>
333
334 <li>If it is <code>NIL</code> the attribute will be left out completely.
335
336 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:td :nowrap nil) <font color="red">=&gt;</font> (write-string &quot;&lt;td /&gt;&quot; s)</pre></td></tr></table>
337
338 <li>If it is a <a
339 href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_c.htm#constant_form"><em>constant form</em></a>, the result of evaluating it will be inserted into the resulting string as if printed with the <a
340 href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_c.htm#constant_form">format string</a> <code>&quot;~A&quot;</code> at macro expansion time.
341
342 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:table :border 3) <font color="red">=&gt;</font> (write-string &quot;&lt;table border='3' /&gt;&quot; s)</pre></td></tr></table>
343
344 <li>If it is any other form it will be left as is and later evaluated at run time and printed like with <a
345 href="http://www.lispworks.com/reference/HyperSpec/Body/f_wr_pr.htm"><code>PRINC</code></a> <em>unless</em> the value is <code>T</code> or <code>NIL</code> which will be treated as above. (It is the application developer's job to provide the correct <a href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_p.htm#printer_control_variable">printer control variables</a>.)
346
347 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre><font color="orange">;; simplified example, see function CHECKBOX below
348 ;; note that this form is not necessarily CONSTANTP in all Lisps</font>
349
350 (:table :border (+ 1 2)) <font color="red">=&gt;</font> (write-string &quot;&lt;table border='&quot; s)
351 (princ (+ 1 2) s)
352 (write-string &quot;' />&quot; s)</pre></td></tr></table>
353 </ul>
354
355 <li>Once an attribute/value pair has been worked up another one can follow, i.e. if the form following an attribute's value is again a keyword it will again be treated as an attribute and so on.
356
357 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:table :border 0 :cellpadding 5 :cellspacing 5)
358 <font color="red">=&gt;</font> (write-string &quot;&lt;table border='0' cellpadding='5' cellspacing='5' /&gt;&quot; s)</pre></td></tr></table>
359
360 <li>The first form following either the tag's name itself or an attribute value which is <em>not</em> a keyword determines the beginning of the tag's <b>content</b>. This and all the following forms are subject to the transformation rules we're just describing.
361
362 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(:p &quot;Paragraph&quot;) <font color="red">=&gt;</font> (write-string &quot;&lt;p&gt;Paragraph&lt;/p&gt;&quot; s)
363 (:p :class "foo" &quot;Paragraph&quot;) <font color="red">=&gt;</font> (write-string &quot;&lt;p class='foo'&gt;Paragraph&lt;/p&gt;&quot; s)
364 (:p :class "foo" &quot;One&quot; &quot; &quot; &quot;long&quot; &quot; &quot; &quot;sentence&quot;) <font color="red">=&gt;</font> (write-string &quot;&lt;p class='foo'&gt;One long sentence&lt;/p&gt;&quot; s)
365 (:p :class "foo" &quot;Visit &quot; (:a :href &quot;http://www.cliki.net/&quot; &quot;CLiki&quot;))
366 <font color="red">=&gt;</font> (write-string &quot;&lt;p class='foo'&gt;Visit &lt;a href='http://www.cliki.net/'&gt;CLiki&lt;/a&gt;&lt;/p&gt;&quot; s)</pre></td></tr></table>
367
368 <li>Beginning with <a href="#install">version&nbsp;0.4.0</a> you can also use a syntax like that of <a href="http://opensource.franz.com/xmlutils/xmlutils-dist/phtml.htm">LHTML</a> where the tag and all attribute/value pairs are enclosed in an additional list:
369
370 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>((:p) &quot;Paragraph&quot;) <font color="red">=&gt;</font> (write-string &quot;&lt;p&gt;Paragraph&lt;/p&gt;&quot; s)
371 ((:p :class "foo") &quot;Paragraph&quot;) <font color="red">=&gt;</font> (write-string &quot;&lt;p class='foo'&gt;Paragraph&lt;/p&gt;&quot; s)
372 ((:p :class "foo" :name "humpty-dumpty") &quot;One&quot; &quot; &quot; &quot;long&quot; &quot; &quot; &quot;sentence&quot;)
373 <font color="red">=&gt;</font> (write-string &quot;&lt;p class='foo' name='humpty-dumpty'&gt;One long sentence&lt;/p&gt;&quot; s)
374 ((:p :class "foo") &quot;Visit &quot; ((:a :href &quot;http://www.cliki.net/&quot;) &quot;CLiki&quot;))
375 <font color="red">=&gt;</font> (write-string &quot;&lt;p class='foo'&gt;Visit &lt;a href='http://www.cliki.net/'&gt;CLiki&lt;/a&gt;&lt;/p&gt;&quot; s)</pre></td></tr></table>
376
377 </ul>
378
379 Here's a slightly more elaborate example:
380 <pre>
381 * (defun checkbox (stream name checked &amp;optional value)
382 (with-html-output (stream)
383 (:input :type &quot;checkbox&quot; :name name :checked checked :value value)))
384
385 CHECKBOX
386 * (with-output-to-string (s) (checkbox s &quot;foo&quot; t))
387
388 &quot;&lt;input type='checkbox' name='foo' checked='checked' /&gt;&quot;
389 * (with-output-to-string (s) (checkbox s &quot;foo&quot; nil))
390
391 &quot;&lt;input type='checkbox' name='foo' /&gt;&quot;
392 * (with-output-to-string (s) (checkbox s &quot;foo&quot; nil &quot;bar&quot;))
393
394 &quot;&lt;input type='checkbox' name='foo' value='bar' /&gt;&quot;
395 * (with-output-to-string (s) (checkbox s &quot;foo&quot; t &quot;bar&quot;))
396
397 &quot;&lt;input type='checkbox' name='foo' checked='checked' value='bar' /&gt;&quot;
398 </pre>
399
400 <li>A keyword alone will be treated like a list containing only this keyword.
401
402 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>:hr <font color="red">=&gt;</font> (write-string &quot;&lt;hr /&gt;&quot; s)</pre></td></tr></table>
403
404 <li>A form which is neither a string nor a keyword nor a list beginning with a keyword will be left as is except for the following <a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm#macrolet">local macros</a>:
405 <ul>
406 <li>Forms that look like <code>(<b>str</b> <i>form</i>)</code> will be substituted with
407 <span style="white-space: nowrap"><code>(let ((result <i>form</i>)) (when result (princ result s)))</code></span>.
408
409 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(loop for i below 10 do (str i)) <font color="red">=&gt;</font>
410 (loop for i below 10 do
411 (let ((#:result i))
412 (when #:result (princ #:result *standard-output*))))</pre></td></tr></table>
413
414 <li>Forms that look like <code>(<b>fmt</b> <i>form*</i>)</code> will be substituted with <code>(format s <i>form*</i>)</code>.
415
416 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(loop for i below 10 do (fmt "~R" i)) <font color="red">=&gt;</font> (loop for i below 10 do (format s "~R" i))</pre></td></tr></table>
417 <li>Forms that look like <code>(<b>esc</b> <i>form</i>)</code> will be substituted with
418 <span style="white-space: nowrap"><code>(let ((result <i>form</i>)) (when result (write-string (<a href="#escape-string">escape-string</a> result s))))</code></span>.
419
420 <li>If a form looks like <code>(<b>htm</b> <i>form*</i>)</code> then each of the <code><i>forms</i></code> will be subject to the transformation rules we're just describing, i.e. this is the body is wrapped with another invocation of <a href="#with-html-output"><code>WITH-HTML-OUTPUT</code></a>.
421
422 <table border=0 cellpadding=2 cellspacing=3><tr><td><pre>(loop for i below 100 do (htm (:b &quot;foo&quot;) :br))
423 <font color="red">=&gt;</font> (loop for i below 100 do (progn (write-string &quot;&lt;b&gt;foo&lt;/b&gt;&lt;br /&gt;&quot; s)))</pre></td></tr></table>
424
425
426 </ul>
427
428 <li>That's all. Note in particular that CL-WHO knows <em>nothing</em> about HTML or XHTML, i.e. it doesn't check whether you mis-spelled tag names or use attributes which aren't allowed. CL-WHO doesn't care if you use, say, <code>:foobar</code> instead of <code>:hr</code>.
429 </ul>
430
431 <br>&nbsp;<br><h3><a class=none name="dictionary">The CL-WHO dictionary</a></h3>
432
433 CL-WHO exports the following symbols:
434
435 <p><br>[Macro]
436 <br><a class=none name="with-html-output"><b>with-html-output</b> <i>(var <tt>&amp;optional</tt> stream <tt>&amp;key</tt> prologue indent) declaration* form*</i> =&gt; <i>result*</i></a>
437
438 <blockquote><br> This is the main macro of CL-WHO. It will transform
439 its body by the transformation rules described
440 in <a href="#syntax"><em>Syntax and Semantics</em></a> such that the
441 output generated is sent to the stream denoted
442 by <code><i>var</i></code>
443 and <code><i>stream</i></code>. <code><i>var</i></code> must be a
444 symbol. If <code><i>stream</i></code> is <code>NIL</code> it is
445 assumed that <code><i>var</i></code> is already bound to a stream,
446 if <code><i>stream</i></code> is
447 not <code>NIL</code> <code><i>var</i></code> will be bound to the
448 form <code><i>stream</i></code> which will be evaluated at run
449 time. <code><i>prologue</i></code> should be a string
450 (or <code>NIL</code> for the empty string which is the default) which
451 is guaranteed to be the first thing sent to the stream from within the
452 body of this macro. If <code><i>prologue</i></code> is <code>T</code>
453 the prologue string is the value
454 of <a href="#*prologue*"><code>*PROLOGUE*</code></a>.
455 <p>
456 CL-WHO will usually try not to insert any unnecessary whitespace in
457 order to save bandwidth. However, if <code><i>indent</i></code>
458 is <em>true</em> line breaks will be inserted and nested tags will be
459 indented properly. The value of <code><i>indent</i></code> - if it is
460 an integer - will be taken as the initial indentation. If it is not an
461 integer it is assumed to mean <code>0</code>. Value
462 of <a href="#*html-no-indent-tags*"><code>*HTML-NO-INDENT-TAGS*</code></a>
463 controls which tag-contents are excempt from indentation: by default
464 contents of <code>PRE</code> and <code>TEXTAREA</code> tags are not
465 indented to avoid spurious layout changes. (Note: in certain
466 situations additional whitespace may change the layout of tables.)
467 <p>
468 The <code><i>results</i></code> are the values returned by
469 the <code><i>forms</i></code>.
470 <p>
471 Note that the keyword arguments <code><i>prologue</i></code>
472 and <code><i>indent</i></code>, and the associated variables are
473 used <em>at macro expansion time</em>.
474
475 <pre>
476 * (with-html-output (*standard-output* nil :prologue t)
477 (:html (:body &quot;Not much there&quot;))
478 (values))
479 &lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;&lt;html&gt;&lt;body&gt;Not much there&lt;/body&gt;&lt;/html&gt;
480 * (with-html-output (*standard-output*)
481 (:html (:body :bgcolor &quot;white&quot;
482 &quot;Not much there&quot;))
483 (values))
484 &lt;html&gt;&lt;body bgcolor='white'&gt;Not much there&lt;/body&gt;&lt;/html&gt;
485 * (with-html-output (*standard-output* nil :prologue t :indent t)
486 (:html (:body :bgcolor &quot;white&quot;
487 &quot;Not much there&quot;))
488 (values))
489 &lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
490 &lt;html&gt;
491 &lt;body bgcolor='white'&gt;
492 Not much there
493 &lt;/body&gt;
494 &lt;/html&gt;
495 </pre>
496 </blockquote>
497
498 <p><br>[Macro]
499 <br><a class=none name="with-html-output-to-string"><b>with-html-output-to-string</b> <i>(var <tt>&amp;optional</tt> string-form <tt>&amp;key</tt> element-type prologue indent) declaration* form*</i> =&gt; <i>result*</i></a>
500
501 <blockquote><br>
502 This is just a thin wrapper around <a href="#with-html-output"><code>WITH-HTML-OUTPUT</code></a>. Indeed, the wrapper is so thin that the best explanation probably is to show its definition:
503 <pre>
504 (defmacro with-html-output-to-string ((var &amp;optional string-form
505 &amp;key (element-type ''character)
506 prologue
507 indent)
508 &amp;body body)
509 &quot;Transform the enclosed BODY consisting of HTML as s-expressions
510 into Lisp code which creates the corresponding HTML as a string.&quot;
511 `(with-output-to-string (,var ,string-form :elementy-type ,element-type)
512 (with-html-output (,var nil :prologue ,prologue :indent ,indent)
513 ,@body)))
514 </pre>
515 Note that the <code><i>results</i></code> of this macro are determined by the behaviour of <a href="http://www.lispworks.com/reference/HyperSpec/Body/m_w_out_.htm"><code>WITH-OUTPUT-TO-STRING</code></a>.
516 </blockquote>
517
518 <p><br>[Special variable]
519 <br><a class=none name="*attribute-quote-char*"><b>*attribute-quote-char*</b></a>
520
521 <blockquote><br>
522 This character is used as the quote character when building attributes. Defaults to the single quote <code>#\'</code>. Only other reasonable character is the double quote <code>#\&quot;</code>.
523 </blockquote>
524
525 <p><br>[Special variable]
526 <br><a class=none name="*downcase-tokens-p*"><b>*downcase-tokens-p*</b></a>
527
528 <blockquote><br>
529 If the value of this variable is <code>NIL</code>, keyword symbols representing a tag or attribute name will not be
530 automatically converted to lowercase. This is useful when one needs to
531 output case sensitive XML. The default is <code>T</code>.
532 </blockquote>
533
534 <p><br>[Special variable]
535 <br><a class=none name="*html-empty-tag-aware-p*"><b>*html-empty-tag-aware-p*</b></a>
536
537 <blockquote><br>
538 Set this to <code>NIL</code> to if you want to use CL-WHO as a strict XML
539 generator. Otherwise, CL-WHO will only write empty tags listed in
540 <a href="#*html-empty-tags*"><code>*HTML-EMPTY-TAGS*</code></a> as <code>&lt;tag/&gt;</code> (XHTML mode) or <code>&lt;tag&gt;</code> (SGML mode or HTML mode). For
541 all other tags, it will always generate <code>&lt;tag&gt;&lt;/tag&gt;</code>. The initial value of this variable is <code>T</code>.
542 </blockquote>
543
544 <p><br>[Special variable]
545 <br><a class=none name="*html-empty-tags*"><b>*html-empty-tags*</b></a>
546
547 <blockquote><br>
548 The list of HTML tags that should be output as empty tags. See
549 <a href="#*html-empty-tag-aware-p*"><code>*HTML-EMPTY-TAG-AWARE-P*</code></a>.
550 The initial value is the list
551 <pre>
552 (:area :atop :audioscope :base :basefont :br :choose :col :command :embed
553 :frame :hr :img :input :isindex :keygen :left :limittext :link :meta :nextid
554 :of :over :param :range :right :source :spacer :spot :tab :track :wbr)
555 </pre>
556 </blockquote>
557
558 <p><br>[Special variable]
559 <br><a class=none name="*html-no-indent-tags*"><b>*html-no-indent-tags*</b></a>
560
561 <blockquote><br>
562 The list of HTML tags that should disable indentation inside them even
563 when indentation is requested. The initial value is a list containing
564 only <code>:pre</code> and <code>:texarea</code>.
565 </blockquote>
566
567 <p><br>[Special variable]
568 <br><a class=none name="*prologue*"><b>*prologue*</b></a>
569
570 <blockquote><br>
571 This is the prologue string which will be printed if the <code><i>prologue</i></code> keyword argument to <a href="#with-html-output"><code>WITH-HTML-OUTPUT</code></a> is <code>T</code>. Gets changed when you set <a href="#html-mode"><code>HTML-MODE</code></a>. Its initial value is
572
573 <pre>&quot;&lt;!DOCTYPE html PUBLIC \&quot;-//W3C//DTD XHTML 1.0 Strict//EN\&quot; \&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\&quot;&gt;&quot;</pre>
574 </blockquote>
575
576 <p><br>[Symbol]
577 <br><a class=none name="esc"><b>esc</b></a>
578 <br>[Symbol]
579 <br><a class=none name="fmt"><b>fmt</b></a>
580 <br>[Symbol]
581 <br><a class=none name="htm"><b>htm</b></a>
582 <br>[Symbol]
583 <br><a class=none name="str"><b>str</b></a>
584
585 <blockquote><br>
586 These are just symbols with no bindings associated with them. The only reason they are exported is their special meaning during the transformations described in <a href="#syntax"><em>Syntax and Semantics</em></a>.
587 </blockquote>
588
589 <p><br>[Accessor]
590 <br><a class=none name="html-mode"><b>html-mode</b></a> <i>=> mode</i>
591 <br><tt>(setf (</tt><b>html-mode</b>) <i>mode</i><tt>)</tt>
592 <blockquote><br>
593 The function <code>HTML-MODE</code> returns the current mode for generating HTML. The default is <code>:XML</code> for XHTML. You can change this by setting it with <code>(SETF&nbsp;(HTML-MODE)&nbsp;:SGML)</code> to pre-XML HTML mode or <code>(SETF&nbsp;(HTML-MODE)&nbsp;:HTML5)</code> to HTML5 mode (using HTML syntax).
594 <p>
595 Setting it to SGML HTML sets the <a href="#*prologue*"><code>*prologue*</code></a> to the doctype string for HTML 4.01 transitional:
596 <pre>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot; &quot;http://www.w3.org/TR/html4/loose.dtd&quot;&gt;</pre>
597 Code generation in SGML HTML is slightly different from XHTML - there's no need to end empty elements with <code>/&gt;</code> and empty attributes are allowed.
598 <p>
599 Setting it to HTML5 sets the <a href="#*prologue*"><code>*prologue*</code></a> to the following doctype string:
600 <pre>&lt;!DOCTYPE html&gt;</pre>
601 </blockquote>
602
603 <p><br>[Function]
604 <br><a class=none name="escape-string"><b>escape-string</b></a> <i>string <tt>&amp;key</tt> test</i> =&gt; <i>escaped-string</i>
605
606 <blockquote><br>
607 This function will accept a string <code><i>string</i></code> and will replace every character for which <code><i>test</i></code> returns <em>true</em> with its character entity. The numeric character entities use decimal instead of hexadecimal values when <a href="#html-mode"><code>HTML-MODE</code></a> is set to <code>:SGML</code> because of compatibility reasons with old clients. <code><i>test</i></code> must be a function of one argument which accepts a character and returns a <a href="http://www.lispworks.com/reference/HyperSpec/Body/26_glo_g.htm#generalized_boolean">generalized boolean</a>. The default is the value of <a href="#*escape-char-p*"><code>*ESCAPE-CHAR-P*</code></a>. Note the <a href="#esc"><code>ESC</code></a> shortcut described in <a href="#syntax"><em>Syntax and Semantics</em></a>.
608
609 <pre>
610 * (escape-string &quot;&lt;Huhner&gt; 'naive'&quot;)
611 &quot;&amp;lt;Huhner&amp;gt; &amp;#x27;naive&amp;#x27;&quot;
612 * (with-html-output-to-string (s)
613 (:b (esc &quot;&lt;Huhner&gt; 'naive'&quot;)))
614 &quot;&lt;!DOCTYPE html PUBLIC \&quot;-//W3C//DTD XHTML 1.0 Strict//EN\&quot; \&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\&quot;&lt;b&gt;&amp;lt;Huhner&amp;gt; &amp;#x27;naive&amp;#x27;&lt;/b&gt;&quot;
615 </pre>
616 </blockquote>
617
618 <p><br>[Function]
619 <br><a class=none name="escape-char"><b>escape-char</b></a> <i>character <tt>&amp;key</tt> test</i> =&gt; <i>escaped-string</i>
620
621 <blockquote><br>
622 This function works identical to <a href="#escape-string"><code>ESCAPE-STRING</code></a>, except that it operates on characters instead of strings.
623 </blockquote>
624
625 <p><br>[Special variable]
626 <br><a class=none name="*escape-char-p*"><b>*escape-char-p*</b></a>
627
628 <blockquote><br>
629 This is the default for the <code><i>test</i></code> keyword argument to <a href="#escape-string"><code>ESCAPE-STRING</code></a> and <a href="#escape-char"><code>ESCAPE-CHAR</code></a>. Its initial value is
630
631 <pre>
632 #'(lambda (char)
633 (or (find char &quot;&lt;&gt;&amp;'\&quot;&quot;)
634 (&gt; (char-code char) 127)))
635 </pre>
636 </blockquote>
637
638 <p><br>[Function]
639 <br><a class=none name="escape-string-minimal"><b>escape-string-minimal</b> <i>string</i> =&gt; <i>escaped-string</i></a>
640 <br>[Function]
641 <br><a class=none name="escape-string-minimal-plus-quotes"><b>escape-string-minimal-plus-quotes</b> <i>string</i> =&gt; <i>escaped-string</i></a>
642 <br>[Function]
643 <br><a class=none name="escape-string-iso-8859-1"><b>escape-string-iso-8859-1</b> <i>string</i> =&gt; <i>escaped-string</i></a>
644 <br>[Function]
645 <br><a class=none name="escape-string-all"><b>escape-string-all</b> <i>string</i> =&gt; <i>escaped-string</i></a>
646 <br>[Function]
647 <br><a class=none name="escape-char-minimal"><b>escape-char-minimal</b> <i>character</i> =&gt; <i>escaped-string</i></a>
648 <br>[Function]
649 <br><a class=none name="escape-char-minimal-plus-quotes"><b>escape-char-minimal-plus-quotes</b> <i>character</i> =&gt; <i>escaped-string</i></a>
650 <br>[Function]
651 <br><a class=none name="escape-char-iso-8859-1"><b>escape-char-iso-8859-1</b> <i>character</i> =&gt; <i>escaped-string</i></a>
652 <br>[Function]
653 <br><a class=none name="escape-char-all"><b>escape-char-all</b> <i>character</i> =&gt; <i>escaped-string</i></a>
654
655 <blockquote><br> These are convenience function based
656 on <a href="#escape-string"><code>ESCAPE-STRING</code></a>
657 and <a href="#escape-char"><code>ESCAPE-CHAR</code></a>. The string
658 functions are defined in a way similar to this one:
659
660 <pre>
661 (defun escape-string-minimal (string)
662 &quot;Escape only #\&lt;, #\&gt;, and #\&amp; in STRING.&quot;
663 (escape-string string :test #'(lambda (char) (find char &quot;&lt;&gt;&amp;&quot;))))
664
665 (defun escape-string-minimal-plus-quotes (string)
666 &quot;Like ESCAPE-STRING-MINIMAL but also escapes quotes.&quot;
667 (escape-string string :test #'(lambda (char) (find char &quot;&lt;&gt;&amp;'\&quot;&quot;))))
668
669 (defun escape-string-iso-8859-1 (string)
670 &quot;Escapes all characters in STRING which aren't defined in ISO-8859-1.&quot;
671 (escape-string string :test #'(lambda (char)
672 (or (find char &quot;&lt;&gt;&amp;'\&quot;&quot;)
673 (&gt; (char-code char) 255)))))
674
675 (defun escape-string-all (string)
676 &quot;Escapes all characters in STRING which aren't in the 7-bit ASCII
677 character set.&quot;
678 (escape-string string :test #'(lambda (char)
679 (or (find char &quot;&lt;&gt;&amp;'\&quot;&quot;)
680 (&gt; (char-code char) 127)))))
681 </pre>
682 The character functions are defined in an analogous manner.
683 </blockquote>
684
685 <p><br>[Function]
686 <br><a class=none name="conc"><b>conc</b> <i><tt>&amp;rest</tt> string-list</i> =&gt; <i>string</i></a>
687
688 <blockquote><br>
689 Utility function to concatenate all arguments (which should be strings) into one string. Meant to be used mainly with attribute values.
690
691 <pre>
692 * (conc &quot;This&quot; &quot; &quot; &quot;is&quot; &quot; &quot; &quot;a&quot; &quot; &quot; &quot;sentence&quot;)
693 &quot;This is a sentence&quot;
694 * (with-html-output-to-string (s)
695 (:div :style (conc &quot;padding:&quot;
696 (format nil &quot;~A&quot; (+ 3 2)))
697 &quot;Foobar&quot;))
698 &quot;&lt;div style='padding:5'&gt;Foobar&lt;/div&gt;&quot;
699 </pre>
700 </blockquote>
701
702 <p><br>[Generic Function]
703 <br><a class=none name="convert-tag-to-string-list"><b>convert-tag-to-string-list</b></a> <i>tag attr-list body body-fn</i> =&gt; <i>strings-or-forms</i>
704
705 <blockquote><br>
706
707 This function exposes some of CL-WHO's internals so users can
708 customize its behaviour. It is called whenever a tag is processed and
709 must return a corresponding list of strings or Lisp forms. The idea
710 is that you can specialize this generic function in order to process
711 certain tags yourself.
712 <p>
713 <code><i>tag</i></code> is a keyword symbol naming the outer tag,
714 <code><i>attr-list</i></code> is an alist of its attributes (the car
715 is the attribute's name as a keyword, the cdr is its value),
716 <code><i>body</i></code> is the tag's body, and
717 <code><i>body-fn</i></code> is a function which should be applied to
718 the body to further process it. Of course, if you define your own
719 methods you can ignore <code><i>body-fn</i></code> if you want.
720 <p>
721 Here are some simple examples:
722 <pre>
723 * (defmethod convert-tag-to-string-list ((tag (eql :red)) attr-list body body-fn)
724 (declare (ignore attr-list))
725 (nconc (cons "&lt;font color='red'&gt;" (funcall body-fn body)) (list "&lt;/font&gt;")))
726 ; Compiling LAMBDA (PCL::.PV-CELL. PCL::.NEXT-METHOD-CALL. TAG ATTR-LIST BODY BODY-FN):
727 ; Compiling Top-Level Form:
728
729 #&lt;STANDARD-METHOD CONVERT-TAG-TO-STRING-LIST ((EQL :RED) T T T) {582B268D}&gt;
730 * (with-html-output (*standard-output*)
731 (:red (:b "Bold and red"))
732 (values))
733 &lt;font color='red'&gt;&lt;b&gt;Bold and red&lt;/b&gt;&lt;/font&gt;
734 * (show-html-expansion (s)
735 (:red :style "spiffy" (if (foo) (htm "Attributes are ignored"))))
736
737 (LET ((S S))
738 (PROGN
739 NIL
740 (WRITE-STRING "&lt;font color='red'&gt;" S)
741 (IF (FOO) (PROGN (WRITE-STRING "Attributes are ignored" S)))
742 (WRITE-STRING "&lt;/font&gt;" S)))
743 * (defmethod convert-tag-to-string-list ((tag (eql :table)) attr-list body body-fn)
744 (cond ((cdr (assoc :simple attr-list))
745 (nconc (cons "&lt;table"
746 (<a class=noborder href="#convert-attributes">convert-attributes</a> (remove :simple attr-list :key #'car)))
747 (list "&gt;")
748 (loop for row in body
749 collect "&lt;tr&gt;"
750 nconc (loop for col in row
751 collect "&lt;td&gt;"
752 when (constantp col)
753 collect (format nil "~A" col)
754 else
755 collect col
756 collect "&lt;/td&gt;")
757 collect "&lt;/tr&gt;")
758 (list "&lt;/table&gt;")))
759 (t
760 <font color=orange>;; you could as well invoke CALL-NEXT-METHOD here, of course</font>
761 (nconc (cons "&lt;table "
762 (<a class=noborder href="#convert-attributes">convert-attributes</a> attr-list))
763 (list "&gt;")
764 (funcall body-fn body)
765 (list "&lt;/table&gt;")))))
766 ; Compiling LAMBDA (PCL::.PV-CELL. PCL::.NEXT-METHOD-CALL. TAG ATTR-LIST BODY BODY-FN):
767 ; Compiling Top-Level Form:
768
769 #&lt;STANDARD-METHOD CONVERT-TAG-TO-STRING-LIST ((EQL :TABLE) T T T) {58AFB7CD}&gt;
770 * (with-html-output (*standard-output*)
771 (:table :border 0 (:tr (:td "1") (:td "2")) (:tr (:td "3") (:td "4"))))
772 &lt;table border='0'&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
773 "&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;"
774 * (show-html-expansion (s)
775 (:table :simple t :border 0
776 (1 2) (3 (fmt "Result = ~A" (compute-result)))))
777
778 (LET ((S S))
779 (PROGN
780 NIL
781 (WRITE-STRING
782 "&lt;table border='0'&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;"
783 S)
784 (FORMAT S "Result = ~A" (COMPUTE-RESULT))
785 (WRITE-STRING "&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;" S)))
786 </pre>
787
788 </blockquote>
789
790 <p><br>[Function]
791 <br><a class=none name="convert-attributes"><b>convert-attributes</b></a> <i>attr-list</i> =&gt; <i>strings-or-forms</i>
792
793 <blockquote><br>
794
795 This is a helper function which can be called from
796 <a href="#convert-tag-to-string-list"><code>CONVERT-TAG-TO-STRING-LIST</code></a> to process the list of attributes.
797
798 </blockquote>
799
800 <br>&nbsp;<br><h3><a class=none name="ack">Acknowledgements</a></h3>
801
802 Thanks to Tim Bradshaw and John Foderaro for the inspiration provided
803 by their libraries mentioned <a href="#abstract">above</a>. Thanks to
804 J&ouml;rg-Cyril H&ouml;hle for his suggestions with respect to
805 attribute values. Thanks to Kevin Rosenberg for the LHTML patch.
806 Thanks to Stefan Scholl for the 'old school' patch. Thanks to Mac
807 Chan for several useful additions.
808
809 <p>
810 $Header: /usr/local/cvsrep/cl-who/doc/index.html,v 1.68 2009/03/09 21:54:11 edi Exp $
811 <p><a href="http://weitz.de/index.html">BACK TO MY HOMEPAGE</a>
812
813 </body>
814 </html>