]>
Commit | Line | Data |
---|---|---|
c05220db | 1 | #!/usr/bin/perl -w |
2 | ||
3 | use strict; | |
4 | use IO::File; | |
5 | use Getopt::Long; | |
6 | ||
7 | # This mess is designed to parse the squid config template file | |
8 | # cf.data.pre and generate a set of HTML pages to use as documentation. | |
9 | # | |
10 | # Adrian Chadd <adrian@squid-cache.org> | |
11 | # | |
12 | # $Id: build-cfg-help.pl,v 1.1 2007/05/18 00:43:28 hno Exp $ | |
13 | ||
14 | # | |
15 | # The template file is reasonably simple to parse. There's a number of | |
16 | # directives which delineate sections but there's no section delineation. | |
17 | # A section will "look" somewhat like this, most of the time: | |
18 | # NAME: <name> | |
19 | # IFDEF: <the ifdef bit> | |
20 | # TYPE: <the config type> | |
21 | # DEFAULT: <the default value> | |
22 | # LOC: <location in the Config struct> | |
23 | # DOC_START | |
24 | # documentation goes here | |
25 | # NOCOMMENT_START | |
26 | # stuff which goes verbatim into the config file goes here | |
27 | # NOCOMMENT_END | |
28 | # DOC_END | |
29 | # | |
30 | # Now, we can't assume its going to be nicely nested, so I'll say that | |
31 | # sections are delineated by NAME: lines, and then stuff is marked up | |
32 | # appropriately. | |
33 | # | |
34 | # Then we have to fake paragraph markups as well for the documentation. | |
35 | # We can at least use <PRE> type markups for the NOCOMMENT_START/_END stuff. | |
36 | ||
37 | # | |
38 | # Configuration sections are actually broken up by COMMENT_START/COMMENT_END | |
39 | # bits, which we can use in the top-level index page. Nifty! | |
40 | # | |
41 | ||
42 | # XXX NAME: can actually have multiple entries on it; we should generate | |
43 | # XXX a configuration index entry for each, linking back to the one entry. | |
44 | # XXX I'll probably just choose the first entry in the list. | |
45 | ||
46 | # | |
47 | # This code is ugly, but meh. We'll keep reading, line by line, and appending | |
48 | # lines into 'state' variables until the next NAME comes up. We'll then | |
49 | # shuffle everything off to a function to generate the page. | |
50 | ||
51 | ||
52 | my ($state) = ""; | |
53 | my (%option); | |
54 | my (%all_names); | |
55 | my ($comment); | |
56 | ||
57 | my $version = "2.HEAD"; | |
58 | my $verbose = ''; | |
59 | my $path = "/tmp"; | |
60 | my $format = "splithtml"; | |
61 | my $pagetemplate; | |
62 | ||
63 | my ($index) = new IO::File; | |
64 | ||
65 | my $top = $0; | |
66 | $top =~ s%[^/]*$%%; | |
67 | ||
68 | GetOptions( | |
69 | 'verbose' => \$verbose, 'v' => \$verbose, | |
70 | 'out=s' => \$path, | |
71 | 'version=s' => \$version, | |
72 | 'format=s' => \$format | |
73 | ); | |
74 | ||
75 | if ($format eq "splithtml") { | |
76 | $pagetemplate = "template.html"; | |
77 | } elsif ($format eq "singlehtml") { | |
78 | $pagetemplate = "template_single.html"; | |
79 | } | |
80 | ||
81 | # XXX should implement this! | |
82 | sub uriescape($) | |
83 | { | |
84 | my ($line) = @_; | |
85 | return $line; | |
86 | } | |
87 | ||
88 | sub filename($) | |
89 | { | |
90 | my ($name) = @_; | |
91 | return $path . "/" . $name . ".html"; | |
92 | } | |
93 | ||
94 | sub htmlescape($) | |
95 | { | |
96 | my ($line) = @_; | |
97 | return "" if !defined $line; | |
98 | $line =~ s/([^\w\s])/sprintf ("&#%d;", ord ($1))/ge; | |
99 | return $line; | |
100 | } | |
101 | ||
102 | sub section_link($) | |
103 | { | |
104 | return uriescape($_[0]).".html" if $format eq "splithtml"; | |
105 | return "#".$_[0] if $format eq "singlehtml"; | |
106 | } | |
107 | ||
108 | sub toc_link($) | |
109 | { | |
110 | return "index.html#toc_".uriescape($_[0]) if $format eq "splithtml"; | |
111 | return "#toc_".uriescape($_[0]) if $format eq "singlehtml"; | |
112 | } | |
113 | ||
114 | sub alpha_link($) | |
115 | { | |
116 | return "index_all.html#toc_".uriescape($_[0]); | |
117 | } | |
118 | ||
119 | # | |
120 | # Yes, we could just read the template file in once..! | |
121 | # | |
122 | sub generate_page($$) | |
123 | { | |
124 | my ($template, $data) = @_; | |
125 | my $fh; | |
126 | my $fh_open = 0; | |
127 | # XXX should make sure the config option is a valid unix filename! | |
128 | if ($format eq "splithtml") { | |
129 | my ($fn) = filename($data->{'name'}); | |
130 | $fh = new IO::File; | |
131 | $fh->open($fn, "w") || die "Couldn't open $fn: $!\n"; | |
132 | $fh_open = 1; | |
133 | } else { | |
134 | $fh = $index; | |
135 | } | |
136 | ||
137 | ||
138 | my ($th) = new IO::File; | |
139 | $th->open($template, "r") || die "Couldn't open $template: $!\n"; | |
140 | ||
141 | # add in the local variables | |
142 | $data->{"title"} = $data->{"name"}; | |
143 | $data->{"ldoc"} = $data->{"doc"}; | |
144 | $data->{"toc_link"} = toc_link($data->{"name"}); | |
145 | $data->{"alpha_link"} = alpha_link($data->{"name"}); | |
146 | if (exists $data->{"aliases"}) { | |
147 | $data->{"aliaslist"} = join(", ", @{$data->{"aliases"}}); | |
148 | } | |
149 | # XXX can't do this and then HTML escape.. | |
150 | # $data->{"ldoc"} =~ s/\n\n/<\/p>\n<p>\n/; | |
151 | # XXX and the end-of-line formatting to turn single \n's into <BR>\n's. | |
152 | ||
153 | while (<$th>) { | |
154 | # Do variable substitution | |
155 | s/%(.*?)%/htmlescape($data->{$1})/ge; | |
156 | print $fh $_; | |
157 | } | |
158 | close $th; | |
159 | undef $th; | |
160 | ||
161 | if ($fh_open) { | |
162 | close $fh; | |
163 | undef $fh; | |
164 | } | |
165 | } | |
166 | ||
167 | $index->open(filename("index"), "w") || die "Couldn't open ".filename("index").": $!\n" if ($format eq "splithtml"); | |
168 | $index->open($path, "w") || die "Couldn't open ".filename("index").": $!\n" if ($format eq "singlehtml"); | |
169 | print $index <<EOF | |
170 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
171 | <html xmlns="http://www.w3.org/1999/xhtml"> | |
172 | <head> | |
173 | <meta http-equiv="content-type" content="text/html; charset=utf-8" /> | |
174 | <title>Squid $version configuration file</title> | |
175 | <meta name="keywords" content="squid squid.conf config configure" /> | |
176 | <meta name="description" content="Squid $version" /> | |
177 | </head> | |
178 | <body> | |
179 | EOF | |
180 | ; | |
181 | ||
182 | ||
183 | my ($name, $data); | |
184 | my (@chained); | |
185 | ||
186 | my $in_options = 0; | |
187 | sub start_option($) | |
188 | { | |
189 | my ($name) = @_; | |
190 | if (!$in_options) { | |
191 | print $index "<ul>\n"; | |
192 | $in_options = 1; | |
193 | } | |
194 | print $index ' <li><a href="' . htmlescape(section_link($name)) . '" name="toc_' . htmlescape($name) . '">' . htmlescape($name) . "</a></li>\n"; | |
195 | } | |
196 | sub end_options() | |
197 | { | |
198 | return if !$in_options; | |
199 | print $index "</ul>\n"; | |
200 | $in_options = 0; | |
201 | } | |
202 | while (<>) { | |
203 | chomp; | |
204 | last if (/^EOF$/); | |
205 | if ($_ =~ /^NAME: (.*)$/) { | |
206 | my (@aliases) = split(/ /, $1); | |
207 | $data = {}; | |
208 | $data->{'version'} = $version; | |
209 | foreach (@aliases) { | |
210 | $all_names{$_} = $data; | |
211 | } | |
212 | ||
213 | $name = shift @aliases; | |
214 | ||
215 | $option{$name} = $data; | |
216 | $data->{'name'} = $name; | |
217 | $data->{'aliases'} = \@aliases; | |
218 | ||
219 | start_option($name); | |
220 | print "DEBUG: new option: $name\n" if $verbose; | |
221 | } elsif ($_ =~ /^COMMENT: (.*)$/) { | |
222 | $data->{"comment"} = $1; | |
223 | } elsif ($_ =~ /^TYPE: (.*)$/) { | |
224 | $data->{"type"} = $1; | |
225 | } elsif ($_ =~ /^DEFAULT: (.*)$/) { | |
226 | if ($1 eq "none") { | |
227 | $data->{"default"} = "$1"; | |
228 | } else { | |
229 | $data->{"default"} = "$name $1"; | |
230 | } | |
231 | } elsif ($_ =~ /^LOC:(.*)$/) { | |
232 | $data->{"loc"} = $1; | |
233 | $data->{"loc"} =~ s/^[\s\t]*//; | |
234 | } elsif ($_ =~ /^DOC_START$/) { | |
235 | $state = "doc"; | |
236 | } elsif ($_ =~ /^DOC_END$/) { | |
237 | $state = ""; | |
238 | my $othername; | |
239 | foreach $othername (@chained) { | |
240 | $option{$othername}{'doc'} = $data->{'doc'}; | |
241 | } | |
242 | undef @chained; | |
243 | } elsif ($_ =~ /^DOC_NONE$/) { | |
244 | push(@chained, $name); | |
245 | } elsif ($_ =~ /^NOCOMMENT_START$/) { | |
246 | $state = "nocomment"; | |
247 | } elsif ($_ =~ /^DEFAULT_IF_NONE: (.*)$/) { | |
248 | $data->{"default_if_none"} = $1; | |
249 | } elsif ($_ =~ /^NOCOMMENT_END$/) { | |
250 | $state = ""; | |
251 | } elsif ($_ =~ /^IFDEF: (.*)$/) { | |
252 | $data->{"ifdef"} = $1; | |
253 | } elsif ($_ =~ /^#/ && $state eq "doc") { | |
254 | $data->{"config"} .= $_ . "\n"; | |
255 | } elsif ($state eq "nocomment") { | |
256 | $data->{"config"} .= $_ . "\n"; | |
257 | } elsif ($state eq "doc") { | |
258 | $data->{"doc"} .= $_ . "\n"; | |
259 | } elsif ($_ =~ /^COMMENT_START$/) { | |
260 | end_options; | |
261 | $state = "comment"; | |
262 | $comment = ""; | |
263 | } elsif ($_ =~ /^COMMENT_END$/) { | |
264 | print $index "<pre>\n"; | |
265 | print $index $comment; | |
266 | print $index "</pre>\n"; | |
267 | } elsif ($state eq "comment") { | |
268 | $comment .= $_ . "\n"; | |
269 | } elsif (/^#/) { | |
270 | next; | |
271 | } elsif ($_ ne "") { | |
272 | print "NOTICE: unknown line '$_'\n"; | |
273 | } | |
274 | } | |
275 | end_options; | |
276 | print $index "</ul>\n"; | |
277 | print $index "<p><a href=\"index_all.html\">Alphabetic index</a></p>\n" if $format eq "splithtml"; | |
278 | print $index "<p><a href=\"#index\">Alphabetic index</a></p>\n" if $format eq "singlehtml"; | |
279 | print $index "<hr />\n" if $format eq "singlehtml"; | |
280 | ||
281 | # and now, build the option pages | |
282 | my (@names) = keys %option; | |
283 | foreach $name (@names) { | |
284 | generate_page("${top}${pagetemplate}", $option{$name}); | |
285 | } | |
286 | ||
287 | # and now, the alpabetic index file! | |
288 | my $fh; | |
289 | my $fh_open = 0; | |
290 | ||
291 | if ($format eq "splithtml") { | |
292 | $fh = new IO::File; | |
293 | my ($indexname) = filename("index_all"); | |
294 | $fh->open($indexname, "w") || die "Couldn't open $indexname for writing: $!\n"; | |
295 | $fh_open = 1; | |
296 | print $fh <<EOF | |
297 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
298 | <html xmlns="http://www.w3.org/1999/xhtml"> | |
299 | <head> | |
300 | <meta http-equiv="content-type" content="text/html; charset=utf-8" /> | |
301 | <title>Squid $version configuration file</title> | |
302 | <meta name="keywords" content="squid squid.conf config configure" /> | |
303 | <meta name="description" content="Squid $version" /> | |
304 | </head> | |
305 | <body> | |
306 | <div id="header"> | |
307 | <div id="logo"> | |
308 | <h1><a href="http://www.squid-cache.org/"><span>Squid-</span>Cache.org</a></h1> | |
309 | <h2>Optimising Web Delivery</h2> | |
310 | </div> | |
311 | </div> | |
312 | ||
313 | <p>| <a href="index.html">Table of contents</a> |</p> | |
314 | ||
315 | <h1>Alphabetic index of all options</h1> | |
316 | EOF | |
317 | ; | |
318 | } elsif ($format eq "singlehtml") { | |
319 | $fh = $index; | |
320 | print $fh "<h2><a name=\"index\">Alphabetic index of all options</a></h2>\n"; | |
321 | } | |
322 | ||
323 | print $fh "<ul>\n"; | |
324 | ||
325 | foreach $name (sort keys %all_names) { | |
326 | my ($data) = $all_names{$name}; | |
327 | print $fh ' <li><a href="' . uriescape($data->{'name'}) . '.html" name="toc_' . htmlescape($name) . '">' . htmlescape($name) . "</a></li>\n"; | |
328 | } | |
329 | ||
330 | print $fh "</ul>\n"; | |
331 | if ($fh_open) { | |
332 | print $fh <<EOF | |
333 | <p>| <a href="index.html">Table of contents</a> |</p> | |
334 | </body> | |
335 | </html> | |
336 | EOF | |
337 | ; | |
338 | $fh->close; | |
339 | } | |
340 | undef $fh; | |
341 | ||
342 | print $index <<EOF | |
343 | </body> | |
344 | </html> | |
345 | EOF | |
346 | ; | |
347 | $index->close; | |
348 | undef $index; |