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