/[svn]/website/trunk/web/site.cd
ViewVC logotype

Contents of /website/trunk/web/site.cd

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1375 - (hide annotations)
Tue Jul 10 18:43:52 2007 UTC (5 years, 11 months ago) by abate
Original Path: web/site.cd
File size: 16260 byte(s)
[r2004-12-26 23:43:53 by afrisch] Oops

Original author: afrisch
Date: 2004-12-26 23:43:54+00:00
1 abate 284 (* This CDuce script produces CDuce web site. *)
2    
3 abate 369 (** Command line **)
4    
5 abate 580 let input =
6 abate 1097 match argv [] with
7 abate 580 | [ s ] -> s
8 abate 709 | _ -> raise "Please use --arg to specify an input file on the command line"
9 abate 369
10 abate 343 (** Output types **)
11 abate 284
12 abate 713 using H = "xhtml"
13 abate 250
14 abate 343 (** Input types **)
15    
16 abate 1065 type Site = <site>[ <title>String Page ]
17 abate 1238 type Page = <page name=Latin1 url=?String new=?"">[ <title>String <banner>[InlineText*]? Item* ]
18 abate 488 type External = <external {|href=String; title=String; name=String |}>[]
19 abate 343
20     type Item =
21 abate 1065 <box ({| title=String; link=String; short=?String |} | {| |})>Content
22 abate 343 | <meta>Content
23     | <left>Content
24 abate 1064 | <footnotes>[]
25 abate 343 | Page
26 abate 488 | External
27 abate 343
28 abate 1330 type Author = <author presenter=?("yes"|"no")>String
29 abate 343 type Paper =
30 abate 1064 <paper file=?String old=?"">[
31 abate 488 <title>String Author+ <comment>[InlineText*] <abstract>Content ]
32 abate 343
33     type Slides =
34 abate 488 <slides file=String>[ <title>String Author+ <comment>[InlineText*] ]
35 abate 343
36     type Link =
37 abate 561 <link url=String title=String>[ InlineText* ]
38 abate 343
39     type Content =
40 abate 614 [ ( <p {|style=?String|}>[InlineText*]
41 abate 1064 | <ul {||}>[<li {||}>Content *]
42 abate 1264 | <ol {|style=?String|}>[<li {||}>Content *]
43 abate 343 | <section title=String>Content
44 abate 344 | <sample highlight=?"true"|"false">String
45 abate 979 | <xmlsample highlight=?"true"|"false">String
46 abate 981 | <sessionsample highlight=?"true"|"false">String
47 abate 713 | H:Xtable
48 abate 343 | Paper | Slides | Link
49 abate 1065 | <boxes-toc short=?"" sections=?"">[]
50     | <pages-toc sections=?"">[]
51 abate 343 | <site-toc>[]
52     | <local-links href=String>[]
53 abate 356 | <two-columns>[ <left>Content <right>Content ]
54 abate 1065 | <note title=?String> Content
55 abate 1064 | <footnotes>[]
56 abate 343 | InlineText
57 abate 488 )* ]
58 abate 343
59     type InlineText =
60     Char
61 abate 593 | <(`b|`i|`tt|`em) {| style=?String |}>[InlineText*]
62 abate 343 | <code>String
63 abate 347 | <local href=String>String
64 abate 625 | <footnote>[InlineText*]
65 abate 1348 | H:Xa | H:Ximg | H:Xbr
66     | <thumbnail href=String width=?IntStr height=?IntStr>[]
67     | <thumbnails href=String width=?IntStr height=?IntStr>[ PCDATA ]
68 abate 343
69 abate 1348 type IntStr = ['0'--'9'+]
70    
71    
72 abate 343 (** Generic purpose functions **)
73    
74     (* Recursive inclusion of XML files and verbatim text files *)
75    
76 abate 1229 let load_include (Latin1 -> [Any*])
77 abate 336 name ->
78 abate 368 (* let _ = print [ 'Loading ' !name '... \n' ] in *)
79 abate 336 xtransform [ (load_xml name) ] with
80 abate 1229 | <include file=(s & Latin1)>[] -> load_include s
81     | <include-verbatim file=(s & Latin1)>[] -> load_file s
82 abate 336
83 abate 343 (* Highlighting text between {{...}} *)
84 abate 336
85 abate 981 let highlight (String -> [ (Char | H:Xstrong | H:Xi)* ] )
86     | [ '{{%%' h ::(Char *?) '%%}}' ; rest ] ->
87     [ <strong class="highlight">[<i>h]; highlight rest ]
88 abate 340 | [ '{{' h ::(Char *?) '}}' ; rest ] ->
89 abate 981 [ <strong class="highlight">h; highlight rest ]
90 abate 1037 | [ '$$%%' h ::(Char *?) '%%$$' ; rest ] ->
91     [ <strong class="ocaml">[<i>h]; highlight rest ]
92     | [ '$$' h ::(Char *?) '$$' ; rest ] ->
93     [ <strong class="ocaml">h; highlight rest ]
94 abate 381 | [ '%%' h ::(Char *?) '%%' ; rest ] ->
95 abate 698 [ <i>h; highlight rest ]
96 abate 340 | [ c; rest ] -> [ c; highlight rest ]
97 abate 488 | [] -> []
98 abate 336
99 abate 343 (* Split a comma-separated string *)
100    
101 abate 488 let split_comma (String -> [String*])
102 abate 341 | [ x::(Char*?) ',' ; rest ] -> (x, split_comma rest)
103 abate 488 | s -> [ s ]
104 abate 336
105 abate 1348 type wschar = ' ' | '\n' | '\t' | '\r'
106 abate 253
107 abate 1348 let split_thumbnails (String -> [(String,String)*])
108     | [ wschar* x::(Char\wschar\':')+ ':' y::_*? '.'; rest ] ->
109     ((x,y), split_thumbnails rest)
110     | [ wschar* x::(Char\wschar)+; rest ] ->
111     ((x,""), split_thumbnails rest)
112     | [ wschar* ] -> []
113    
114 abate 343 (** Internal types **)
115 abate 341
116 abate 488 type Path = [ { url = String; title = String }* ]
117 abate 341 type Tree = { name = String; url = String; title = String;
118 abate 1058 children = [Tree*]; boxes = [H:Xul?] }
119 abate 250
120 abate 1065 let url_of_page (Page -> String)
121     | <page url=u>_ -> u
122     | <page name=n>_ -> n @ ".html"
123 abate 346
124 abate 1330 let render(a : String)(p : {presenter=?"yes"|"no"}) : H:Flow =
125     (match p with {presenter="yes"} -> [<strong class="ocaml">a] | _ -> a)
126 abate 250
127 abate 1330 let authors ([Author+] -> H:Flow)
128     | [ <author (p)>a ] -> render a p
129     | [ <author (p1)>a1 <author (p2)>a2 ] -> (render a1 p1) @ ", and " @ (render a2 p2)
130     | [ <author (p)>a; rem ] -> (render a p)@ ", " @ authors rem
131    
132 abate 488 let find_local_link (sitemap : [Tree*], l : String) : Tree =
133 abate 341 match sitemap with
134     | (h,t) ->
135 abate 347 if (h . name = l) then h
136 abate 341 else
137     (try find_local_link (t,l) with `Not_found ->
138     find_local_link (h . children,l))
139 abate 488 | [] -> raise `Not_found
140 abate 341
141 abate 713 let local_link (sitemap : Tree, l : String, txt : String) : H:Inline =
142 abate 347 try
143     let h = find_local_link ([sitemap],l) in
144     let txt = if txt = "" then h . title else txt in
145     <a href=(h . url)>txt
146 abate 488 with `Not_found -> raise [ 'Local link not found: ' !l ]
147 abate 341
148 abate 488 let compute_sitemap ((Page|External) -> Tree)
149 abate 1064 | <page name=name>[ <title>title (c::(Page|External) | _)* ] & p ->
150 abate 341 let children = map c with p -> compute_sitemap p in
151 abate 1065 { name = name; url = (url_of_page p); title = title;
152 abate 1058 children = children; boxes = (boxes_of p) }
153 abate 1064 | <external name=name href=h title=t>[] ->
154 abate 1058 { name = name; url = h; title = t; children = []; boxes = [] }
155 abate 341
156 abate 1064 let ul([H:Xli*] -> [H:Xul?]) [] -> [] | l -> [ <ul>l ]
157    
158 abate 1264 let ol(([H:Xli*],{|style=?String|}) -> [H:Xol?])
159     ([],_) -> []
160     | (l,s&{|style=?String|}) -> [ <ol (s)>l ]
161 abate 1126
162 abate 1264
163    
164 abate 713 let display_sitemap (h : Tree) : H:Xli =
165 abate 341 let ch = map h . children with x -> display_sitemap x in
166 abate 1064 <li>[ <a href=(h . url)>[ '[' !(h . title) ']' ] !(h . boxes); (ul ch) ]
167 abate 341
168 abate 681
169 abate 1064 let boxes_of (Page -> [H:Xul?])
170 abate 1065 <page>[ (items::Item | _)*] & p ->
171 abate 1064 let toc = transform items with
172 abate 1065 | <box title=t link=l>_ -> [ <li>[ <a href=[ !(url_of_page p) '#' !l ]>t ] ]
173 abate 1064 in
174     ul toc
175 abate 351
176 abate 1065 let link_to (<page>[<title>t ; _ ] & p : Page) : H:Xa =
177 abate 1064 let t = match p with
178     | <_ new="">_ -> t @ [ <img src="img/new.gif" alt="(new)" style="border:0">[]]
179     | _ -> t in
180 abate 1065 <a href=(url_of_page p)>t
181 abate 1064
182 abate 1065 let small_box (x : H:Flow) : H:Block =
183 abate 580 <table cellpadding="2"
184 abate 586 style="font-size:11px ; font-family:arial,sans-serif;
185     border: solid 2px black; background: #ffffff" width="100%">
186 abate 580 [ <tr> [<td>x] ]
187    
188 abate 713 let meta (x : H:Flow) : H:Block =
189 abate 580 <table cellpadding="2"
190 abate 797 style="border: solid 1px #b0b0b0; background: #e0e0e0; font-size: 80%"
191 abate 580 width="100%">
192     [ <tr> [<td>x] ]
193    
194 abate 1058 let box_title (x : H:Flow, a : String, t : String) : H:Block =
195 abate 580 <table cellpadding="5"
196     style="border: solid 2px black; background: #ffffff" width="100%">
197 abate 1058 [ <tr>[
198     <td style="background: #fff0f0; color: #0000ff; font: bold 100%
199     helvetica">[<a name=a>t] ]
200     <tr> [<td>x] ]
201 abate 580
202 abate 1065 let box (x : H:Flow) : H:Block =
203     <table cellpadding="5"
204     style="border: solid 2px black; background: #ffffff" width="100%">
205     [ <tr> [<td>x] ]
206    
207 abate 580 let style = "
208     a:link:hover, a:visited:hover {
209     text-decoration: none;
210     background: #FFFFD0;
211     color: #FF0000;
212     }
213 abate 1064 a.old, a.old:hover, a.old:visited:hover { text-decoration: line-through; }
214     p { text-align: justify; margin: 1ex 1em 0 1em; }
215     pre { margin: 1ex 1em 0 1em; }
216     strong.ocaml{ color: #333b8e; }
217     strong.highlight { color: #FF0000; }
218     img.icon { border: 0; }
219     div.code { background: #E0E0E0; margin: 0.5ex 0.5em 0 0.5em; padding: 0.2ex; }
220     div.xmlcode { background:#ebefa2; margin: 0.5ex 0.5em 0 0.5em; padding: 0.2ex;}
221 abate 976
222 abate 580 div.abstract {
223 abate 736 font: bold helvetica;
224 abate 580 margin: 1ex 1em 1ex 1em;
225     padding: 1ex 1em 1ex 1em;
226     background: #F0F0F0;
227     }
228 abate 976
229     div.note {
230 abate 979 text-align: justify;
231 abate 976 font: bold helvetica;
232     margin: 1ex 3em 1ex 3em;
233     padding: 1ex 1em 1ex 1em;
234     background: #D0E2D2;
235     }
236    
237 abate 981 div.session
238     {
239     font: bold 80% helvetica;
240     margin: 1ex 1em 1ex 1em;
241     padding: 1ex 1em 1ex 1em;
242     border: solid .5px grey;
243     }
244    
245 abate 1064 div.abstract p { font: sans-serif; }
246 abate 580 "
247    
248 abate 1064 type PageO = Page | []
249    
250 abate 343 (* Main transformation function *)
251    
252 abate 386 (* returns the last page of the descendance *)
253 abate 1348
254     let thumbnail(w : String, h : String)
255     (url : String)(title : String) : H:Inlines =
256     [ <a href=url>[
257     <img src=url width=w height=h alt="Click to enlarge" title=title>[] ] ]
258    
259     let thumbwh({ width =? IntStr; height =? IntStr} ->
260     (String -> String ->H:Inlines))
261     | { width = w; height = h } ->
262     let w = int_of w in let h = int_of h in
263     (match h with
264     | 0 -> raise "Thumbnail height = 0"
265     | h -> let w = string_of ((w * 200) div h) in thumbnail (w,"200"))
266     | _ -> thumbnail ("266","200")
267    
268 abate 1065 let gen_page (site : String,
269     prev : PageO, page : Page, next : PageO,
270 abate 1064 path : Path, sitemap : Tree) : PageO =
271     match page with
272 abate 381 <page name=name>[
273 abate 1064 <title>title <banner>banner | <title>(title & banner); items ] ->
274 abate 341
275 abate 625 let footnote_counter = ref Int 0 in
276 abate 713 let footnotes = ref H:Flow [] in
277 abate 625
278 abate 713 let text (t : [InlineText*]) : H:Inlines =
279 abate 625 transform t with
280     | <code>x -> [ <b>[ <tt>(highlight x) ] ]
281 abate 1375 | <local href=l>txt ->
282     [ (local_link (sitemap,l,txt)) ]
283 abate 625 | <(tag & (`b|`i|`tt|`em)) (attr)>x -> [ <(tag) (attr)>(text x) ]
284 abate 1264 | <footnote nocount=_>_ ->
285     let n = string_of !footnote_counter in
286     [ <a name=[ 'bnote' !n ]>[]
287     <a href=[ '#note' !n ]>[ '[' !n ']' ] ]
288    
289 abate 625 | <footnote>c ->
290     footnote_counter := !footnote_counter + 1;
291     let n = string_of !footnote_counter in
292     let fn = !footnotes in
293     footnotes := [];
294     let c = <p>[ <a name=[ 'note' !n ]>[]
295     <a href=[ '#bnote' !n ]>[ '[' !n ']' ]
296     ' ' ; text c ] in
297     footnotes := fn @ [ c ] @ !footnotes;
298     [ <a name=[ 'bnote' !n ]>[]
299     <a href=[ '#note' !n ]>[ '[' !n ']' ] ]
300 abate 1348 | <thumbnail ({href=url} & r)>[] ->
301     thumbwh r url ""
302     | <thumbnails ({href=url} & r)>l ->
303     let l = split_thumbnails l in
304     let f = thumbwh r in
305     let c = ref Int 0 in
306     (transform l with (x,y) ->
307     let t = f (url @ x) y in
308     if (!c = 4) then (c := 1; [ <br>[] ] @ t)
309     else (c := !c + 1; t))
310 abate 698 | z -> [ z ]
311 abate 341 in
312 abate 254
313 abate 713 let content (t : Content) : H:Flow =
314 abate 250 transform t with
315 abate 284 | <section title=title>c ->
316 abate 580 [ <p>[ <b style="color: #008000">title ] !(content c) ]
317 abate 250 | <paper (r)>[ <title>tit aut::Author* <comment>com <abstract>ab ] ->
318 abate 734 [ (match r with
319 abate 1064 | { file = f; old = "" } -> <a class="old" href=f>tit
320 abate 734 | { file = f } -> <a href=f>tit
321     | _ -> <b>tit) '. '
322 abate 250 !(authors aut) '. '
323 abate 254 !(text com)
324 abate 250 <div class="abstract">[ 'Abstract:' !(content ab) ]
325     ]
326     | <slides file=f>[ <title>tit aut::Author* <comment>com ] ->
327 abate 254 [ <a href=f>tit '. ' !(authors aut) '. ' !(text com) ]
328 abate 344 | <sample highlight="false">s ->
329     [ <div class="code">[ <pre>s ] ]
330 abate 336 | <sample>s ->
331 abate 340 [ <div class="code">[ <pre>(highlight s) ] ]
332 abate 979 | <xmlsample highlight="false">s ->
333     [ <div class="xmlcode">[ <pre>s ] ]
334     | <xmlsample>s ->
335     [ <div class="xmlcode">[ <pre>(highlight s) ] ]
336 abate 981 | <sessionsample highlight="false">s ->
337     [ <div class="session">[ <pre>s ] ]
338     | <sessionsample>s ->
339     [ <div class="session">[ <pre>(highlight s) ] ]
340 abate 561 | <link url=url title=title>com ->
341 abate 1065 [ <ul>[ <li>[ <a href=url>title '. ' !(text com) ] ] ]
342 abate 284 | <ul>lis ->
343 abate 1064 ul (map lis with <li>x -> <li>(content x))
344 abate 1264 | <ol (attr) >lis ->
345     ol ((map lis with <li>x -> <li>(content x) ),(attr))
346 abate 713 | H:Xtable & x ->
347 abate 1201 [ <table width="100%">[<tr>[<td align="center">[x]]] ]
348 abate 614 | <p (attr)>x -> [ <p (attr)>(text x) ]
349 abate 681 | <pages-toc (a)>[] ->
350 abate 1064 let toc = transform items with
351     | Page & p ->
352     let sects = match a with {|sections=_|} -> boxes_of p | _ -> [] in
353     [ <li>[ (link_to p) ; sects ] ]
354     | <external href=l title=t>[] -> [ <li>[ <a href=l>t ] ] in
355     ul toc
356 abate 696 | <boxes-toc (a)>[] ->
357 abate 1065 let sections = match a with { section=_ } -> `true | _ -> `false in
358     let short = match a with { short=_ } -> `true | _ -> `false in
359 abate 1064 let toc = transform items with
360 abate 1065 | <box ({title=t; link=l} & ({short=s} | {title=s}))>b ->
361     let t = if short then s else t in
362     let sects =
363     if sections then
364 abate 1064 (transform b with <section title=t>_ -> [<br>[] '-' !t])
365 abate 1065 else [] in
366 abate 1064 [ <li>[ <a href=('#',l)>t !sects ]] in
367     ul toc
368 abate 341 | <site-toc>[] ->
369     [ <ul>[ (display_sitemap sitemap) ] ]
370     | <local-links href=s>[] ->
371 abate 1064 ul (map (split_comma s) with x -> <li>[ (local_link(sitemap,x,"")) ])
372 abate 356 | <two-columns>[ <left>x <right>y ] ->
373     [ <table width="100%">[
374     <tr>[
375     <td valign="top">(content x)
376     <td valign="top">(content y) ] ] ]
377 abate 1065 | <note title=t>c -> [ <div class="note">[ <b>[!t ': '] !(content c) ]]
378 abate 1064 | <note>c -> [ <div class="note">[ <b>"Note: " !(content c) ]]
379     | <footnotes>[] ->
380     (match !footnotes with
381     | [] -> []
382     | n -> footnotes := []; [ <br>[] (meta n) ] )
383 abate 341 | t -> text [ t ]
384     in
385 abate 250
386 abate 580 (* Preparing left panel *)
387 abate 698
388 abate 580 let navig = transform items with <left>c -> [ c ] in
389     let left = match navig with [] -> [ [<boxes-toc>[]] ] | n -> n in
390     let left =
391     <td valign="top" align="left">[
392 abate 589 <table cellpadding="0" cellspacing="15"
393 abate 586 width="200"
394     style="font-size:80%; border: 1px dashed black;
395 abate 797 background: #ffcd72"> (* altbg 9aa8ba *)
396 abate 1065 (map left with x -> <tr>[ <td>[ (small_box (content x)) ] ]) ] in
397 abate 580
398 abate 713 let dpath : H:Inlines = transform path with
399 abate 391 | { url = f; title = t } -> [ <a href=f>t ': ']
400 abate 341 in
401 abate 1065 let npath = path @ [ { url = (url_of_page page); title = title } ] in
402 abate 351 let subpages = transform items with p & Page -> [ p ] in
403 abate 1065 let (next,last) = gen_page_seq (site,page, subpages, next, npath, sitemap) in
404 abate 391 let next = match next with [] -> []
405 abate 1065 | <page>[ <title>t; _ ] & p ->
406     [ <a href=(url_of_page p)>[
407 abate 595 <img width="16" height="16" class="icon" alt="Next page:"
408 abate 586 src="img/right.gif">[]
409 abate 391 ' ' !t
410     ] ] in
411     let prev = match prev with [] -> []
412 abate 1065 | <page>[ <title>t; _ ] & p ->
413     [ <a href=(url_of_page p)>[
414 abate 586 <img width="16" height="16" class="icon"
415 abate 595 alt="Previous page:" src="img/left.gif">[]
416 abate 391 ' ' !t
417     ] ] in
418 abate 580 let navig =
419 abate 391 if prev = [] then [] else
420 abate 1065 [ (small_box [
421 abate 391 <p>[ !dpath !title ]
422 abate 580 <p>[ !prev ' ' !next ] ]) ] in
423    
424     (* Preparing main panel *)
425     let main = transform items with
426 abate 1065 | <box title=t link=l>c -> [ (box_title (content c, l, t)) ]
427     | <box>c -> [ (box (content c)) ]
428 abate 1064 | <footnotes>[] ->
429     (match !footnotes with
430     | [] -> []
431     | n -> footnotes := []; [ (meta n) ] )
432 abate 1058 | <meta>c -> [ (meta (content c)) ]
433 abate 580 in
434 abate 625 let notes = match !footnotes with
435     | [] -> []
436     | n -> [ (meta n) ] in
437     let main = match (navig @ main @ notes @ navig) with
438 abate 580 | [] -> raise "Empty page !"
439     | x -> x in
440    
441 abate 713 let right : H:Xtd =
442 abate 580 <td valign="top" align="left" style="width:100%">[
443     <table width="100%">[
444     <tr>[ <td valign="top" align="left"
445     style="border: 2px solid black; background: #ffffff;
446     text-align:center; color: #aa0000; font: bold 200% helvetica" >
447     (text banner)
448     ]
449    
450     <tr>[
451     <td valign="top" align="left"
452 abate 797 style="border: 1px solid black; background: #fccead">[ (* altbg c8ccd1 *)
453 abate 589 <table width="100%" cellpadding="0" cellspacing="17">
454 abate 580 (map main with x -> <tr>[ <td>[x] ])
455     ] ]
456     ] ] in
457    
458 abate 713 let html : H:Xhtml =
459 abate 250 <html>[
460     <head>[
461 abate 1065 <title>[ !site ': ' !title ]
462 abate 561 <meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type">[]
463 abate 580 <style type="text/css">style
464 abate 250 ]
465 abate 797 <body style="margin: 0; padding : 0; background: #fcb333">[ (* altbg 4e6e99 *)
466 abate 580 <table cellspacing="10" cellpadding="0" width="100%" border="0">[
467     <tr>[ left right ]
468     ]
469 abate 250 ]
470 abate 341 ]
471     in
472 abate 391 let txt : Latin1 =
473 abate 351 [ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"'
474 abate 341 ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
475 abate 580 !(print_xml html) ] in
476     let fn = "www/" @ name @ ".html" in
477 abate 625 dump_to_file fn txt;
478 abate 488 last
479 abate 386
480 abate 250
481 abate 488 let gen_page_seq
482 abate 1065 (site : String,
483     prev : PageO, pages : [Page*], next : PageO,
484 abate 1064 path : Path, sitemap : Tree) : (PageO, PageO) =
485 abate 351 match pages with
486     | [ p1 p2 ; _ ] & [ _; rest ] ->
487 abate 1065 let last = gen_page (site,prev,p1,p2, path, sitemap) in
488     let (_,last) = gen_page_seq (site,last, rest, next, path, sitemap) in
489 abate 386 (p1,last)
490 abate 351 | [ p ] ->
491 abate 1065 let last = gen_page (site,prev,p,next, path, sitemap) in (p,last)
492 abate 488 | [] -> (next,prev)
493 abate 351
494    
495 abate 488 ;;
496 abate 284
497 abate 369 match load_include input with
498 abate 1065 | [ <site>[ <title>(site & String) (p & Page) ] ] ->
499     let _ = gen_page (site,[],p,[], [], compute_sitemap p) in []
500 abate 488 | _ -> raise ("Invalid input document " @ input)

CVS Admin">CVS Admin
ViewVC Help
Powered by ViewVC 1.1.5