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