1#!/usr/bin/perl -w
2# $LynxId: cfg2html.pl,v 1.16 2012/02/04 00:54:50 tom Exp $
3#
4# This script uses embedded formatting directives in the lynx.cfg file to
5# guide it in extracting comments and related information to construct a
6# set of HTML files.  Comments begin with '#', and directives with '.'.
7# Directives implemented:
8#
9#	h1 {Text}
10#		major heading.  You may specify the same major heading in
11#		more than one place.
12#	h2 {Text}
13#		minor heading, i.e. a keyword.
14#	ex [number]
15#		the following line(s) are an example.  The [number] defaults
16#		to 1.
17#	nf [number]
18#		turn justification off for the given number of lines, defaulting
19#		to the remainder of the file.
20#	fi
21#		turn justification back on
22#	url text
23#		embed an HREF to external site.
24#
25use strict;
26
27use Getopt::Std;
28
29use vars qw($opt_a $opt_m $opt_s);
30
31use vars qw(@cats);
32use vars qw(%cats);
33
34use vars qw(@settings_avail);
35use vars qw(%settings_avail);
36
37# Options:
38#	-a	show all options, not only those that are available.
39#	-m	mark unavailable options with an '*'.  Data for this is read
40#		from standard input.
41#	-s	sort entries in body.html
42&getopts('ams');
43
44if ( defined $opt_m ) {
45	my $l;
46	my @settings_ = <STDIN>;
47	%settings_avail = ();
48	foreach $l (@settings_) {
49		chop $l;
50		if ($l =~ /^[[:alpha:]_][[:alnum:]_]*$/) {
51			$settings_avail{uc $l} = 1;
52		}
53	}
54} else {
55	$opt_a = 1;
56}
57
58# This sub tells whether the support for the given setting was enabled at
59# compile time.
60sub ok {
61	my ($name) = @_;
62	my $ret = defined($settings_avail{uc $name})+0;
63	$ret;
64}
65
66
67if ( $#ARGV < 0 ) {
68	&doit("lynx.cfg");
69} else {
70	while ( $#ARGV >= 0 ) {
71		&doit ( shift @ARGV );
72	}
73}
74exit (0);
75
76
77# process a Lynx configuration-file
78sub doit {
79	my ($name) = @_;
80	my $n;
81
82	# Ignore our own backup files
83	if ( $name =~ ".*~" ) {
84		return;
85	}
86
87	# Read the file into an array in memory.
88	open(FP,$name) || do {
89		print STDERR "Can't open $name: $!\n";
90		return;
91	};
92	my (@input) = <FP>;
93	close(FP);
94
95	for $n (0..$#input) {
96		chop $input[$n]; # trim newlines
97		$input[$n] =~ s/\s*$//; # trim trailing blanks
98		$input[$n] =~ s/^\s*//; # trim leading blanks
99	}
100
101	&gen_alphatoc(@input);
102	@cats = &gen_cattoc(@input);
103	&gen_body(@input);
104}
105
106sub gen_alphatoc {
107	my (@input) = @_;
108	my @minor;
109	my ($n, $m, $c, $d);
110	my $output = "alphatoc.html";
111	open(FP,">$output") || do {
112		print STDERR "Can't open $output: $!\n";
113		return;
114	};
115	print FP <<'EOF';
116<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
117<html>
118<head>
119<link rev="made" href="mailto:lynx-dev@nongnu.org">
120<title>Settings by name</title>
121<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
122</head>
123<body>
124<h1>Alphabetical table of settings</h1>
125EOF
126	$m=0;
127	for $n (0..$#input) {
128		if ( $input[$n] =~ /^\.h2\s*[[:upper:]][[:upper:][:digit:]_]*$/ ) {
129			$minor[$m] = $input[$n];
130			$minor[$m] =~ s/^.h2\s*//;
131			$m++ if (ok($minor[$m]) || defined $opt_a);
132		}
133	}
134	@minor = sort @minor;
135	# index by the first character of each keyword
136	$c=' ';
137	for $n (0..$#minor) {
138		$d = substr($minor[$n],0,1);
139		if ($d ne $c) {
140			printf FP "<a href=\"#%s\">%s</a> \n", $d, $d;
141			$c=$d;
142		}
143	}
144	# index by the first character of each keyword
145	$c=' ';
146	for $n (0..$#minor) {
147		$d = substr($minor[$n],0,1);
148		if ($d ne $c) {
149			printf FP "<h2><a name=%s>%s</a></h2>\n", $d, $d;
150			$c=$d;
151		}
152		my $avail = ok($minor[$n]);
153		my $mark = (!$avail && defined $opt_m) ? "*" : "";
154		if (defined $opt_a || $avail) {
155		    printf FP "<a href=\"body.html#%s\">%s</a>&nbsp;&nbsp;\n", $minor[$n], $minor[$n] . $mark;
156		};
157	}
158	my $str = <<'EOF'
159<p>
160<a href=cattoc.html>To list of settings by category</a>
161EOF
162. (defined $opt_a && defined $opt_m ?
163"<p>Support for all settings suffixed with '*' was disabled at compile time.\n" :
164 "") . <<'EOF'
165</body>
166</html>
167EOF
168	;print FP $str;
169	close(FP);
170}
171
172# This uses the associative array $cats{} to store HREF values pointing into
173# the cattoc file.
174#
175# We could generate this file in alphabetic order as well, but choose to use
176# the order of entries in lynx.cfg, since some people expect that arrangement.
177sub gen_body {
178	my @input = @_;
179	my ($n, $c);
180	my @h2;
181	my $output = "body.html";
182	open(FP,">$output") || do {
183		print STDERR "Can't open $output: $!\n";
184		return;
185	};
186	print FP <<'EOF';
187<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
188<html>
189<head>
190<link rev="made" href="mailto:lynx-dev@nongnu.org">
191<title>Description of settings in lynx configuration file</title>
192<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
193</head>
194<body>
195EOF
196	my $l;
197	my $t;
198	my $d = -1;
199	my $p = 0;
200	my $m = 0;
201	my $h1 = "";
202	my $sp = ' ';
203	my $ex = 0;
204	my $nf = 0;
205	my $any = 0;
206	my $first = 0;
207	my $next = 0;
208	my $left = 0;
209	my %keys;
210	undef %keys;
211
212	my @optnames;
213	my %optname_to_fname;	#this maps optname to fname - will be used
214	    			#for alphabetical output of the content
215	my $curfilename = "tmp000";	#will be incremented each time
216	my $tmpdir = "./";	#temp files will be created there
217	close(FP);
218
219	for $n (0..$#input) {
220		if ( $next ) {
221			$next--;
222			next;
223		}
224		$c = $input[$n];
225		my $count = $#input;
226		my $once = 1;
227		if ( $c =~ /^\.h1\s/ ) {
228			$h1 = 1;
229			$h1 = $c;
230			$h1 =~ s/^.h1\s*//;
231			$m = 0;
232			$first = 1;
233			undef %keys;
234			next;
235		} elsif ( $c =~ /^\.h2\s/ ) {
236			$c =~ s/^.h2\s*//;
237			$h2[$m] = $c;
238			$keys{$c} = 1;
239			$m++;
240			next;
241		} elsif ( $c =~ /^\./ ) {
242			my $s = $c;
243			$s =~ s/^\.[[:lower:]]+\s//;
244			if ( $s =~ /^[[:digit:]]+$/ ) {
245				$count = $s;
246				$once = $s;
247			}
248		}
249		if ( $c =~ /^\.ex/ ) {
250			$ex = $once;
251			printf FP "<h3><em>Example%s:</em></h3>\n", $ex > 1 ? "s" : "";
252		} elsif ( $c =~ /^\.url/ ) {
253			my $url = $c;
254			$url =~ s/^\.url\s+//;
255			printf FP "<blockquote><a href=\"%s\">%s</a></blockquote>\n", $url, $url;
256		} elsif ( $c =~ /^\.nf/ ) {
257			printf FP "<pre>\n";
258			$nf = $count;
259		} elsif ( $c =~ /^\.fi/ ) {
260			printf FP "</pre>\n";
261			$nf = 0;
262		} elsif ( $c =~ /^$/ ) {
263			if ( $m > 1 ) {
264				my $j;
265				for $j (1..$#h2) {
266					close(FP);++$curfilename;
267					push @optnames,$h2[$j];
268					open(FP,">$tmpdir/$curfilename") || do {
269						print STDERR "Can't open tmpfile: $!\n";
270						return;
271					};
272					$optname_to_fname{$h2[$j]} = $curfilename;
273
274					printf FP "<hr>\n";
275					printf FP "<h2><kbd><a name=\"%s\">%s</a></kbd>\n", $h2[$j], $h2[$j];
276					if ( $h1 ne "" ) {
277						printf FP " &ndash; <a href=\"cattoc.html#%s\">%s</a>", $cats{$h1}, $h1;
278					}
279					printf FP "</h2>\n";
280					printf FP "<h3><em>Description</em></h3>\n";
281					printf FP "Please see the description of <a href=\"#%s\">%s</a>\n", $h2[0], $h2[0];
282				}
283				@h2 = "";
284			}
285			$m = 0;
286			$first = 1;
287		} elsif ( $c =~ /^[#[:alpha:]]/ && $m != 0 ) {
288			if ( $first ) {
289				close(FP);++$curfilename;
290				push @optnames,$h2[0];
291				open(FP,">$tmpdir/$curfilename") || do {
292				    print STDERR "Can't open tmpfile: $!\n";
293				    return;
294				};
295				$optname_to_fname{$h2[0]} = $curfilename;
296
297				if ( $any ) {
298					printf FP "<hr>\n";
299				}
300				printf FP "<h2><kbd><a name=\"%s\">%s</a></kbd>\n", $h2[0], $h2[0];
301				if ( $h1 ne "" ) {
302					printf FP " &ndash; <a href=\"cattoc.html#%s\">%s</a>", $cats{$h1}, $h1;
303				}
304				printf FP "</h2>\n";
305				printf FP "<h3><em>Description</em></h3>\n";
306				$any++;
307				$first = 0;
308			}
309
310			# Convert tabs first, to retain relative alignment
311			$c =~ s#^\t#' 'x8#e;
312			while ( $c =~ /\t/ ) {
313				$c =~ s#(^[^\t]+)\t#$1 . $sp x (9 - (length($1) % 8 ))#e;
314			}
315
316			# Strip off the comment marker
317			$c =~ s/^#//;
318
319			# and convert simple expressions:
320			$c =~ s/&/&amp;/g;
321			$c =~ s/>/&gt;/g;
322			$c =~ s/</&lt;/g;
323			#hvv - something wrong was with next statement
324			$c =~ s/'([^ ])'/"<strong>$1<\/strong>"/g;
325
326			my $k = 0;
327			if ( $c =~ /^[[:alpha:]_][[:alnum:]_]+:/ ) {
328				$t = $c;
329				$t =~ s/:.*//;
330				$k = $keys{$t};
331			}
332
333			if ( $c =~ /^$/ ) {
334				if ( $nf ) {
335					printf FP "\n";
336				} else {
337					$p = 1;
338				}
339			} elsif ( $ex != 0 ) {
340				printf FP "<br><code>%s</code><br>\n", $c;
341				$ex--;
342			} elsif ( $k ) {
343				if ( $d != $n && ! $nf ) {
344					printf FP "<h3><em>Default value</em></h3>\n";
345				}
346				$c =~ s/:$/:<em>none<\/em>/;
347				$c =~ s/:/<\/code>:<code>/;
348				$c = "<code>" . $c . "</code>";
349				if ( ! $nf ) {
350					$c .= "<br>";
351				}
352				printf FP "%s\n", $c;
353				$d = $n + 1;
354			} else {
355				if ( $p && ! $nf ) {
356					printf FP "<p>\n";
357				}
358				$p = 0;
359				if ( $input[$n+1] =~ /^#\s*==/ ) {
360					$c = "<br><em>$c</em>";
361					if ( ! $nf ) {
362						$c .= "<br>";
363					}
364					$next++;
365				}
366				printf FP "%s\n", $c;
367			}
368			if ( $nf != 0 && $nf-- == 0 ) {
369				printf FP "</pre>\n";
370			}
371		}
372	}
373	close(FP);
374	# Here we collect files with description of needed lynx.cfg
375	# options in the proper (natural or sorted) order.
376	open(FP,">>$output") || do {
377		print STDERR "Can't open $output: $!\n";
378		return;
379	};
380	{
381	    my @ordered = (defined $opt_s ? (sort keys(%optname_to_fname)) : @optnames);
382	    if (defined $opt_s) {
383		print FP "Options are sorted by name.\n";
384	    } else {
385		print FP "Options are in the same order as lynx.cfg.\n";
386	    }
387	    foreach $l (@ordered) {
388		my $fnm = $tmpdir . $optname_to_fname{$l};
389		open(FP1,"<$fnm") || do {
390		    print STDERR "Can't open $fnm: $!\n";
391		    return;
392		};
393		my $avail = ok($l);
394		if (defined $opt_a || $avail) {
395		    my @lines = <FP1>;
396		    print FP @lines;
397		    if (!$avail && defined $opt_m) {
398			print FP <<'EOF';
399<p>Support for this setting was disabled at compile-time.
400EOF
401		    }
402		}
403		close(FP1);
404	    }
405	    foreach $l (values(%optname_to_fname)) {
406		unlink $l;
407	    }
408	}
409
410	print FP <<'EOF';
411</body>
412</html>
413EOF
414	close(FP);
415}
416
417sub gen_cattoc {
418	my @input = @_;
419	my @major;
420	my %descs;
421	my %index;
422	my ($n, $m, $c, $d, $found, $h1, $nf, $ex, $count, $once);
423	my $output = "cattoc.html";
424
425	open(FP,">$output") || do {
426		print STDERR "Can't open $output: $!\n";
427		return;
428	};
429	print FP <<'EOF';
430<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
431<html>
432<head>
433<link rev="made" href="mailto:lynx-dev@nongnu.org">
434<title>Settings by category</title>
435<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
436</head>
437<body>
438<h1>Settings by category</h1>
439These are the major categories of configuration settings in Lynx:
440<ul>
441EOF
442	$m = -1;
443	$h1 = 0;
444	$nf = 0;
445	for $n (0..$#input) {
446		my $count = $#input;
447		my $once = 1;
448		$c = $input[$n];
449		if ( $input[$n] =~ /^\.h1\s/ ) {
450			$h1 = 1;
451			$c =~ s/^.h1\s*//;
452			$m = $#major + 1;
453			$d = 0;
454			$found = 0;
455			while ( $d <= $#major && ! $found ) {
456				if ( $major[$d] eq $c ) {
457					$m = $d;
458					$found = 1;
459				}
460				$d++;
461			}
462			if ( ! $found ) {
463				$major[$m] = $c;
464				$descs{$major[$m]} = "";
465				$index{$major[$m]} = "";
466			}
467			next;
468		} elsif ( $h1 != 0 ) {
469			if ( $c =~ /^\.(nf|ex)/ ) {
470				my $s = $c;
471				$s =~ s/^\.[[:lower:]]+\s//;
472				if ( $s =~ /^[[:digit:]]+$/ ) {
473					$count = $s;
474					$once = $s;
475				}
476			}
477			if ( $input[$n] =~ /^$/ ) {
478				$h1 = 0;
479			} elsif ( $input[$n] =~ /^\.nf/ ) {
480				$descs{$major[$m]} .= "<pre>" . "\n";
481				$nf = $count;
482			} elsif ( $input[$n] =~ /^\.fi/ ) {
483				$descs{$major[$m]} .= "</pre>" . "\n";
484				$nf = 0;
485			} elsif ( $input[$n] =~ /^\.ex/ ) {
486				$ex = $once;
487				$descs{$major[$m]} .=
488					"<h3><em>Example"
489					.
490					($ex > 1 ? "s" : "")
491					.
492					":</em></h3>\n"
493					. "\n";
494			} elsif ( $input[$n] =~ /^\s*#/ ) {
495				$c = $input[$n];
496				$c =~ s/^\s*#\s*//;
497				$descs{$major[$m]} .= $c . "\n";
498			}
499		}
500		if ( $m >= 0 && $input[$n] =~ /^\.h2\s/ ) {
501			$c = $input[$n];
502			$c =~ s/^.h2\s*//;
503			$index{$major[$m]} .= $c . "\n"
504			    if (defined $opt_a || ok($c));
505			$h1 = 0;
506		}
507		if ( $nf != 0 && $nf-- == 0 ) {
508			$descs{$major[$m]} .= "</pre>\n";
509		}
510	}
511	@major = sort @major;
512	for $n (0..$#major) {
513		$cats{$major[$n]} = sprintf("header%03d", $n);
514		printf FP "<li><a href=\"#%s\">%s</a>\n", $cats{$major[$n]}, $major[$n];
515	}
516	printf FP "</ul>\n";
517	for $n (0..$#major) {
518		printf FP "\n";
519		printf FP "<h2><a name=\"%s\">%s</a></h2>\n", $cats{$major[$n]}, $major[$n];
520		if ($descs{$major[$n]} !~ /^$/) {
521			printf FP "<h3>Description</h3>\n%s\n", $descs{$major[$n]};
522		}
523		$c = $index{$major[$n]};
524		if ( $c ne "" ) {
525			my @c = split(/\n/, $c);
526			@c = sort @c;
527			printf FP "<p>Here is a list of settings that belong to this category\n";
528			printf FP "<ul>\n";
529			for $m (0..$#c) {
530				my $avail = ok($c[$m]);
531				my $mark = (!$avail && defined $opt_m) ? "*" : "";
532				printf FP "<li><a href=\"body.html#%s\">%s</a>\n", $c[$m], $c[$m] . $mark;
533			}
534			printf FP "</ul>\n";
535		}
536	}
537	my $str = <<'EOF'
538<p>
539<a href=alphatoc.html>To list of settings by name</a>
540EOF
541. (defined $opt_a && defined $opt_m ?
542"<p>Support for all settings suffixed with '*' was disabled at compile time." :
543 "") . <<'EOF'
544</body>
545</html>
546EOF
547	;print FP $str;
548	close(FP);
549	return @cats;
550}
551