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