summaryrefslogtreecommitdiff
path: root/doc/doc-scripts
diff options
context:
space:
mode:
authorPhilip Hazel <ph10@hermes.cam.ac.uk>2004-10-07 15:04:35 +0000
committerPhilip Hazel <ph10@hermes.cam.ac.uk>2004-10-07 15:04:35 +0000
commit495ae4b01f36d0d8bb0e34a1d7263c2b8224aa4a (patch)
treefcfaa2c623d4f155eef907b50b950b602829a30b /doc/doc-scripts
parent0756eb3cb50d73a77b486e47528f7cb1bffdb299 (diff)
Start
Diffstat (limited to 'doc/doc-scripts')
-rwxr-xr-xdoc/doc-scripts/BuildFAQ59
-rwxr-xr-xdoc/doc-scripts/BuildHTML12
-rwxr-xr-xdoc/doc-scripts/BuildInfo32
-rwxr-xr-xdoc/doc-scripts/BuildPDF10
-rwxr-xr-xdoc/doc-scripts/DoConts71
-rwxr-xr-xdoc/doc-scripts/DoIndex430
-rwxr-xr-xdoc/doc-scripts/JoinPS130
-rw-r--r--doc/doc-scripts/Makefile31
-rwxr-xr-xdoc/doc-scripts/f2h338
-rwxr-xr-xdoc/doc-scripts/f2txt107
-rwxr-xr-xdoc/doc-scripts/faqchk102
-rwxr-xr-xdoc/doc-scripts/fc2k344
-rwxr-xr-xdoc/doc-scripts/g2h1451
-rwxr-xr-xdoc/doc-scripts/g2man251
-rwxr-xr-xdoc/doc-scripts/g2t1347
15 files changed, 4715 insertions, 0 deletions
diff --git a/doc/doc-scripts/BuildFAQ b/doc/doc-scripts/BuildFAQ
new file mode 100755
index 000000000..9712eff4e
--- /dev/null
+++ b/doc/doc-scripts/BuildFAQ
@@ -0,0 +1,59 @@
+#! /bin/sh
+# $Cambridge: exim/doc/doc-scripts/BuildFAQ,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# Script to build the Exim FAQ in text and HTML formats.
+
+/bin/rm -f FAQ.txt* html/FAQ* FAQ-html/* FAQ-html.tar.*
+/bin/rm -f config.samples.tar.gz config.samples.tar.bz2
+
+# The FAQchk Perl script checks for the numbers being in order and for the
+# right number of blank lines at various places.
+
+faqchk FAQ.src
+if [ $? != 0 ]; then exit 1; fi
+
+# HTML version
+
+f2h FAQ.src html
+echo "html/FAQ*.html made"
+
+fc2k
+echo "html/FAQ-KWIC*.html made"
+
+cp html/FAQ* html/*.txt FAQ-html
+echo "copied to FAQ-html"
+
+tar cf FAQ-html.tar FAQ-html
+gzip FAQ-html.tar
+echo "FAQ-html.tar.gz made"
+
+tar cf FAQ-html.tar FAQ-html
+bzip2 -9 FAQ-html.tar
+echo "FAQ-html.tar.gz2 made"
+
+# ASCII version
+
+f2txt FAQ.src FAQ.txt
+echo "FAQ.txt made"
+
+cp FAQ.txt FAQ.txt-t
+gzip -v --best FAQ.txt-t
+mv FAQ.txt-t.gz FAQ.txt.gz
+echo "FAQ.txt.gz made"
+
+cp FAQ.txt FAQ.txt-t
+bzip2 -v -9 FAQ.txt-t
+mv FAQ.txt-t.bz2 FAQ.txt.bz2
+echo "FAQ.txt.bz2 made"
+
+# Configuration samples
+
+tar cf config.samples.tar config.samples
+gzip config.samples.tar
+echo "config.samples.tar.gz made"
+
+tar cf config.samples.tar config.samples
+bzip2 -9 config.samples.tar
+echo "config.samples.tar.bz2 made"
+
+# End
diff --git a/doc/doc-scripts/BuildHTML b/doc/doc-scripts/BuildHTML
new file mode 100755
index 000000000..9d60034ea
--- /dev/null
+++ b/doc/doc-scripts/BuildHTML
@@ -0,0 +1,12 @@
+#! /bin/sh
+# $Cambridge: exim/doc/doc-scripts/BuildHTML,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+if [ $# != 1 ]; then
+ echo "*** Usage: BuildHTML <Exim version>"
+ exit 1
+fi
+
+g2h -split chapter filter.src "Exim Filter Specification"
+g2h -split chapter spec.src "Exim $1 Specification"
+
+# End
diff --git a/doc/doc-scripts/BuildInfo b/doc/doc-scripts/BuildInfo
new file mode 100755
index 000000000..9f8d10591
--- /dev/null
+++ b/doc/doc-scripts/BuildInfo
@@ -0,0 +1,32 @@
+#! /bin/sh
+# $Cambridge: exim/doc/doc-scripts/BuildInfo,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+if [ "$1" = "filter" ]; then
+g2t -filter filter.src >filter.texinfo
+if [ $? != 0 ]; then exit 1; fi
+cd info
+makeinfo filter.texinfo
+if [ $? != 0 ]; then exit 1; fi
+echo ""
+echo info filter.info
+echo ""
+info -f ./filter.info
+exit
+fi
+
+if [ "$1" = "" ]; then
+g2t spec.src >spec.texinfo
+if [ $? != 0 ]; then exit 1; fi
+cd info
+makeinfo spec.texinfo
+if [ $? != 0 ]; then exit 1; fi
+echo ""
+echo info spec.info
+echo ""
+info -f ./spec.info
+exit
+fi
+
+echo "***Usage: null or filter argument required"
+
+####
diff --git a/doc/doc-scripts/BuildPDF b/doc/doc-scripts/BuildPDF
new file mode 100755
index 000000000..c8a2bc2be
--- /dev/null
+++ b/doc/doc-scripts/BuildPDF
@@ -0,0 +1,10 @@
+#! /bin/sh
+# $Cambridge: exim/doc/doc-scripts/BuildPDF,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+echo "PDFing the spec"
+ps2pdf spec.ps spec.pdf
+
+echo "PDFing the filter spec"
+ps2pdf filter.ps filter.pdf
+
+# End
diff --git a/doc/doc-scripts/DoConts b/doc/doc-scripts/DoConts
new file mode 100755
index 000000000..410c3baa1
--- /dev/null
+++ b/doc/doc-scripts/DoConts
@@ -0,0 +1,71 @@
+#! /usr/bin/perl -w
+# $Cambridge: exim/doc/doc-scripts/DoConts,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+$style = (@ARGV > 0)? $ARGV[0] : "a4ps";
+
+open(IN, "z-rawindex") || die "Can't open z-rawindex\n";
+open(OUT, ">z-contents") || die "Can't open z-contents\n";
+
+print OUT <<'EOF';
+.if ~~sys.fancy
+.linelength ~~sys.linelength + 0.2in
+.pagedepth ~~sys.pagedepth - 0.2in
+.linedepth 12.24
+.fi
+.include "markup.sg"
+.set chapter -1
+.set p 0
+.format p roman
+.tabset 2em 2em
+.
+.foot
+.set p ~~sys.pagenumber
+$c [~~p]
+.endfoot
+.
+.chapter Contents
+.disable filling
+.justify left
+EOF
+
+while(<IN>)
+ {
+ if (/\$e/)
+ {
+ s/\$e\s*$//; # "see also" lines have no line number
+ s/--\s*\d+$//; # remove "extra" number for index page
+
+ s/\n$//; # trailing newline
+
+ if (!/^\$/)
+ {
+ print OUT ".blank\n";
+ print OUT ".if ~~sys.leftonpage < 2*~~sys.linedepth\n";
+ print OUT ".newpage\n";
+ print OUT ".fi\n";
+ print OUT "\$shead\{$_\}\n";
+ print OUT ".blank\n";
+ }
+ else
+ {
+ print OUT "$_\n";
+ }
+ }
+ }
+
+close(IN);
+close(OUT);
+
+system("sgcal z-contents -to zc-gcode -style $style -index /dev/null");
+if ($style eq "a4ps")
+ {
+ system("sgtops zc-gcode -to zc-ps");
+ print "PostScript in zc-ps\n";
+ }
+else
+ {
+ system("mv -f zc-gcode zc-txt");
+ print "Text in zc-txt\n";
+ }
+
+# End
diff --git a/doc/doc-scripts/DoIndex b/doc/doc-scripts/DoIndex
new file mode 100755
index 000000000..1caddbd6f
--- /dev/null
+++ b/doc/doc-scripts/DoIndex
@@ -0,0 +1,430 @@
+#! /usr/bin/perl -w
+# $Cambridge: exim/doc/doc-scripts/DoIndex,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# Script for producing the Index for the Exim manual from the output of the
+# SGCAL run. This is copied from the script for the Exim book.
+
+
+##############################################################################
+# Patterns for matching things to be removed from the sort keys
+
+# This was copied from the Exim book processor, but we have now found a
+# better way of doing this. Leave the code until I am quite sure...
+
+# $pat[0] = qr/ \(\\\*see also\*\\[^)]+\)/;
+# $pat[1] = qr/(?<!@)\/\//; # //
+# $pat[2] = qr/(?<!@)\/\\/; # /\
+# $pat[3] = qr/(?<!@)\\\//; # \/
+# $pat[4] = qr/(?<!@) \\ # non-@ \, followed by one of
+# (?:
+# [\.\/] | # dot or slash
+# !- | # !-
+# !\+ | # !+
+# !\. | # !.
+# "\+ | # "+
+# \([.\/]? | # ( and optional . or slash
+# [[\$\\%?!-"] | # [ $ \ % ! " or -
+# \*{1,2} | # * or **
+# \^{1,2}\/? # ^ or ^^ and optional slash
+# )/x;
+# $pat[5] = qr/(?: []\$\\%)?!"] | # ] $ \ % ) ? " or ! )
+# \*{1,2} | # * or ** ) optional
+# \^{1,2})? # ^ or ^^ )
+# \\/x; # then \
+# $pat[6] = qr/(?<!@)::/;
+# $pat[7] = qr/\sR[FS]\b/;
+# $pat[8] = qr/``/;
+# $pat[9] = qr/''/;
+# $pat[10] = qr/`/;
+# $pat[11] = qr/'/;
+# $pat[12] = qr/,/;
+# $pat[13] = qr/\(e?s\)/;
+
+
+# Other patterns
+
+# $keysplit = qr/^(.*?)(\|\|.*?)?\s(R[AZ])?\s?(\d+)$/;
+
+$keysplit = qr/^(.*?)(\@\|\@\|.*?)?\s(R[AZ])?\s?(\d+)$/;
+
+
+# The sort function
+
+sub cf {
+my($x,$y) = ($a,$b);
+
+############old#############
+#foreach $pattern (@pat) # Remove strings by pattern
+# {
+# $x =~ s/$pattern//g;
+# $y =~ s/$pattern//g;
+# }
+##########################
+
+
+# Turn || into @|@|
+
+$x =~ s/\|\|/@|@|/g;
+$y =~ s/\|\|/@|@|/g;
+
+# Remove all special characters, except those preceded by @
+
+$x =~ s/(?<!\@)[^\w\@\s]//g;
+$y =~ s/(?<!\@)[^\w\@\s]//g;
+
+# Remove the escaping @s
+
+#$x =~ s/\@(.)/$1/g;
+#$y =~ s/\@(.)/$1/g;
+
+
+
+################old ########################
+#$x =~ s/:(\w+):/$1/g; # :fail: etc => fail
+#$y =~ s/:(\w+):/$1/g;
+
+#$x =~ s/^\@[^a-z]+/\@/i; # Make keys starting with @
+#$y =~ s/^\@[^a-z]+/\@/i; # sort on @ followed by the first letter
+##############################################3
+
+
+$x =~ s/\@_/\x7f/g; # Make underscore sort late (option names)
+$y =~ s/\@_/\x7f/g;
+
+# Split up to sort on individual parts
+
+my($xp,$xs,$xr,$xn) = $x =~ /$keysplit/;
+my($yp,$ys,$yr,$yn) = $y =~ /$keysplit/;
+
+$xr = "" if !defined $xr;
+$yr = "" if !defined $yr;
+
+$xs = "" if !defined $xs;
+$ys = "" if !defined $ys;
+
+if ($show_keys)
+ {
+ print "a=$a\n x=$x\n xp=$xp\n xs=$xs\n xr=$xr\n xn=$xn\n";
+ print "b=$b\n y=$y\n yp=$yp\n ys=$ys\n yr=$yr\n yn=$yn\n";
+ }
+
+my ($c) = "\L$xp" cmp "\L$yp"; # Caseless, primary text only
+$c = $xp cmp $yp if $c == 0; # Caseful, primary text only
+$c = "\L$xs" cmp "\L$ys" if $c == 0; # Caseless, secondary text only
+$c = $xs cmp $ys if $c == 0; # Caseful, secondary text only
+$c = $xn <=> $yn if $c == 0; # Compare the numbers
+$c = $xr cmp $yr if $c == 0; # Sort RA before RZ
+return $c;
+}
+
+
+
+##############################################################################
+# Function for getting the next line from the @lines vector, using the global
+# index $1. If the next pair of lines specifies a range of pages, combine them.
+# That's why $linenumber has to be global - so we can increment it. If there's
+# a range error, return "".
+
+sub getnextentry {
+my($line) = $lines[$linenumber];
+my($aa,$zz,$tline,$nextline,$tnextline);
+
+if ($line =~ / RA (\d+)/)
+ {
+ $aa = $1;
+ $nextline = $lines[++$linenumber];
+ if ($nextline =~ / RZ (\d+)/)
+ {
+ $zz = $1;
+ }
+ else
+ {
+ print STDERR "** Bad range data (1)\n";
+ print STDERR " $line\n";
+ print STDERR " $nextline\n";
+ return "";
+ }
+
+ $tline = $line;
+ $tnextline = $nextline;
+
+ $tline =~ s/ RA \d+//;
+ $tnextline =~ s/ RZ \d+//;
+
+ if ($tline ne $tnextline)
+ {
+ print STDERR "** Bad range data (2)\n";
+ print STDERR " $line\n";
+ print STDERR " $nextline\n";
+ return "";
+ }
+
+ $line = ($aa eq $zz)? "$tline $aa" : "$tline $aa--$zz";
+ }
+
+elsif ($line =~ / RZ (\d+)/)
+ {
+ print STDERR "** Bad range data (RZ without RA)\n";
+ print STDERR " $line\n";
+ return "";
+ }
+
+return $line
+}
+
+
+
+
+##############################################################################
+# Function for outputting a line, checking for the current primary
+# and indenting a bit for secondaries. We also need a newpar
+# before each item, because the main indent is set to a largish indent
+# for long reference lists, but the parindent is set to counter this.
+# This is where we handle the break between letters. We know that any non-
+# alphamerics at the start of lines are markup, except for @. A reference
+# value of 99999 is for the "see also" lines. Suppress it.
+
+sub outline {
+my($text,$ref) = ($_[0],$_[1]);
+my ($letter) = $text =~ /^[^A-Za-z0-9\@]*(.)/;
+
+return if $text =~ /^\s*$/;
+
+if ($ref eq "99999") # dummy for see also
+ {
+ $ref = ""
+ }
+else
+ {
+ $ref = "#$ref"; # prepend space
+ }
+
+if ($letter =~ /\d/) { $letter = "0"; } else { $letter = "\U$letter"; }
+
+print OUT ".newpar\n";
+
+if ($letter ne $currentletter && $letter ge "A")
+ {
+ print OUT ".newletter\n";
+ $currentletter = $letter;
+ }
+
+$text =~ s/\@'/\$'/g; # Turns @' into $' so that it prints a non-curly quote
+
+if ($text =~ /^(.+)\|\|(.*)$/)
+ {
+ my($primary,$secondary) = ($1,$2);
+
+ if ($primary ne $lastprimary)
+ {
+ print OUT ".primary $primary\n";
+ $lastprimary = $primary;
+ }
+
+ $primary =~ s/"/""/g;
+ $secondary =~ s/"/""/g;
+
+ my($contprim) = $primary;
+ $contprim =~ s/ \(\\\*see also\*\\[^)]+\)//;
+
+ print OUT ".secondary \"$primary\" \"$secondary$ref\" \"$contprim\"\n";
+ }
+
+# Not a two-part item; insert @ if the first char is a dot
+
+else
+ {
+ print OUT "@" if $text =~ /^\./;
+ print OUT "$text$ref\n";
+ $lastprimary = $text;
+ }
+}
+
+
+
+
+
+##############################################################################
+# The main script
+
+$save_sorted = 0;
+$test_index = 0;
+$show_keys = 0;
+
+while (@ARGV > 0)
+ {
+ my($arg) = shift @ARGV;
+ if ($arg eq "-k") { $show_keys = 1; }
+ elsif ($arg eq "-s") { $save_sorted = 1; }
+ elsif ($arg eq "-t") { $test_index = $save_sorted = 1; }
+ else { die "Unknown option $arg\n"; }
+ }
+
+if ($test_index)
+ {
+ open(IN, "z-testindex") || die "Can't open z-testindex\n";
+ }
+else
+ {
+ open(IN, "z-rawindex") || die "Can't open z-rawindex\n";
+ }
+
+open(OUT, ">z-index") || die "Can't open z-index\n";
+
+# Extract index lines ($e lines are contents). Until we hit the first
+# $e line, we are dealing with "see also" index lines, for which we want
+# to turn the line number into 99999.
+
+$#lines = -1;
+$prestuff = 1;
+
+while (<IN>)
+ {
+ s/\n$//;
+ if (/\$e/)
+ {
+ $prestuff = 0;
+ }
+ else
+ {
+ s/(\D)$/$1 99999/ if $prestuff; # No number in "see also"
+ push(@lines, $_);
+ }
+ $index_pagenumber = $1 if /^Index\$e(\d+)/;
+ }
+close(IN);
+
+# Sort, ignoring markup
+
+print STDERR "Sorting ...\n";
+@lines = sort cf @lines;
+
+# Keep a copy of the sorted data, for reference
+
+if ($save_sorted)
+ {
+ open(X, ">z-indexsorted") || die "Can't open z-indexsorted\n";
+ foreach $line (@lines)
+ {
+ print X "$line\n";
+ }
+ close(X);
+ }
+
+# Heading for the index file
+
+print OUT <<"EOF";
+.library "a4ps"
+.linelength ~~sys.linelength + 16.0
+
+.include "markup.sg"
+
+.indent 3em
+.parspace 0
+.parindent -3em
+.justify left
+.
+.foot
+\$c [~~sys.pagenumber]
+.endfoot
+.
+.cancelflag #
+.flag # "\$S*1"
+.set INDEX true
+.
+.macro primary "text"
+.if ~~sys.leftonpage < 2ld
+.newcolumn
+.fi
+~~1
+.newpar
+.endm
+.
+.macro secondary "prim" "sec" "contprim"
+.if ~~sys.leftonpage < 1ld
+.newcolumn
+.newpar
+~~3 \$it\{(continued)\}
+.newpar
+.fi
+##~~2
+.endm
+.
+.macro newletter
+.if ~~sys.leftonpage < 4ld
+.newcolumn
+.else
+.space 1ld
+.fi
+.newpar
+.endm
+.
+.set chapter -1
+.page $index_pagenumber
+.chapter Index
+.columns 2
+.newpar
+.
+EOF
+
+# Process the lines and output the result.
+# Note that $linenumber is global, and is changed by getnextentry() for
+# pairs of lines that represent ranges.
+
+$lastprimary = "";
+$lastref = "";
+$currenttext = $currentref = "";
+$currentletter = "";
+$badrange = 0;
+
+print STDERR "Processing ...\n";
+
+for ($linenumber = 0; $linenumber < @lines; $linenumber++)
+ {
+ $line = &getnextentry();
+
+ if ($line eq "") # Bad range data - but carry on to get all of it
+ {
+ $badrange = 1;
+ next;
+ }
+
+ # Split off the text and reference
+
+ ($text,$ref) = $line =~ /^(.*)\s+([\d-]+)$/;
+
+ # If same as current text, just add the new reference, unless its a duplicate
+
+ if ($text eq $currenttext)
+ {
+ if ($ref ne $lastref)
+ {
+ $currentref .= ", $ref";
+ $lastref = $ref;
+ }
+ next;
+ }
+
+ # Not the same as the current text. Output the current text, then
+ # set up a new current.
+
+ &outline($currenttext, $currentref);
+
+ $currenttext = $text;
+ $currentref = $lastref = $ref;
+ }
+
+# Output the final line and close the file
+
+&outline($currenttext, $currentref);
+close(OUT);
+
+die "** Aborted\n" if $badrange;
+
+# Format the index
+
+system("sgcal z-index -to zi-gcode -index /dev/null");
+system("sgtops zi-gcode -to zi-ps");
+print "PostScript in zi-ps\n";
+
+# End
diff --git a/doc/doc-scripts/JoinPS b/doc/doc-scripts/JoinPS
new file mode 100755
index 000000000..92ba59a5f
--- /dev/null
+++ b/doc/doc-scripts/JoinPS
@@ -0,0 +1,130 @@
+#!/usr/bin/perl
+# $Cambridge: exim/doc/doc-scripts/JoinPS,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# Make the basic PostScript file for the Exim spec from the gcode file, then
+# join it together with the contents and the index, to make a single
+# PostScript file, suitable for double-sided printing.
+
+sub blank_page {
+my($title) = shift @_;
+printf(OUT "%%%%Page: %s %d\nxpage\n\n", $title, $pagenumber++);
+}
+
+
+@roman = ("i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x",
+ "xi", "xii", "xiii", "xiv", "xv", "xvi", "xvii", "xviii", "xix");
+
+$pagenumber = 1;
+
+system("sgtops z-gcode -to z-ps") && die "sgtops run failed\n";
+
+open(SPEC, "z-ps") || die "Can't open z-ps\n";
+open(CONTS, "zc-ps") || die "Can't open zc-ps\n";
+open(INDEX, "zi-ps") || die "Can't open zi-ps\n";
+open(OUT, ">spec.ps") || die "Can't open spec.ps\n";
+
+# Copy spec headings etc.
+
+while (<SPEC>)
+ {
+ last if (/^%%Page:/) ;
+ print OUT;
+ }
+
+# Copy the first two pages - the title page, and its blank verso
+
+for ($i = 1; $i < 3; $i++)
+ {
+ printf(OUT "%%%%Page: title%s %d\n", ($i == 1)? "" : "-verso", $pagenumber++);
+ while (<SPEC>)
+ {
+ last if (/^%%Page:/) ;
+ print OUT;
+ }
+ }
+
+# Skip the contents heading
+
+while (<CONTS>)
+ {
+ last if (/^%%Page:/) ;
+ }
+
+# Output the contents pages - fudge the roman numerals as we know there
+# won't be a vast number of them.
+
+$subpagenumber = 0;
+while (!eof CONTS)
+ {
+ printf(OUT "%%%%Page: %s %d\n", $roman[$subpagenumber++], $pagenumber++);
+ while (<CONTS>)
+ {
+ next if (/^%%Pages:/);
+ next if (/^%%Trailer/);
+ last if (/^%%Page:/) ;
+ print OUT;
+ }
+ }
+printf(OUT "\n");
+
+# If contents was an odd number of pages, insert a blank page
+
+&blank_page("contents-pad") if ($pagenumber & 1) == 0;
+
+# Copy the rest of the main file
+
+$subpagenumber = 1;
+
+while (!eof SPEC)
+ {
+ printf(OUT "%%%%Page: %d %d\n", $subpagenumber++, $pagenumber++);
+ while (<SPEC>)
+ {
+ next if (/^%%Pages:/);
+ next if (/^%%Trailer/);
+ last if (/^%%Page:/) ;
+ print OUT;
+ }
+ }
+printf(OUT "\n");
+
+# If contents was an odd number of pages, insert a blank page
+
+&blank_page("body-pad") if ($pagenumber & 1) == 0;
+
+# Skip the index heading
+
+while (<INDEX>)
+ {
+ last if (/^%%Page:/) ;
+ }
+
+# Copy the index pages
+
+$subpagenumber = 1;
+
+while (!eof INDEX)
+ {
+ printf(OUT "%%%%Page: I%d %d\n", $subpagenumber++, $pagenumber++);
+ while (<INDEX>)
+ {
+ next if (/^%%Pages:/);
+ next if (/^%%Trailer/);
+ last if (/^%%Page:/) ;
+ print OUT;
+ }
+ }
+
+# Add final comments
+
+printf(OUT "%%%%Trailer\n");
+printf(OUT "%%%%Pages: %d\n", $pagenumber-1);
+
+# That's it
+
+close(SPEC);
+close(CONTS);
+close(INDEX);
+close(OUT);
+
+# End
diff --git a/doc/doc-scripts/Makefile b/doc/doc-scripts/Makefile
new file mode 100644
index 000000000..79b1f0490
--- /dev/null
+++ b/doc/doc-scripts/Makefile
@@ -0,0 +1,31 @@
+# $Cambridge: exim/doc/doc-scripts/Makefile,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# Makefile for Exim documentation
+
+ps:; sgcal-fr spec.src -v -to z-gcode -index z-rawindex
+ sgtops z-gcode -to z-ps
+
+txt:; g2man
+ sgcal-fr spec.src -style online -v -to z-txt -index z-rawindex
+
+contents:; @DoConts
+
+index:; @DoIndex
+
+# The file z-rawindex is included by the filter source to create a TOC.
+# First empty it, then do a dummy format to create it, then do a second
+# pass. This works because the TOC occupies no more than the rest of the
+# first page.
+
+filterps:; /bin/rm -rf z-rawindex
+ touch z-rawindex
+ sgcal-fr filter.src -v -to z-gcode -index z-rawindex
+ sgcal-fr filter.src -v -to z-gcode -index /dev/null
+ sgtops z-gcode -to filter.ps
+
+filtertxt:; /bin/rm -rf z-rawindex
+ touch z-rawindex
+ sgcal-fr filter.src -style online -v -to filter.txt -index z-rawindex
+ sgcal-fr filter.src -style online -v -to filter.txt -index /dev/null
+
+clean:; /bin/rm -f z*
diff --git a/doc/doc-scripts/f2h b/doc/doc-scripts/f2h
new file mode 100755
index 000000000..426e46e67
--- /dev/null
+++ b/doc/doc-scripts/f2h
@@ -0,0 +1,338 @@
+#!/usr/bin/perl
+# $Cambridge: exim/doc/doc-scripts/f2h,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# Script to turn the Exim FAQ into HTML.
+
+use integer;
+
+# Function to do text conversions that apply to both displays and non displays
+
+sub process_both {
+my($s) = $_[0];
+$s =~ s/</&#60;/g; # Deal with < and >
+$s =~ s/>/&#62;/g;
+return $s;
+}
+
+
+# Function to do text conversions to display paragraphs
+
+sub process_display {
+my($s) = $_[0];
+$s =~ s/^==>/ /;
+my($indent) = $s =~ /^(\s+)/;
+my($remove) = " " x (length($indent) - 3);
+$s =~ s/^$remove//mg;
+$s = &process_both($s);
+return $s;
+}
+
+
+# Function to do text conversions to paragraphs not in displays.
+
+sub process_non_display {
+my($s) = &process_both($_[0]);
+
+$s =~ s/@\\/@@backslash@@/g; # @\ temporarily hidden
+
+$s =~ s/\\#/&nbsp;/g; # \# is a hard space
+
+$s =~ s/\\\*\*([^*]*)\*\*\\/<b>$1<\/b>/g; # \**...**\ => bold
+$s =~ s/\\\*([^*]*)\*\\/<i>$1<\/i>/g; # \*.....*\ => italic
+$s =~ s/\\"([^"]*)"\\/<tt>$1<\/tt>/g; # \"....."\ => fixed pitch
+$s =~ s/\\\$([^\$]*)\$\\/<i>\$$1<\/i>/g; # \$.....$\ => $italic
+$s =~ s/\\\\([^\\]*)\\\\/<small>$1<\/small>/g; # \\.....\\ => small
+$s =~ s/\\\(([^)]*)\)\\/<i>$1<\/i>/g; # \(.....)\ => italic
+$s =~ s/\\-([^\\]*)-\\/<b>-$1<\/b>/g; # \-.....-\ => -bold
+$s =~ s/\\\[([^]]*)\]\\/&\#60;<i>$1<\/i>&\#62;/gx; # \[.....]\ => <italic>
+$s =~ s/\\\?(.*?)\?\\/<a href="$1">$1<\/a>/g; # \?.....?\ => URL
+$s =~ s/\\\^\^([^^]*)\^\^\\/<i>$1<\/i>/g; # \^^...^^\ => italic
+$s =~ s/\\\^([^^]*)\^\\/<i>$1<\/i>/g; # \^.....^\ => italic
+$s =~ s/\\%([^%]*)%\\/<b>$1<\/b>/g; # \%.....%\ => bold
+$s =~ s/\\\/([^\/]*)\/\\/<i>$1<\/i>/g; # \/...../\ => italic
+$s =~ s/\\([^\\]+)\\/<tt>$1<\/tt>/g; # \.......\ => fixed pitch
+
+$s =~ s"//([^/\"]*)//"<i>$1</i>"g; # //.....// => italic
+$s =~ s/::([^:]*)::/<i>$1:<\/i>/g; # ::.....:: => italic:
+
+$s =~ s/``(.*?)''/&#147;$1&#148;/g; # ``.....'' => quoted text
+
+$s =~ s/\s*\[\[br\]\]\s*/<br>/g; # [[br]] => <br>
+
+$s =~ s/@@backslash@@/\\/g; # Put back single backslash
+
+$s =~ s/^(\s*\(\d\)\s)/$1&nbsp;/; # Extra space after (1), etc.
+
+# Cross references within paragraphs
+
+$s =~ s/Q(\d{4})(?!:)/<a href="$xref{$1}">$&<\/a>/xg;
+
+# References to configuration samples
+
+$s =~ s/\b([CFLS]\d\d\d)\b/<a href="$1.txt">$1<\/a>/g;
+
+# Remove white space preceding a newline in the middle of paragraphs,
+# to keep the file smaller (and for human reading when debugging).
+
+$s =~ s/^\s+//mg;
+
+return $s;
+}
+
+
+# Main program
+
+# We want to read the file paragraph by paragraph; Perl only does this if the
+# separating lines are truly blank. Having been caught by lines containing
+# whitespace before, do a detrailing pass first.
+
+open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (preliminary)\n";
+open(OUT, ">$ARGV[0]-$$") || die "can't open $ARGV[0]-$$\n";
+while (<IN>)
+ {
+ s/[ \t]+$//;
+ print OUT;
+ }
+close(IN);
+close(OUT);
+rename("$ARGV[0]-$$", "$ARGV[0]") ||
+ die "can't rename $ARGV[0]-$$ as $ARGV[0]\n";
+
+# The second argument is the name of a directory into which to put multiple
+# HTML files. We start off with FAQ.html.
+
+$hdir = $ARGV[1];
+open(OUT, ">$hdir/FAQ.html") || die "can't open $hdir/FAQ.html\n";
+
+# Initial output
+
+print OUT <<End ;
+<html>
+<head>
+<title>The Exim FAQ</title>
+</head>
+<body bgcolor="#F8F8F8" text="#00005A" link="#0066FF" alink="#0066FF" vlink="#000099">
+<h1>The Exim FAQ</h1>
+End
+
+$/ = "";
+
+# First pass to read the titles and questions and create the table of
+# contents. We save it up in a vector so that it can be written after the
+# introductory paragraphs.
+
+open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (first time)\n";
+
+$toc = 0;
+$sec = -1;
+$inul = 0;
+
+while ($_ = <IN>)
+ {
+ $count = s/\n/\n/g - 1; # Number of lines in paragraph
+
+ if ($count == 1 && /^\d+\./) # Look for headings
+ {
+ chomp;
+ push @toc, "</ul>" if $inul;
+ $inul = 0;
+ push @toc, "<br>\n\n" if $sec++ >= 0;
+ push @toc, "<a name=\"TOC$toc\" href=\"FAQ_$sec.html\">$_</a>\n";
+ $toc++;
+
+ ($number,$title) = /^(\d+)\.\s+(.*)$/;
+ if ($title ne "UUCP" && $title ne "IRIX" && $title ne "BSDI" &&
+ $title ne "HP-UX")
+ {
+ ($initial,$rest) = $title =~ /^(.)(.*)$/;
+ $title = "$initial\L$rest";
+ $title =~ s/isdn/ISDN/;
+ $title =~ s/\btls\b/TLS/;
+ $title =~ s/\bssl\b/SSL/;
+ $title =~ s/ os x/ OS X/;
+ }
+ push @seclist, "<a href=\"FAQ_$sec.html\">$number. $title</a>";
+
+ next;
+ }
+
+ if (/^(Q\d{4})/) # Q initial paragraph
+ {
+ if (!$inul)
+ {
+ push @toc, "<ul>\n";
+ $inul = 1;
+ }
+ $num = $1;
+ $rest = $';
+ $xref{substr($num,1)} = "FAQ_$sec.html#TOC$toc";
+ $rest =~ s/^: /:&nbsp;&nbsp;/;
+ $rest = &process_non_display($rest);
+ push @toc, "<li><a name=\"TOC$toc\" href=\"FAQ_$sec.html#TOC$toc\">$num</a>$rest<br><br></li>\n";
+ $toc++;
+ next;
+ }
+ }
+
+push @toc, "</ul>\n" if $inul;
+close(IN);
+
+
+# This is the main processing pass. We have to detect the different kinds of
+# "paragraph" and do appropriate things.
+
+open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (second time)\n";
+
+# Skip the title line
+
+$_ = <IN>;
+
+# Handle the rest of the file
+
+$toc = 0;
+$maxsec = $sec;
+$sec = -1;
+
+while ($_ = <IN>)
+ {
+ $count = s/\n/\n/g - 1; # Number of lines in paragraph
+ chomp; # Trailing newlines
+
+ if (/^The FAQ is divided into/)
+ {
+ my($count) = scalar(@seclist);
+ my($cols) = ($count + 1)/2;
+
+ print OUT "<hr><a name=\"TOC\"><h1>Index</h1></a>\n";
+ print OUT "<p>A <i>Keyword-in-context</i> <a href=\"FAQ-KWIC_A.html\">index</a> " .
+ "to the questions is available. This is usually the " .
+ "quickest way to find information in the FAQ.</p>\n";
+
+ print OUT "<h1>Contents</h1>\n";
+ print OUT "<p>The FAQ is divided into the following sections:<br><br></p>\n";
+
+ print OUT "<table>\n";
+
+ for ($i = 0; $i < $cols; $i++)
+ {
+ print OUT "<tr>\n";
+ print OUT " <td>", "&nbsp;" x 4, "</td>\n";
+ print OUT " <td>&nbsp;$seclist[$i]</td>\n";
+ print OUT " <td>", "&nbsp;" x8, "$seclist[$cols+$i]</td>\n"
+ if $cols+$i < $count;
+ print OUT "</tr>\n";
+ }
+ print OUT "</table><br><p>\n<hr><br>\n";
+ print OUT "<h1>List of questions</h1>\n";
+
+ $_ = <IN>; # Skip section list
+ next;
+ }
+
+ if ($count == 1 && /^\d+\./) # Look for headings
+ {
+ if (@toc != 0) # TOC when hit first heading
+ {
+ while (@toc != 0) { print OUT shift @toc; }
+ }
+
+ # Output links at the bottom of this page
+
+ print OUT "<hr><br>\n";
+ print OUT "<a href=\"FAQ.html#TOC\">Contents</a>&nbsp;&nbsp;\n";
+ if ($sec > 0)
+ {
+ printf OUT ("<a href=\"FAQ_%d.html\">Previous</a>&nbsp;&nbsp;\n", $sec-1);
+ }
+ printf OUT ("<a href=\"FAQ_%d.html\">Next</a>\n", $sec+1);
+
+ # New section goes in new file
+
+ print OUT "</body>\n</html>\n";
+ close OUT;
+
+ $sec++;
+ open(OUT, ">$hdir/FAQ_$sec.html") ||
+ die "Can't open $hdir/FAQ_$sec.html\n";
+
+ print OUT "<html>\n<head>\n" .
+ "<title>The Exim FAQ Section $sec</title>\n" .
+ "</head>\n" .
+ "<body bgcolor=\"#F8F8F8\" text=\"#00005A\" " .
+ "link=\"#FF6600\" alink=\"#FF9933\" vlink=\"#990000\">\n";
+
+ printf OUT "<h1>The Exim FAQ</h1>\n";
+
+ print OUT "<a href=\"FAQ.html#TOC\">Contents</a>&nbsp;&nbsp;\n";
+ if ($sec > 0)
+ {
+ printf OUT ("<a href=\"FAQ_%d.html\">Previous</a>&nbsp;&nbsp;\n", $sec-1);
+ }
+ if ($sec < $maxsec)
+ {
+ printf OUT ("<a href=\"FAQ_%d.html\">Next</a>\n", $sec+1);
+ }
+
+ print OUT "<hr><br>\n";
+
+ print OUT "<h2><a href=\"FAQ.html#TOC$toc\">$_</a></h2>\n";
+ $toc++;
+ next;
+ }
+
+ s/^([QA]\d{4}|[CFLS]\d{3}): /$1:&nbsp;&nbsp;/;
+
+ if (/^(Q\d{4}:)/) # Q initial paragraph
+ {
+ print OUT "<p>\n<a name=\"TOC$toc\" href=\"FAQ.html#TOC$toc\">$1</a>";
+ $_ = &process_non_display($');
+ print OUT "$_\n</p>\n";
+ $toc++;
+ next;
+ }
+
+ if (/^A\d{4}:/) # A initial paragraph
+ {
+ $_ = &process_non_display($_);
+ s/^(A\d{4}:)/<font color="#00BB00">$1<\/font>/;
+ print OUT "<p>\n$_\n</p>\n";
+ next;
+ }
+
+ # If a paragraph begins ==> it is a display which must remain verbatin
+ # and not be reformatted. The flag gets turned into spaces.
+
+ if ($_ =~ /^==>/)
+ {
+ $_ = &process_display($_);
+ chomp;
+ print OUT "<pre>\n$_</pre>\n";
+ }
+
+ # Non-display paragraph; massage the final line & my sig.
+
+ elsif (/^\*\*\* End of Exim FAQ \*\*\*/)
+ {
+ }
+
+ else
+ {
+ $_ = &process_non_display($_);
+ if (/^Philip Hazel/)
+ {
+ s/\n/<br>\n/g;
+ s/<br>$/<hr><br>/;
+ }
+ print OUT "<p>\n$_\n</p>\n";
+ }
+ }
+
+close(IN);
+
+print OUT "<hr><br>\n";
+print OUT "<a href=\"FAQ.html#TOC\">Contents</a>&nbsp;&nbsp;\n";
+printf OUT ("<a href=\"FAQ_%d.html\">Previous</a>\n", $sec-1);
+
+print OUT "</body>\n</html>\n";
+close(OUT);
+End
diff --git a/doc/doc-scripts/f2txt b/doc/doc-scripts/f2txt
new file mode 100755
index 000000000..ff5d70392
--- /dev/null
+++ b/doc/doc-scripts/f2txt
@@ -0,0 +1,107 @@
+#!/usr/bin/perl
+# $Cambridge: exim/doc/doc-scripts/f2txt,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# Script to turn the Exim FAQ into plain ASCII.
+
+use integer;
+
+
+# Function to do text conversions to display paragraphs
+
+sub process_display {
+my($s) = $_[0];
+$s =~ s/^==>/ /;
+return $s;
+}
+
+
+# Function to do text conversions to paragraphs not in displays.
+
+sub process_non_display {
+my($s) = $_[0];
+
+$s =~ s/@\\/@@backslash@@/g; # @\ temporarily hidden
+
+$s =~ s/\\#/ /g; # \# is a hard space
+
+$s =~ s/\\\*\*([^*]*)\*\*\\/$1/g; # \**...**\ => text
+$s =~ s/\\\*([^*]*)\*\\/"$1"/g; # \*.....*\ => "text"
+$s =~ s/\\"([^"]*)"\\/"$1"/g; # \"....."\ => "text"
+$s =~ s/\\\$([^\$]*)\$\\/\$$1/g; # \$.....$\ => $text
+$s =~ s/\\\\([^\\]*)\\\\/$1/g; # \\.....\\ => text
+$s =~ s/\\\(([^)]*)\)\\/$1/g; # \(.....)\ => text
+$s =~ s/\\-([^-]*)-\\/-$1/g; # \-.....-\ => -text
+$s =~ s/\\\[([^]]*)\]\\/<$1>/gx; # \[.....]\ => <text>
+$s =~ s/\\\?(.*?)\?\\/$1/g; # \?.....?\ => text
+$s =~ s/\\\^\^([^^]*)\^\^\\/$1/g; # \^^...^^\ => text
+$s =~ s/\\\^([^^]*)\^\\/$1/g; # \^.....^\ => text
+$s =~ s/\\%([^%]*)%\\/"$1"/g; # \%.....%\ => "text"
+$s =~ s/\\\/([^\/]*)\/\\/$1/g; # \/...../\ => text
+$s =~ s/\\([^\\]+)\\/"$1"/g; # \.......\ => "text"
+
+$s =~ s"//([^/\"]*)//"$1"g; # //.....// => text
+$s =~ s/::([^:]*)::/$1:/g; # ::.....:: => text:
+
+$s =~ s/``(.*?)''/"$1"/g; # ``.....'' => "text"
+
+$s =~ s/\s*\[\[br\]\]\s*\n/\n/g; # Remove [[br]]
+
+$s =~ s/@@backslash@@/\\/g; # Put back single backslash
+
+return $s;
+}
+
+
+# Main program
+
+# We want to read the file paragraph by paragraph; Perl only does this if the
+# separating lines are truly blank. Having been caught by lines containing
+# whitespace before, do a detrailing pass first.
+
+open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (preliminary)\n";
+open(OUT, ">$ARGV[0]-$$") || die "can't open $ARGV[0]-$$\n";
+while (<IN>)
+ {
+ s/[ \t]+$//;
+ print OUT;
+ }
+close(IN);
+close(OUT);
+rename("$ARGV[0]-$$", "$ARGV[0]") ||
+ die "can't rename $ARGV[0]-$$ as $ARGV[0]\n";
+
+# The second argument is the name of the output file.
+
+open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (for real)\n";
+open(OUT, ">$ARGV[1]") || die "can't open $ARGV[1]\n";
+
+$/ = "";
+
+while ($_ = <IN>)
+ {
+ # Comment lines start with ##
+
+ next if /^\#\#/;
+
+ # If a paragraph begins ==> it is a display which must remain verbatin
+ # and not be reformatted. The flag gets turned into spaces.
+
+ if ($_ =~ /^==>/)
+ {
+ $_ = &process_display($_);
+ }
+
+ # Non-display paragraph
+
+ else
+ {
+ $_ = &process_non_display($_);
+ }
+
+ print OUT;
+ }
+
+close(IN);
+close(OUT);
+
+End
diff --git a/doc/doc-scripts/faqchk b/doc/doc-scripts/faqchk
new file mode 100755
index 000000000..939d0b253
--- /dev/null
+++ b/doc/doc-scripts/faqchk
@@ -0,0 +1,102 @@
+#!/usr/bin/perl -w
+# $Cambridge: exim/doc/doc-scripts/faqchk,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# Script to check the FAQ source and make sure I have all the numbers
+# in the right order without duplication. Also check the numbers of blank
+# lines. It gives up on any error (other than bad blank line counts).
+
+sub oops {
+print "\nLine $line: $_[0]\n";
+print;
+exit 1;
+}
+
+sub poops {
+print "\nLine $line: $_[0]\n";
+print;
+$precede_fail = 1;
+}
+
+
+$line = 0;
+$section = -1;
+$expect_answer = 0;
+$precede_fail = 0;
+
+while (<>)
+ {
+ $line++;
+ if (/^\s*$/)
+ {
+ $blankcount++;
+ next;
+ }
+ $preceded = $blankcount;
+ $blankcount = 0;
+
+ if (/^(\d+)\./)
+ {
+ &poops("$preceded empty lines precede (3 expected)") if $preceded != 3;
+ &oops(sprint("Answer %02d%02d is missing\n", $section, $question))
+ if $expect_answer;
+ $section = $1;
+ $question = ($section == 20)? 0 : 1;
+ $expected = 1;
+ if ($section == 99)
+ {
+ $c = 1;
+ $f = 1;
+ }
+ next;
+ }
+
+ if ($section != 99)
+ {
+ if (/^Q(\d\d)(\d\d):/)
+ {
+ &poops("$preceded empty lines precede ($expected expected)")
+ if $preceded != $expected;
+ $expected = 2;
+
+ &oops("Answer expected") if $expect_answer;
+ &oops("Q$1$2 is in the wrong section") if ($1 != $section);
+ &oops("Q$1$2 is out of order") if $2 != $question;
+
+ $expect_answer = 1;
+ next;
+ }
+
+ if (/^A(\d\d)(\d\d):/)
+ {
+ &poops("$preceded empty lines precede (1 expected)") if $preceded != 1;
+
+ &oops("Question expected") if !$expect_answer;
+ &oops("A$1$2 is in the wrong section") if $1 != $section;
+ &oops("A$1$2 is out of order") if $2 != $question++;
+
+ $expect_answer = 0;
+ next;
+ }
+ }
+
+ else
+ {
+ if (/^C(\d\d\d):/)
+ {
+ &oops("C$1 is mixed up in the Fs") if $f != 1;
+ # Some Cxxx configs are omitted (not translated from Exim 3) so can
+ # only check increasing number.
+ &oops("C$1 is out of order") if $1 < $c;
+ $c = $1;
+ }
+ elsif (/^F(\d\d\d):/)
+ {
+ &oops("F$1 is out of order") if $1 != $f++;
+ next;
+ }
+ }
+ }
+
+exit 1 if $precede_fail;
+
+# End
diff --git a/doc/doc-scripts/fc2k b/doc/doc-scripts/fc2k
new file mode 100755
index 000000000..936392979
--- /dev/null
+++ b/doc/doc-scripts/fc2k
@@ -0,0 +1,344 @@
+#! /usr/bin/perl -w
+# $Cambridge: exim/doc/doc-scripts/fc2k,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# Script to read the HTML table of contents for the Exim FAQ and create an
+# HTML KWIC index out of it.
+
+
+########################################################################
+# List of words to ignore - kept alphabetically for reference, but they
+# don't have to be in order.
+
+$ignore_list = "
+
+a ability able about address addresses addressed affect affected
+after against all allow allowed allows already also although always am an and
+and/or any anybody anyone anything anywhere are aren't arrange arrive as at
+
+back bad based basically be because been behave behaviour being best between
+bob both bug build builds built busy but by
+
+call called calls can can't cannot causes causing central certain code comes
+coming command commands complain complaining complains configure configured
+conjunction contact contain contains contained correct correctly could
+currently customer
+
+day days defined deliver delivers delivered delivery deliveries did do does
+doesn't doing don't down during
+
+e-mail e-mails each easy else email emails entirely entries entry especially
+etc even ever every example exim exim's experiencing
+
+far few file files find fine fly following for form found from fully
+
+get gets getting given gives giving go goes going got
+
+handle handles handled handling happen happens has have haven't having helpful
+him host hosts how however
+
+i i'd i'm i've if in indeed instead into is issue issues isn't it it's its
+
+jim just
+
+keep keeps know knows
+
+like line lines look looked looking lot
+
+machine machines machine's mail mails main make me mean means message messages
+might more must my myself
+
+near need neither no nor not now
+
+occur of off often ok on one only or other our out over own
+
+part parts particular per place possibility possible present problem problems
+put puts
+
+quite
+
+raised rather really reason rid right round run runs
+
+same say saying see seeing seem seems seen sees set setting she should so some
+somehow something sometimes stand state statement still strange such supposed
+system systems
+
+take takes than that the their them then there these they things think this
+those to try though to/for told too tried tries trying
+
+under until up use uses used using usually
+
+valid value values via
+
+want wanted wanting was way we we've well what what's when where whereabouts
+whenever whether which while who whose why will with within without wish won't
+wondered work worked working works would wrong
+
+xxx
+
+yet yyy
+
+";
+########################################################################
+
+
+# The regular expression fragment that defines the separator between words
+
+$wordgap = "(?:[]().?,;:\"']|(?><[^>]*>))*(?:\\s+|\$)(?:[[(\"'`]|(?><[^>]*>))*";
+
+
+########################################################################
+# Function to add to a length to accommodate HTML stuff
+
+sub setlen{
+my($len, $s) = @_;
+
+$len += length($1) while ($s =~ /(<\/?[a-z]+>)/ig);
+$len += 1 while ($s =~ /&#\d+;/g);
+
+return $len;
+}
+
+
+########################################################################
+# Function to write out the list of initials with references
+
+sub write_initials {
+my($this_initial) = "$_[0]";
+
+print OUT "<p>\n&nbsp;&nbsp;";
+
+foreach $initial (sort keys %initials)
+ {
+ if ($initial eq $this_initial)
+ {
+ print OUT "&nbsp;&nbsp;&nbsp;<font size=7 color=\"#FF0A0A\"><b>$initial</b></font>&nbsp;";
+ }
+ else
+ {
+ print OUT "<a href=\"FAQ-KWIC_$initial.html\">&nbsp;&nbsp;$initial</a>";
+ }
+ }
+
+print OUT "&nbsp;"x4 . "<a href=\"FAQ.html#TOC\">FAQ Contents</a>\n</p>\n";
+}
+
+
+
+########################################################################
+# The main program. We can pick out the contents lines because they lie
+# between <li> and </li> in the file, sometimes on more than one physical
+# line.
+
+# Turn the list of ignorable words into a hash for quick lookup. Add the
+# empty word to the list.
+
+@words = split /\s+/, $ignore_list;
+foreach $word (@words) { $ignore{$word} = 1; }
+$ignore{""} = 1;
+
+
+# Open the file and do the job
+
+open(IN, "html/FAQ.html") || die "Can't open html/FAQ.html\n";
+
+while (<IN>)
+ {
+ next unless /^<li>/;
+ $_ .= <IN> while !/<\/li>$/;
+ chomp;
+ s/\n\s*/ /g;
+
+ # Extract the operative text into $text, with the beginning in $pre.
+
+ my($pre,$text,$post) = /^<li>(.*<\/a>:(?:&nbsp;)*)(.*)<br><br><\/li>$/;
+
+ # Now split into words. As well as punctuation, there may be HTML thingies
+ # between words. Absorb them into the separators.
+
+ my(@words) = split /$wordgap/, $text;
+
+ # Lower case all the words, and remove those that we don't want.
+ # Then keep a list of all the used initials.
+
+ REMOVE_IGNORE:
+ for ($i = 0; $i < scalar @words; $i++)
+ {
+ my($word) = $words[$i] = "\L$words[$i]\E";
+
+ # Remove certain forms of word and those on the ignore list
+
+ if (defined $ignore{$word} || # word on ignore list
+ $word =~ /^-+$/ || # word consists entirely of hyphens
+ $word =~ /^-[^a-z]/ || # follows leading hyphen with non-letter
+ $word =~ /^[^a-z-]/ || # starts with a non-letter or hyphen
+ $word =~ /[@^.]/ # contains @ or ^ or .
+ )
+ {
+ splice(@words, $i, 1);
+ redo REMOVE_IGNORE if $i < scalar @words;
+ }
+
+ # Otherwise, build up a list of initials
+
+ else
+ {
+ my($inword) = $word;
+ $inword =~ s/^-//;
+ $initial = substr($inword, 0, 1);
+ $initials{"\U$initial\E"} = 1;
+ }
+ }
+
+ # Create the lines for the KWIC index, and store them in associative
+ # arrays, with the keyword as the key. That will get them sorted
+ # automatically.
+
+ while (scalar @words > 0)
+ {
+ my($word) = shift @words;
+ my($pretext, $casedword, $posttext) =
+ $text =~ /(.*?)(?<![a-z])(\Q$word\E)(?![a-z])(.*)/i;
+
+ # Remove a leading hyphen from $word so that it sorts according to
+ # the leading letter. What is actually output is $casedword, which
+ # retains the hyphen.
+
+ $word =~ s/^-//;
+
+ my($prelen) = length $pretext;
+ my($postlen) = length $posttext;
+
+ # We want to chop excessively long entries on either side. We can't set
+ # a fixed length because of the HTML control data. Call a function to
+ # add the given length to allow for HTML stuff. This is crude, but it
+ # does roughtly the right thing.
+
+ my($leftlen) = &setlen(70, $pretext);
+ my($rightlen) = &setlen(70, $posttext);
+
+ if ($prelen > $leftlen)
+ {
+ my($cutoff) = $leftlen;
+ $cutoff++
+ while ($cutoff < $prelen && substr($pretext, -$cutoff, 1) ne " ");
+ $pretext = "... " . substr($pretext, -$cutoff);
+ }
+
+ if ($postlen > $rightlen)
+ {
+ my($cutoff) = $rightlen;
+ $cutoff++
+ while ($cutoff < $postlen && substr($posttext, $cutoff, 1) ne " ");
+ $posttext = substr($posttext, 0, $cutoff) . "...";
+ }
+
+ # If the pre text has a font-ending not preceded by a font beginning
+ # (i.e. we've chopped the beginning off), we must insert a beginning.
+
+ while ($pretext =~ /^(.*?)<\/(small|tt|b|i)>/ && $1 !~ /<$2>/)
+ {
+ $pretext = "<$2>" . $pretext;
+ }
+
+ # If the pre text ends in a special font, we have to terminate that,
+ # and reset it at the start of the post text.
+
+ my($poststart) = "";
+
+ while ($pretext =~ /<(small|tt|b|i)>(?!.*?<\/\1>)/)
+ {
+ $pretext .= "</$1>";
+ $poststart .= "<$1>";
+ }
+
+ # If the post text changes font but doesn't close it, we must add
+ # the closure.
+
+ while ($posttext =~ /<(small|tt|b|i)>(?!.*?<\/\1>)/)
+ {
+ $posttext .= "</$1>";
+ }
+
+ # Remove any unnecessary changes in either of them
+
+ $pretext =~ s/<(small|tt|b|i)>\s*<\/\1>//g;
+ $posttext =~ s/<(small|tt|b|i)>\s*<\/\1>//g;
+
+ # Save the texts in associative arrays. Add the question number to
+ # the end of the word to make the key.
+
+ $pre =~ /(Q\d\d\d\d)/;
+ my($key) = "$word-$1";
+
+ $tableft{$key} = $pre . $pretext;
+ $tabright{$key} = $poststart .
+ "<font color=\"#FF0A0A\">$casedword</font>" . $posttext;
+ }
+ }
+
+close(IN);
+
+# Now write out the files. Each letter in the index goes in a different file
+
+$current_initial = "";
+
+foreach $key (sort keys %tableft)
+ {
+ my($initial) = $key =~ /^(.)/;
+ $initial = "\U$initial\E";
+
+ if ($initial ne $current_initial)
+ {
+ if ($current_initial ne "")
+ {
+ print OUT "</table>\n";
+ &write_initials($current_initial);
+ print OUT "</body>\n</html>\n";
+ close OUT;
+ }
+
+ open (OUT, ">html/FAQ-KWIC_$initial.html") ||
+ die "Can't open html/FAQ-KWIC_$initial.html\n";
+ print OUT
+ "<html>\n" .
+ "<head>\n" .
+ "<title>Exim FAQ: KWIC index section $initial</title>\n" .
+ "</head>\n" .
+ "<body bgcolor=\"#F8F8F8\" text=\"#00005A\" link=\"#0066FF\" alink=\"#0066FF\" vlink=\"#000099\">\n" .
+ "<h1>Exim FAQ: Keyword-in-context index</h1>\n";
+
+ write_initials($initial);
+
+ if ($initial eq "A")
+ {
+ print OUT <<End ;
+<p>
+This <i>Keyword-in-context</i> index for the Exim FAQ is generated
+automatically from the FAQ source. Browsers may not display the data very
+prettily, but it is hoped that it may provide a useful aid for finding things
+in the FAQ.
+</p>
+End
+ }
+
+ print OUT "<table border>\n";
+ $current_initial = $initial;
+ }
+
+ print OUT "<tr>\n";
+ print OUT "<td align=\"right\">$tableft{$key}</td>\n";
+ print OUT "<td align=\"left\">$tabright{$key}</td>\n";
+ print OUT "</tr>\n";
+ }
+
+# Close the final file
+
+if ($current_initial ne "")
+ {
+ print OUT "</table>\n";
+ &write_initials($current_initial);
+ print OUT "</body>\n</html>\n";
+ close OUT;
+ }
+
+# End
diff --git a/doc/doc-scripts/g2h b/doc/doc-scripts/g2h
new file mode 100755
index 000000000..e940e669b
--- /dev/null
+++ b/doc/doc-scripts/g2h
@@ -0,0 +1,1451 @@
+#! /usr/bin/perl -w
+# $Cambridge: exim/doc/doc-scripts/g2h,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# This is a script that turns the SGCAL source of Exim's documentation into
+# HTML. It can be used for both the filter document and the main Exim
+# specification. The syntax is
+#
+# g2h [-split no|section|chapter] <source file> <title>
+#
+# Previously, -split section was used for the filter document, and -split
+# chapter for the main specification. However, the filter document has gained
+# some chapters, so they are both split by chapter now. Only one -split can be
+# specified.
+#
+# A number of assumptions about the style of the input markup are made.
+#
+# The HTML is written into the directory html/ using the source file base
+# name as its base.
+
+# Written by Philip Hazel
+# Starting 21-Dec-2001
+# Last modified 26-Nov-2003
+
+#############################################################################
+
+
+
+##################################################
+# Open an output file #
+##################################################
+
+sub openout {
+open (OUT, ">$_[0]") || die "Can't open $_[0]\n";
+
+# Boilerplate
+
+print OUT "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n";
+
+print OUT "<html>\n<head>\n<title>$doctitle" .
+ (($thischapter > 0)? " chapter $thischapter" : "") .
+ (($thissection > 0)? " section $thissection" : "") .
+ "</title>\n</head>\n" .
+ "<body bgcolor=\"#F8F8F8\" text=\"#00005A\" " .
+ "link=\"#FF6600\" alink=\"#FF9933\" vlink=\"#990000\">\n";
+
+# Forward/backward links when chapter splitting
+
+if ($chapsplit)
+ {
+ print OUT "<font size=2>\n";
+ printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;\n",
+ $thischapter - 1) if $thischapter > 1;
+ printf OUT ("<a href=\"${file_base}_%s.html\">Next</a>&nbsp;&nbsp;\n",
+ $thischapter + 1) if $thischapter < $maxchapter;
+ print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
+ print OUT "&nbsp;" x 6, "($doctitle)\n</font><hr>\n";
+ }
+
+# Forward/backward links when section splitting
+
+elsif ($sectsplit)
+ {
+ print OUT "<font size=2>\n";
+ printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;\n",
+ $thissection - 1) if $thissection > 1;
+ printf OUT ("<a href=\"${file_base}_%s.html\">Next</a>&nbsp;&nbsp;\n",
+ $thissection + 1) if $thissection < $maxsection;
+ print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
+ print OUT "&nbsp;" x 6, "($doctitle)\n</font><hr>\n";
+ }
+
+# Save the final component of the current file name (for TOC creation)
+
+$_[0] =~ /^(?:.*)\/([^\/]+)$/;
+$current_file = $1;
+}
+
+
+
+##################################################
+# Close an output file #
+##################################################
+
+# The first argument is one of:
+#
+# "CHAP" a chapter is ending
+# "SECT" a section is ending
+# "" the whole thing is ending
+#
+# In the first two cases $thischapter and $thissection contain the new chapter
+# and section numbers, respectively. In the third case, we can deduce what is
+# ending from the flags. The variables contain the current values.
+
+sub closeout {
+my($s) = $_[0];
+
+print OUT "<hr>\n" if !$lastwasrule;
+&setpar(0);
+
+if ($s eq "CHAP")
+ {
+ print OUT "<font size=2>\n";
+ printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;",
+ $thischapter - 2) if ($thischapter > 2);
+ print OUT "<a href=\"${file_base}_$thischapter.html\">Next</a>&nbsp;&nbsp;";
+ print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
+ print OUT "&nbsp;" x 6, "($doctitle)\n</font>\n";
+ }
+
+elsif ($s eq "SECT")
+ {
+ print OUT "<font size=2>\n";
+ printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;",
+ $thissection - 2) if ($thissection > 2);
+ print OUT "<a href=\"${file_base}_$thissection.html\">Next</a>&nbsp;&nbsp;";
+ print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
+ print OUT "&nbsp;" x 6, "($doctitle)\n</font>\n";
+ }
+
+else
+ {
+ if ($chapsplit)
+ {
+ print OUT "<font size=2>\n";
+ printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;",
+ $thischapter - 1) if ($thischapter > 1);
+ print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
+ print OUT "&nbsp;" x 6, "($doctitle)\n</font>\n";
+ }
+ elsif ($sectsplit)
+ {
+ print OUT "<font size=2>\n";
+ printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;",
+ $thissection - 1) if ($thissection > 1);
+ print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
+ print OUT "&nbsp;" x 6, "($doctitle)\n</font>\n";
+ }
+ }
+
+print OUT "</body>\n</html>\n";
+close(OUT);
+}
+
+
+
+##################################################
+# Handle an index line #
+##################################################
+
+# This function returns an empty string so that it can be called as part
+# of an s operator when handling index items within paragraphs. The two
+# arguments are:
+#
+# the text to index, already converted to HTML
+# 1 for the concept index, 0 for the options index
+
+sub handle_index {
+my($text) = $_[0];
+my($hash) = $_[1]? \%cindex : \%oindex;
+my ($key,$ref);
+
+# Up the index count, and compute the reference to the file and the
+# label within it.
+
+$index_count++;
+$ref = $chapsplit?
+ "${file_base}_$thischapter.html#IX$index_count"
+ : $sectsplit?
+ "${file_base}_$thissection.html#IX$index_count"
+ :
+ "#IX$index_count";
+
+# Create the index key, which consists of the text with all the HTML
+# coding and any leading quotation marks removed. Turn the primary/secondary
+# splitting string "||" into ":".
+
+$text =~ s/\|\|/:/g;
+
+$key = "$text";
+$key =~ s/<[^>]+>//g;
+$key =~ s/&#(\d+);/chr($1)/eg;
+$key =~ s/^`+//;
+
+# Turn all spaces in the text into &nbsp; so that they don't ever split.
+# However, there may be spaces in the HTML that already exists in the
+# text, so we have to avoid changing spaces inside <>.
+
+$text =~ s/ (?=[^<>]*(?:<|$))/&nbsp;/g;
+
+# If this is the first encounter with this index key, we create a
+# straightforward reference.
+
+if (!defined $$hash{$key})
+ {
+ $$hash{$key} = "<a href=\"$ref\">$text</a>";
+ }
+
+# For the second and subsequent encounters, add "[2]" etc. to the
+# index text. We find out the number by counting occurrences of "<a"
+# in the existing string.
+
+else
+ {
+ my($number) = 1;
+ $number++ while $$hash{$key} =~ /<a/g;
+ $$hash{$key} .= " &nbsp;<a href=\"$ref\">[$number]</a>";
+ }
+
+# Place the name in the current output
+
+print OUT "<a name=\"IX$index_count\"></a>\n";
+return "";
+}
+
+
+
+##################################################
+# Handle emphasis bars #
+##################################################
+
+# Set colour green for text marked with "emphasis bars", keeping
+# track in case the matching isn't perfect.
+
+sub setinem {
+if ($_[0])
+ {
+ return "" if $inem;
+ $inem = 1;
+ return "<font color=green>\n";
+ }
+else
+ {
+ return "" if !$inem;
+ $inem = 0;
+ return "</font>\n";
+ }
+}
+
+
+
+##################################################
+# Convert marked-up text #
+##################################################
+
+# This function converts text from SGCAL markup to HTML markup, with a couple
+# of exceptions:
+#
+# 1. We don't touch $t because that is handled by the .display code.
+#
+# 2. The text may contain embedded .index, .em, and .nem directives. We
+# handle .em and .nem, but leave .index because it must be done during
+# paragraph outputting.
+#
+# In a non-"rm" display, we turn $rm{ into cancelling of <tt>. Otherwise
+# it is ignored - in practice it is only used in that special case.
+#
+# The order in which things are done in this function is highly sensitive!
+
+sub handle_text {
+my($s) = $_[0];
+my($rmspecial) = $_[1];
+
+# Escape all & characters (they aren't involved in markup) but for the moment
+# use &+ instead of &# so that we can handle # characters in the text.
+
+$s =~ s/&/&+038;/g;
+
+# Turn SGCAL literals into HTML literals that don't look like SGCAL
+# markup, so won't be touched by what follows. Again, use + instead of #.
+
+$s =~ s/@@/&+064;/g;
+$s =~ s/@([^@])/"&+".sprintf("%0.3d",ord($1)).";"/eg;
+
+# Now turn any #s that are markup into spaces, and convert the previously
+# created literals to the correct form.
+
+$s =~ s/#/&nbsp;/g;
+$s =~ s/&\+(\d+);/&#$1;/g;
+
+# Some simple markup that doesn't involve argument text.
+
+$s =~ s/\$~//g; # turn $~ into nothing
+$s =~ s/__/_/g; # turn __ into _
+$s =~ s/--(?=$|\s|\d)/&#150;/mg; # turn -- into endash in text or number range
+$s =~ s/\(c\)/&copy;/g; # turn (c) into copyright symbol
+
+# Use double quotes
+
+# $s =~ s/`([^']+)'/``$1''/g;
+
+$s =~ s/`([^']+)'/&#147;$1&#148;/g;
+
+# This is a fudge for some specific usages of $<; can't just do a global
+# is it occurs in things like "$<variable name>" as well.
+
+$s =~ s/(\d)\$<-/$1-/g; # turn 0$<- into 0-
+$s =~ s/\$<//g; # other $< is ignored
+
+# Turn <<...>> into equivalent SGCAL markup that doesn't involve the use of
+# < and >, and then escape the remaining < and > characters in the text.
+
+$s =~ s/<<([^>]*?)>>/<\$it{$1}>/g; # turn <<xxx>> into <$it{xxx}>
+$s =~ s/</&#060;/g;
+$s =~ s/>/&#062;/g;
+
+# Other markup...
+
+$s =~ s/\$sm\{//g; # turn $sm{ into nothing
+$s =~ s/\$smc\{//g; # turn $smc{ into nothing
+$s =~ s/\$smi\{//g; # turn $smi{ into nothing
+
+$s =~ s/\$tt\{([^\}]*?)\}/<tt>$1<\/tt>/g; # turn $tt{xxx} into <tt>xxx</tt>
+$s =~ s/\$it\{([^\}]*?)\}/<em>$1<\/em>/g; # turn $it{xxx} into <em>xxx</em>
+$s =~ s/\$bf\{([^\}]*?)\}/<b>$1<\/b>/g; # turn $bf{xxx} into <b>xxx</b>
+
+$s =~ s/\$cb\{([^\}]*?)\}/<tt><b>$1<\/b><\/tt>/g; # turn $cb{xxx} into
+ # <tt><b>xxx</b></tt>
+
+$s =~ s/\\\\([^\\]*?)\\\\/<font size=-1>$1<\/font>/g; # turn \\xxx\\ into
+ # small font
+$s =~ s/\\\?([^?]*?)\?\\/<a href="$1">$1<\/a>/g; # turn \?URL?\ into URL
+
+$s =~ s/\\\(([^)]*?)\)\\/<i>$1<\/i>/g; # turn \(xxx)\ into <i>xxx</i>
+$s =~ s/\\\"([^\"]*?)\"\\/<tt>$1<\/tt>/g; # turn \"xxx"\ into <tt>xxx</tt>
+
+
+$s =~ s/\\\$([^\$]*?)\$\\/<tt>\$$1<\/tt>/g; # turn \$xxx$\ into <tt>$xxx</tt>
+$s =~ s/\\\-([^\\]*?)\-\\/<i>-$1<\/i>/g; # turn \-xxx-\ into -italic
+$s =~ s/\\\*\*([^*]*?)\*\*\\/<b>$1<\/b>/g; # turn \**xxx**\ into <b>xxx</b>
+$s =~ s/\\\*([^*]*?)\*\\/<i>$1<\/i>/g; # turn \*xxx*\ into italic
+$s =~ s/\\%([^*]*?)%\\/<b>$1<\/b>/g; # turn \%xxx%\ into bold
+$s =~ s/\\([^\\]*?)\\/<tt>$1<\/tt>/g; # turn \xxx\ into <tt>xxx</tt>
+$s =~ s/::([^\$]*?)::/<i>$1:<\/i>/g; # turn ::xxx:: into italic:
+$s =~ s/\$\*\$/\*/g; # turn $*$ into *
+
+# Handle $rm{...}
+
+if ($rmspecial)
+ {
+ $s =~ s/\$rm\{([^\}]*?)\}/<\/tt>$1<tt>/g; # turn $rm{xxx} into </tt>xxx<tt>
+ }
+else
+ {
+ $s =~ s/\$rm\{([^\}]*?)\}/$1/g; # turn $rm{xxx} into xxx
+ }
+
+# There is one case where the terminating } of an escape sequence is
+# in another paragraph - this follows $sm{ - it can be fixed by
+# removing any stray } in a paragraph that contains no { chars.
+
+$s =~ s/\}//g if !/\{/;
+
+# Remove any null flags ($$)
+
+$s =~ s/\$\$//g;
+
+# If the paragraph starts with $c\b, remove it.
+
+$s =~ s/^\$c\b//;
+
+# If the paragraph starts with $e\b, indent it slightly.
+
+$s =~ s/^\$e\b/&nbsp;&nbsp;/;
+
+# Handle .em, and .nem directives that occur within the paragraph
+
+$s =~ s/\.em\s*\n/&setinem(1)/eg;
+$s =~ s/\.nem\s*\n/&setinem(0)/eg;
+
+# Explicitly included HTML
+
+$s =~ s/\[\(([^)]+)\)\]/<$1>/g; # turn [(...)] into <...>
+
+# Finally, do the substitutions and return the modified text.
+
+$s =~ s/~~(\w+)/$var_value{$1}/eg;
+
+return $s;
+}
+
+
+
+##################################################
+# Start/end a paragraph #
+##################################################
+
+# We want to leave paragraphs unterminated until we know that a horizontal
+# rule does not follow, to avoid getting space inserted before the rule,
+# which doesn't look good. So we have this function to help control things.
+# If the argument is 1 we are starting a new paragraph; if it is 0 we want
+# to force the ending of any incomplete paragraph.
+
+sub setpar {
+if ($inpar)
+ {
+ print OUT "</p>\n";
+ $inpar = 0;
+ }
+if ($_[0])
+ {
+ print OUT "<p>\n";
+ $inpar = 1;
+ }
+}
+
+
+
+##################################################
+# Handle a "paragraph" #
+##################################################
+
+# Read a paragraph of text, which may contain many lines and may contain
+# .index, .em, and .nem directives within it. We may also encounter
+# ".if ~~html" within paragraphs. Process those directives,
+# convert the markup, and output the rest as an HTML paragraph.
+
+
+sub handle_paragraph{
+my($par) = $_;
+my($htmlcond) = 0;
+while(<IN>)
+ {
+ if (/^\.if\s+~~html\b/)
+ {
+ $htmlcond = 1;
+ $par =~ s/\s+$//; # lose unwanted whitespace and newlines
+ next;
+ }
+ elsif ($htmlcond && /^\.else\b/)
+ {
+ while (<IN>) { last if /^\.fi\b/; }
+ $htmlcond = 0;
+ next;
+ }
+ elsif ($htmlcond && /^\.fi\b/)
+ {
+ $htmlcond = 0;
+ next;
+ }
+
+ last if /^\s*$/ || (/^\./ && !/^\.index\b/ && !/^\.em\b/ && !/^\.nem\b/);
+ $par .= $_;
+ }
+$par = &handle_text($par, 0);
+
+# We can't handle .index until this point, when we do it just before
+# outputting the paragraph.
+
+if ($par !~ /^\s*$/)
+ {
+ &setpar(1);
+ $par =~ s/\.index\s+([^\n]+)\n/&handle_index($1, 1)/eg;
+ print OUT "$par";
+ }
+}
+
+
+
+##################################################
+# Handle a non-paragraph directive #
+##################################################
+
+# The directives .index, .em, and .nem can also appear within paragraphs,
+# and are then handled within the handle_paragraph() code.
+
+sub handle_directive{
+my($new_lastwasitem) = 0;
+
+$lastwasrule = 0;
+
+if (/^\.r?set\b/ || /^\.(?:\s|$)/) {} # ignore .(r)set and comments
+
+elsif (/^\.justify\b/) {} # and .justify
+
+elsif (/^\.newline\b/) { print OUT "<br>\n"; }
+
+elsif (/^\.blank\b/ || /^\.space\b/) { print OUT "<br>\n"; }
+
+elsif (/^\.rule\b/) { &setpar(0); print OUT "<hr>\n"; $lastwasrule = 1; }
+
+elsif (/^\.index\s+(.*)/) { &handle_index(&handle_text($1), 1); }
+
+# Emphasis is handled by colour
+
+elsif (/^\.em\b/)
+ {
+ &setpar(0);
+ print OUT "<font color=green>" if ! $inem;
+ $inem = 1;
+ }
+
+elsif (/^\.nem\b/)
+ {
+ &setpar(0);
+ print OUT "</font>" if $inem;
+ $inem = 0;
+ }
+
+# Ignore tab setting stuff - we use tables instead.
+
+elsif (/^\.tabs(?:et)?\b/) {}
+
+# .tempindent is used only to align some of the expansion stuff nicely;
+# just ignore it. It is used in conjunction with .push/.pop.
+
+elsif (/^\.(tempindent|push|pop)\b/) {}
+
+# There are some instances of .if ~~sys.fancy in the source. Some of those
+# that are not inside displays are two-part things, in which case we just keep
+# the non-fancy part. For diagrams, however, they are in three parts:
+#
+# .if ~~sys.fancy
+# <aspic drawing stuff for PostScript and PDF>
+# .elif !~~html
+# <ascii art for txt and Texinfo>
+# .else
+# <HTML instructions for including a gif>
+# .fi
+#
+# In this case, we skip to the third part.
+
+elsif (/^\.if\s+~~sys\.fancy/ || /^\.else\b/)
+ {
+ while (<IN>)
+ { last if /^\.else\b/ || /^\.elif\s+!\s*~~html/ || /^\.fi\b/; }
+
+ if (/^\.elif\b/)
+ {
+ while (<IN>) { last if /^\.else\b/ || /^\.fi\b/; }
+ }
+ }
+
+# Similarly, for .if !~~sys.fancy, take the non-fancy part.
+
+elsif (/^\.if\s+!\s*~~sys.fancy/) {}
+
+# There are some explicit tests for ~~html for direct HTML inclusions
+
+elsif (/^\.if\s+~~html\b/) {}
+
+# There are occasional requirements to do things differently for Texinfo/HTML
+# and PS/txt versions. The latter are produced by SGCAL, so that's what the
+# flag is called.
+
+elsif (/\.if\s+~~sgcal/)
+ {
+ while (<IN>) { last if /\.else\b/ || /\.fi\b/; }
+ }
+
+# Also there is a texinfo flag
+
+elsif (/^\.if\s+~~texinfo\b/)
+ {
+ while (<IN>)
+ { last if /^\.else\b/ || /^\.elif\s+!\s*~~html/ || /^\.fi\b/; }
+ }
+
+# Ignore any other .if, .else, or .fi directives
+
+elsif (/^\.if\b/ || /^\.fi\b/ || /^\.else\b/) {}
+
+# Ignore .indent
+
+elsif (/^\.indent\b/) {}
+
+# Various flavours of numberpars map to corresponding list types.
+
+elsif (/^\.numberpars\b/)
+ {
+ $rest = $';
+ &setpar(0);
+
+ if ($rest =~ /(?:\$\.|\" \")/)
+ {
+ unshift @endlist, "ul";
+ unshift @listtype, "";
+ print OUT "<ul>\n<li>";
+ }
+ else
+ {
+ $nptype = ($rest =~ /roman/)? "a" : "1";
+ unshift @endlist, "ol";
+ unshift @listtype, " TYPE=\"$nptype\"";
+ print OUT "<ol>\n<li$listtype[0]>";
+ }
+ }
+
+elsif (/^\.nextp\b/)
+ {
+ &setpar(0);
+ print OUT "</li>\n<li$listtype[0]>";
+ }
+
+elsif (/^\.endp\b/)
+ {
+ &setpar(0);
+ print OUT "</li>\n</$endlist[0]>\n";
+ shift @listtype;
+ shift @endlist;
+ }
+
+# .display asis can use <pre> which uses a typewriter font.
+# Otherwise, we have to do our own line breaking. Turn tabbed lines
+# into an HTML table. There will always be a .tabs line first.
+
+elsif (/^\.display\b/)
+ {
+ my($intable) = 0;
+ my($asis) = /asis/;
+ my($rm) = /rm/;
+ my($eol,$indent);
+
+ # For non asis displays, start a paragraph, and set up to put an
+ # explicit break after every line.
+
+ if (!$asis)
+ {
+ &setpar(1);
+ $eol = "<br>";
+ $indent = "<tt>&nbsp;&nbsp;</tt>";
+ }
+
+ # For asis displays, use <pre> and no explicit breaks
+
+ else
+ {
+ print OUT "<pre>\n";
+ $eol = "";
+ $indent = "&nbsp;&nbsp;";
+ }
+
+ # Now read through until we hit .endd (or EOF, but that shouldn't happen)
+ # and process the lines in the display.
+
+ while (<IN>)
+ {
+ last if /^\.endd\b/;
+
+ # The presence of .tabs[et] starts a table
+
+ if (/^\.tabs/)
+ {
+ $intable = 1;
+ print OUT "<table cellspacing=0 cellpadding=0>\n";
+ }
+
+ # Some displays have an indent setting - ignore
+
+ elsif (/^\.indent\b/) {}
+
+ # Some displays have .blank inside them
+
+ elsif (/^\.blank\b/)
+ {
+ print OUT "<br>\n";
+ }
+
+ # Some displays have emphasis inside them
+
+ elsif (/^\.em\b/)
+ {
+ print OUT "<font color=green>" if ! $inem;
+ $inem = 1;
+ }
+
+ elsif (/^\.nem\b/)
+ {
+ print OUT "</font>" if $inem;
+ $inem = 0;
+ }
+
+ # There are occasional instances of .if [!]~~sys.fancy inside displays.
+ # In both cases we want the non-fancy alternative. (The only thing that
+ # matters in practice is noticing .tabs[et] actually.) Assume the syntax
+ # is valid.
+
+ elsif (/^\.if\s+~~sys.fancy/ || /^\.else\b/)
+ {
+ while (<IN>)
+ {
+ last if /^\.fi\b/ || /^\.else/;
+ }
+ }
+
+ elsif (/^\.if\s+!\s*~~sys.fancy/) {}
+
+ elsif (/^\.fi\b/) {}
+
+ # Ignore .newline and .linelength
+
+ elsif (/^\.newline\b/ || /^\.linelength\b/) {}
+
+ # Ignore comments
+
+ elsif (/^\.(\s|$)/) {}
+
+ # There shouldn't be any other directives inside displays
+
+ elsif (/^\./)
+ {
+ print "*** Ignored directive inside .display: $_";
+ }
+
+ # Handle a data line within a display. If it's an asis display, the only
+ # conversion is to escape the HTML characters. Otherwise, process the
+ # SGCAL markup.
+
+ else
+ {
+ chomp;
+ if ($asis)
+ {
+ s/&/&#038;/g;
+ s/</&#060;/g;
+ s/>/&#062;/g;
+ }
+ else
+ {
+ $_ = &handle_text($_, !$rm);
+ $_ = "<tt>$_</tt>" if !$rm && $_ ne "";
+ }
+
+ # In a table, break fields at $t. For non-rm we must break the
+ # <tt> group as well.
+
+ if ($intable)
+ {
+ if ($rm)
+ {
+ s/\s*\$t\s*/&nbsp;&nbsp;<\/td><td>/g;
+ }
+ else
+ {
+ s/\s*\$t\s*/&nbsp;&nbsp;<\/tt><\/td><td><tt>/g;
+ }
+ s/<tt><\/tt>//g;
+ print OUT "<tr><td>&nbsp;&nbsp;$_</td></tr>\n";
+ }
+
+ # Otherwise, output straight, with <br> for non asis displays
+
+ else
+ {
+ s/<tt><\/tt>//g;
+ print OUT "$indent$_$eol\n";
+ }
+ }
+ } # Loop for display contents
+
+ # Finish off the table and the <pre> - leave a paragraph open
+
+ print OUT "</table>\n" if $intable;
+ print OUT "</pre>\n" if $asis;
+ }
+
+# Handle configuration option definitions
+
+elsif (/^\.startconf\b/) {}
+
+elsif (/^\.conf\b/)
+ {
+ my($option, $type, $default) =
+ /^\.conf\s+(\S+)\s+("(?:[^"]|"")+"|\S+)\s+("(?:[^"]|"")+"|.*)/;
+
+ $option =~ s/\@_/_/g; # Underscore will be quoted in option name
+
+ # If $type ends with $**$, add ",expanded" as there doesn't seem to be
+ # a dagger character generally available.
+
+ $type =~ s/^"([^"]+)"/$1/;
+ $type =~ s/\$\*\*\$/, expanded/;
+
+ # Default may be quoted, and it may also have quotes that are required,
+ # if it is a string.
+
+ $default =~ s/^"(.*)"$/$1/;
+ $default =~ s/""/"/g;
+ $default = &handle_text($default, 0);
+
+ print OUT "<hr>";
+ &setpar(0);
+ &handle_index($option, 0);
+ print OUT "<h3>$option</h3>\n" .
+ "<i>Type:</i>&nbsp; $type<br><i>Default:</i>&nbsp; $default<br>\n";
+ }
+
+elsif (/^\.endconf\b/)
+ {
+ print OUT "<hr><br>\n";
+ }
+
+
+# Handle "items" - used for expansion items and the like. We force the
+# item text into bold, and put a rule between items.
+
+elsif (/^\.startitems\b/) {}
+
+elsif (/^\.item\s+(.*)/)
+ {
+ my($arg) = $1;
+ chomp($arg);
+ $arg =~ s/^"(.*)"$/$1/;
+ $arg = &handle_text($arg, 0);
+
+ # If there are two .items in a row, we don't want to put in the
+ # separator line or start a new paragraph.
+
+ if ($lastwasitem)
+ {
+ print OUT "<br>";
+ }
+ else
+ {
+ print OUT "<hr>";
+ &setpar(1);
+ }
+ print OUT "<b>$arg</b>\n";
+ $new_lastwasitem = 1;
+ }
+
+elsif (/^\.enditems\b/)
+ {
+ print OUT "<hr><br>\n";
+ }
+
+
+# Handle command line option items
+
+elsif (/^\.startoptions\b/) {}
+
+elsif (/^\.option\s+(.*)/)
+ {
+ my($arg) = $1;
+ $arg =~ s/^"(.*)"$/$1/;
+
+ print OUT "<hr>";
+ &setpar(0);
+
+ # For indexing, we want to take up to the first # or < in the line,
+ # before processing.
+
+ my($name) = $arg =~ /^([^#<]+)/;
+ $name = &handle_text($name, 0);
+ &handle_index("-$name", 0);
+
+ # Output as heading, after the index
+
+ $arg = &handle_text($arg, 0);
+ print OUT "<h3>-$arg</h3>\n";
+ }
+
+elsif (/^\.endoptions\b/)
+ {
+ print OUT "<hr><br>\n";
+ }
+
+# Found an SGCAL directive that isn't dealt with. Oh dear.
+
+else
+ {
+ print "*** Unexpected SGCAL directive: line $. ignored:\n";
+ print "$_\n";
+ }
+
+# Remember if last was a .item, and read the next line
+
+$lastwasitem = $new_lastwasitem;
+$_ = <IN>;
+}
+
+
+
+##################################################
+# First Pass - collect references #
+##################################################
+
+sub pass_one{
+$thischapter = 0;
+
+open (IN, $source_file) || die "Can't open $source_file (first pass)\n";
+$_ = <IN>;
+
+# At the start of the specification text, there are some textual replacement
+# definitions. They set values, but not cross-references.
+
+while (/^\.r?set\s+(\S+)\s+"?([^"]+)\"?\s*$/)
+ {
+ $var_value{$1} = $2;
+ $_ = <IN>;
+ }
+
+# Now skip on till we hit the start of the first chapter. It will be numbered
+# 0 if we hit ".set chapter -1". There is only ever one unnumbered chapter.
+
+while (!/^\.chapter/)
+ {
+ $thischapter = -1 if /^\.set\s+chapter\s+-1/;
+ $_ = <IN>;
+ }
+
+# Loop for handling chapters
+
+while ($_)
+ {
+ $thischapter++;
+ $thissection = 0;
+
+ # Scan through chapter, setting up cross-references to the chapter
+ # and to the sections within it.
+
+ while (<IN>)
+ {
+ last if /^\.chapter/;
+ chomp;
+
+ if (/^\.section/)
+ {
+ $thissection++;
+ next;
+ }
+
+ # Handle .(r)set directives.
+
+ if (/^\.r?set\s+(\S+)\s+"?([^"]+)\"?\s*$/ && $1 ne "runningfoot")
+ {
+ my($key,$value) = ($1,$2);
+ $value =~ s/~~chapter/$thischapter/e;
+ $value =~ s/~~section/$thissection/e;
+
+ # Only one of $chapsplit or $sectsplit can be set.
+
+ if ($key =~ /^CHAP/)
+ {
+ $value = $chapsplit?
+ "<a href=\"${file_base}_$thischapter.html\">$value</a>"
+ :
+ "<a href=\"#CHAP$thischapter\">$value</a>";
+ }
+
+ elsif ($key =~ /^SECT/)
+ {
+ $value = $chapsplit?
+ "<a href=\"${file_base}_$thischapter.html" .
+ "#SECT$thischapter.$thissection\">$value</a>"
+ :
+ $sectsplit? "<a href=\"${file_base}_$thissection.html\">$value</a>"
+ :
+ "<a href=\"#SECT$thischapter.$thissection\">$value</a>";
+ }
+
+ $var_value{$key} = $value;
+ }
+ }
+ }
+
+close(IN);
+}
+
+
+
+
+
+##################################################
+# Second Pass - generate HTML #
+##################################################
+
+sub pass_two{
+my($tocn) = 0;
+my($inmacro) = 0;
+my($insection) = 0;
+
+$inem = 0;
+$thischapter = 0;
+$thissection = 0;
+
+# Open the source file and get the first line
+
+open (IN, $source_file) || die "Can't open $source_file (2nd pass)\n";
+$_ = <IN>;
+
+# Skip on till we hit the start of the first chapter, but note if we
+# pass ".set chapter -1", which is used to indicate no chapter numbering for
+# the first chapter (we number is 0). Keep track of whether we are in macro
+# definitions or not, and when not, notice occurrences of .index, because this
+# are the "x see y" type entries.
+
+while (!/^\.chapter/)
+ {
+ $thischapter = -1 if /^\.set\s+chapter\s+-1/;
+ $inmacro = 1 if /^\.macro/;
+ $inmacro = 0 if /^\.endm/;
+ if (!$inmacro && /^\.index\s+(.*)/)
+ {
+ my($key);
+ my($s) = $1;
+ $s = &handle_text($s, 0);
+ $s =~ s/ /&nbsp;/g; # All spaces unsplittable
+ $key = "\L$s";
+ $key =~ s/<[^>]+>//g;
+ $key =~ s/&#(\d+);/chr($1)/eg;
+ $cindex{$key} = $s;
+ }
+ $_ = <IN>;
+ }
+
+# Open the TOC file
+
+open (TOC, ">$html/${file_base}_toc.html") ||
+ die "Can't open $html/${file_base}_toc.html\n";
+
+print TOC "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n";
+print TOC "<html>\n<head>\n<title>$doctitle Contents</title>\n</head>\n" .
+ "<body bgcolor=\"#F8F8F8\" text=\"#00005A\" " .
+ "link=\"#FF6600\" alink=\"#FF9933\" vlink=\"#990000\">\n";
+print TOC "<h1>$doctitle</h1><hr>\n<ul>\n";
+
+# Open the data file if we are not splitting at chapters
+
+&openout("$html/${file_base}.html") if !$chapsplit;
+
+# Loop for handling chapters. At the start of this loop, $_ is either EOF,
+# or contains a .chapter line.
+
+$firstchapter = $thischapter + 1;
+
+while ($_)
+ {
+ print TOC "</ul>\n" if $insection;
+ $insection = 0;
+
+ $thischapter++;
+ $thissection = 0;
+ $lastwasrule = 0;
+
+ # Start a new file if required
+
+ if ($chapsplit)
+ {
+ &closeout("CHAP") if $thischapter != $firstchapter;
+ &openout("$html/${file_base}_$thischapter.html");
+ }
+
+ # Set up the chapter title. Save it for the TOC. Set up the anchor and
+ # link back to the TOC and show the title.
+
+ $_ =~ /^\.chapter\s+(.*)/;
+
+ my($title) = (($thischapter > 0)? "$thischapter. " : "") . &handle_text($1, 0);
+
+ $tocn++;
+ print TOC "<li><a " .
+ "name=\"TOC$tocn\" " .
+ "href=\"$current_file#CHAP$thischapter\">$title</a></li>\n";
+
+ print OUT "<h1>\n";
+ print OUT "<a name=\"CHAP$thischapter\" href=\"${file_base}_toc.html#TOC$tocn\">\n";
+ print OUT "$title\n</a></h1>\n";
+
+ # Scan the contents of the chapter
+
+ $_ = <IN>;
+ while ($_)
+ {
+ last if /^\.chapter/;
+
+ # Handle the start of a new section, starting a new file if required
+
+ if (/^\.section\s+(.*)/)
+ {
+ $thissection++;
+
+ print TOC "<ul>\n" if !$insection;
+ $insection = 1;
+
+ my($title) = (($thischapter > 0)? "$thischapter." : "") .
+ "$thissection. " . &handle_text($1, 0);
+
+ if ($sectsplit)
+ {
+ &closeout("SECT");
+ &openout("$html/${file_base}_$thissection.html");
+ }
+
+ $tocn++;
+ printf TOC ("<li><a " .
+ "name=\"TOC$tocn\" " .
+ "href=\"$current_file#SECT%s$thissection\">%s</a></li>\n",
+ ($thischapter > 0)? "$thischapter." : "", $title);
+
+ &setpar(0);
+ print OUT "<h2>\n";
+ printf OUT ("<a name=\"SECT%s$thissection\" ",
+ ($thischapter > 0)? "$thischapter." : "");
+ print OUT "href=\"${file_base}_toc.html#TOC$tocn\">\n";
+ print OUT "$title\n</a></h2>\n";
+ $_ = <IN>;
+ $lastwasrule = 0;
+ }
+
+ # Blank lines at this level are ignored
+
+ elsif (/^\s*$/)
+ {
+ $_ = <IN>;
+ }
+
+ # Directive and non-directive lines are handled independently, though
+ # in each case further lines may be read. Afterwards, the next line is
+ # in $_. If .em is at the start of a paragraph, treat it with the
+ # paragraph, because the matching .nem will be too. Messy!
+
+ elsif (/^\./)
+ {
+ if (/^\.em\b/)
+ {
+ $_=<IN>;
+ if (/^\./)
+ {
+ print OUT "<font color=green>" if ! $inem;
+ $inem = 1;
+ # Used to handle it here - but that fails if it is .section.
+ # Just let the next iteration of the loop handle it.
+ # &handle_directive();
+ }
+
+ else
+ {
+ $_ = ".em\n" . $_;
+ &handle_paragraph();
+ $lastwasrule = 0;
+ $lastwasitem = 0;
+ }
+ }
+
+ # Not .em
+
+ else
+ {
+ &handle_directive();
+ }
+ }
+
+ # Not a directive
+
+ else
+ {
+ &handle_paragraph();
+ $lastwasrule = 0;
+ $lastwasitem = 0;
+ }
+
+ } # Loop for each line in a chapter
+ } # Loop for each chapter
+
+# Close the last file, end off the TOC, and we are done.
+
+&closeout("");
+
+print TOC "</ul>\n" if $insection;
+
+if (defined %cindex)
+ {
+ $cindex_tocn = ++$tocn;
+ print TOC "<li><a name=\"TOC$tocn\" ".
+ "href=\"${file_base}_cindex.html\">Concept Index</a></li>\n";
+ }
+
+if (defined %oindex)
+ {
+ $oindex_tocn = ++$tocn;
+ print TOC "<li><a name=\"TOC$tocn\" ".
+ "href=\"${file_base}_oindex.html\">Option Index</a></li>\n";
+ }
+
+print TOC "</ul>\n</body>\n</html>\n";
+close(TOC);
+close(IN);
+}
+
+
+
+
+##################################################
+# Adjust index points #
+##################################################
+
+# Because of the way the source is written, there are often index entries
+# that immediately follow the start of chapters and sections and the definition
+# of "items" like "helo = verify". This gets the correct page numbers for the
+# PostScript and PDF formats. However, for HTML we want the index anchor to be
+# before the section heading, because browsers tend to put the index point at
+# the top of the screen. So we re-read all the files we've just created, and
+# move some of the index points about. This is necessary only if indexes exist.
+# The files are small enough to be handled entirely in memory.
+
+sub adjust_index_points {
+print "Adjusting index points to precede headings\n";
+
+$" = "";
+
+opendir(DIR, "$html") || die "Failed to opendir $html\n";
+while ($file = readdir(DIR))
+ {
+ my($i);
+ next unless $file =~ /^${file_base}_\d+\.html$/;
+
+ open(IN, "<$html/$file") ||
+ die "Failed to open $html/$file (read)\n";
+ my(@lines) = <IN>;
+ close(IN);
+
+ for ($i = 0; $i < @lines; $i++)
+ {
+ if ($lines[$i] =~ /^<a name="IX\d+"><\/a>$/)
+ {
+ # Handle an index line that follows a heading definition. Move it back
+ # to just before the <h1> or whatever. This preserves the order of
+ # multiple index lines, not that that matters.
+
+ if ($lines[$i-1] =~ /^<\/a><\/h(\d)>/)
+ {
+ my($j);
+ my($found) = 0;
+ for ($j = $i-2; $j > 0 && $j > $i - 10; $j--)
+ {
+ if ($lines[$j] =~ /<h$1>/)
+ {
+ $found = 1;
+ last;
+ }
+ }
+ if ($found)
+ {
+ splice(@lines, $j, 0, splice(@lines, $i, 1));
+ }
+ }
+
+ # Handle an index line that follows an "item". Move it back one line.
+
+ elsif ($lines[$i-1] =~ /^<b>.*<\/b>\s*$/)
+ {
+ splice(@lines, $i-1, 0, splice(@lines, $i, 1));
+ }
+
+ # Handle an index line that follows a "conf" definition
+
+ elsif ($lines[$i-1] =~ /^<i>Type:<\/i>/ && $lines[$i-2] =~ /^<h3>/)
+ {
+ splice(@lines, $i-2, 0, splice(@lines, $i, 1));
+ }
+
+ # Handle an index line that follows an "option" definition
+
+ elsif ($lines[$i-1] =~ /^<h3>/)
+ {
+ splice(@lines, $i-1, 0, splice(@lines, $i, 1));
+ }
+ }
+ }
+
+ open(OUT, ">$html/$file") ||
+ die "Failed to open $html/$file (write)\n";
+
+ print OUT "@lines";
+ close OUT;
+ undef @lines;
+ }
+}
+
+
+
+
+##################################################
+# Create Index #
+##################################################
+
+sub create_index{
+my($hash) = $_[0];
+my($ifname) = $_[1];
+my($ititle) = $_[2];
+my(%indexindex);
+
+open(INDEX, ">$html/${file_base}_$_[1].html") ||
+ die "Failed to open $html/${file_base}_$ifname\n";
+
+print INDEX "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n";
+print INDEX "<html>\n<head>\n<title>$doctitle $ititle</title>\n";
+print INDEX "<base target=\"body\">\n</head>\n";
+
+print INDEX "<body bgcolor=\"#FFFFDF\" text=\"#00005A\" " .
+ "link=\"#FF6600\" alink=\"#FF9933\" vlink=\"#990000\">\n";
+
+print INDEX "<h3>$ititle</h3>\n";
+
+# We have to scan the keys in the hash twice; first to build the list
+# of initial letters, and then to do the business. The first time we
+# do not need to sort them.
+
+foreach $key (keys %$hash)
+ {
+ my($initial) = substr($key,0,1);
+ $initial = "\U$initial";
+ $indexindex{$initial} = 1 if $initial ge "A";
+ }
+
+print INDEX "<p>\n";
+foreach $key (sort keys %indexindex)
+ {
+ print INDEX "&nbsp;<a href=\"#$key\" target=\"index\">$key</a>\n";
+ }
+print INDEX "<hr></p>\n";
+
+my($letter) = "";
+print INDEX "<p>\n";
+
+foreach $key (sort
+ { ("\L$a" eq "\L$b")? ("$a" cmp "$b") : ("\L$a" cmp "\L$b") }
+ keys %$hash)
+ {
+ my($initial) = substr($key,0,1);
+ $initial = "\U$initial";
+ if ($initial ne $letter)
+ {
+ if ($initial ge "A")
+ {
+ print INDEX "<br>\n" if $letter ne "";
+ print INDEX "<a name=\"$initial\"></a>\n";
+ print INDEX "<font size=\"+1\">\U$initial\E</font><br>\n";
+ }
+ $letter = $initial;
+ }
+ print INDEX "$$hash{$key}<br>\n";
+ }
+
+print INDEX "</p>\n";
+
+print INDEX "</body>\n</html>\n";
+close(INDEX);
+}
+
+
+
+
+##################################################
+# Show usage and die #
+##################################################
+
+sub usage {
+die "Usage: g2h [-split no|section|chapter] <source> <title>\n";
+}
+
+
+
+##################################################
+# Entry point and main program #
+##################################################
+
+
+# Directory in which to put the new HTML files
+
+$html = "html";
+
+# Global variables.
+
+%cindex = ();
+%oindex = ();
+
+$chapsplit = 0;
+$cindex_tocn = 0;
+$file_base = "";
+$index_count = 0;
+$inem = 0;
+$inpar = 0;
+$lastwasitem = 0;
+$lastwasrule = 0;
+$oindex_tocn = 0;
+$sectsplit = 0;
+$source_file = "";
+$thischapter = 0;
+$thissection = 0;
+
+
+# Handle options
+
+my($splitset) = 0;
+
+while (scalar @ARGV > 0 && $ARGV[0] =~ /^-/)
+ {
+ if ($ARGV[0] eq "-split" && !$splitset)
+ {
+ $splitset = 1;
+ shift @ARGV;
+ my($type) = shift @ARGV;
+ if ($type eq "section") { $sectsplit = 1; }
+ elsif ($type eq "chapter") { $chapsplit = 1; }
+ elsif ($type eq "no" ) { $sectsplit = $chapsplit = 0; }
+ else { &usage(); }
+ }
+ else { &usage(); }
+ }
+
+# Get the source file and its base
+
+&usage() if scalar @ARGV <= 0;
+$source_file = shift @ARGV;
+($file_base) = $source_file =~ /^(.*)\.src$/;
+
+&usage() if scalar @ARGV <= 0;
+$doctitle = shift @ARGV;
+
+print "\nCreate HTML for $doctitle from $source_file\n";
+
+# Remove the old HTML files
+
+print "Removing old HTML files\n";
+system("/bin/rm -rf $html/${file_base}_*.html");
+
+# First pass identifies all the chapters and sections, and collects the
+# values of the cross-referencing variables.
+
+print "Scanning for cross-references\n";
+&pass_one();
+
+$maxchapter = $thischapter; # Used if chapter splitting
+$maxsection = $thissection; # Used if section splitting
+
+# Second pass actually creates the HTML files.
+
+print "Creating the HTML files\n";
+&pass_two();
+
+# Reprocess for moving some of the index points, if indexes were created
+
+&adjust_index_points() if scalar(keys %cindex) > 0 || scalar(keys %oindex) > 0;
+
+# Finally, we must create the option and concept indexes if any data
+# has been collected for them.
+
+if (scalar(keys %cindex) > 0)
+ {
+ print "Creating concept index\n";
+ &create_index(\%cindex, "cindex", "Concepts");
+ }
+
+if (scalar(keys %oindex) > 0)
+ {
+ print "Creating option index\n";
+ &create_index(\%oindex, "oindex", "Options");
+ }
+
+# End of g2h
diff --git a/doc/doc-scripts/g2man b/doc/doc-scripts/g2man
new file mode 100755
index 000000000..e3006b5ec
--- /dev/null
+++ b/doc/doc-scripts/g2man
@@ -0,0 +1,251 @@
+#! /usr/bin/perl -w
+# $Cambridge: exim/doc/doc-scripts/g2man,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# Script to find the command line options in the Exim spec, and turn them
+# into a man page, because people like that.
+
+
+##################################################
+# De-markup one line #
+##################################################
+
+sub process {
+my($x) = $_[0];
+
+# Hide SGCAL escapes
+
+$x =~ s/\@\@/&&a/g; # @@
+$x =~ s/\@\\/&&b/g; # @\
+$x =~ s/\@</&&l/g; # @<
+$x =~ s/\@>/&&g/g; # @>
+$x =~ s/\@\{/&&c/g; # @{
+$x =~ s/\@\}/&&d/g; # @}
+$x =~ s/\@#/&&s/g; # @#
+$x =~ s/\@(.)/$1/g; # all other @s
+
+# Convert SGCAL markup
+
+$x =~ s/#/ /g; # turn # into a space
+$x =~ s/\$~//g; # turn $~ into nothing
+$x =~ s/__/_/g; # turn __ into _
+$x =~ s/\$sc\{([^\}]*?)\}/$1/g; # turn $sc{xxx} into xxx
+$x =~ s/\$st\{([^\}]*?)\}/$1/g; # turn $st{xxx} into xxx
+$x =~ s/\$si\{([^\}]*?)\}/$1/g; # turn $si{xxx} into xxx
+$x =~ s/\$tt\{([^\}]*?)\}/$1/g; # turn $tt{xxx} into xxx
+$x =~ s/\$it\{([^\}]*?)\}/$1/g; # turn $it{xxx} into xxx
+$x =~ s/\$bf\{([^\}]*?)\}/$1/g; # turn $bf{xxx} into xxx
+$x =~ s/\$rm\{([^\}]*?)\}/$1/g; # turn $rm{xxx} into xxx
+$x =~ s/\$cb\{([^\}]*?)\}/$1/g; # turn $cb{xxx} into xxx
+
+
+$x =~ s/\\\\([^\\]*?)\\\\/\U$1/g; # turn \\xxx\\ into XXX
+$x =~ s/\\\(([^)]*?)\)\\/$1/g; # turn \(xxx)\ into xxx
+$x =~ s/\\\"([^\"]*?)\"\\/$1/g; # turn \"xxx"\ into xxx
+$x =~ s/\\\%([^\%]*?)\%\\/"$1"/g; # turn \%xxx%\ into "xxx"
+
+$x =~ s/\\\?([^?]*?)\?\\/$1/g; # turn \?URL?\ into URL
+$x =~ s/<<([^>]*?)>>/<$1>/g; # turn <<xxx>> into <xxx>
+$x =~ s/\\\$([^\$]*?)\$\\/\$$1/g; # turn \$xxx$\ into $xxx
+$x =~ s/\\\-([^\\]*?)\-\\/\-$1/g; # turn \-xxx-\ into -xxx
+$x =~ s/\\\*\*([^*]*?)\*\*\\/$1/g; # turn \**xxx**\ into xxx
+$x =~ s/\[\(([\w\/]*)\)\]//g; # remove inline HTML
+
+$x =~ s/\\\*([^*]*?)\*\\/$1/g; # turn \*xxx*\ into xxx
+$x =~ s/\\([^\\]*?)\\/"$1"/g; # turn \xxx\ into "xxx"
+$x =~ s/\$\*\$/\*/g; # turn $*$ into *
+$x =~ s/\$t\b//g; # turn $t into nothing
+
+$x =~ s/::([^:]+)::/$1:/g; # turn ::xxx:: into xxx:
+
+# Put back escaped SGCAL specials
+
+$x =~ s/&&a/\@/g; # @@ => @
+$x =~ s/&&b/\\/g; # @\ => \
+$x =~ s/&&l/</g; # @< => <
+$x =~ s/&&g/>/g; # @> => >
+$x =~ s/&&c/\@{/g; # @{ => @{
+# $x =~ s/&&rc/{/g; #
+# $x =~ s/&&rd/}/g; #
+$x =~ s/&&d/\@}/g; # @} => @}
+$x =~ s/&&s/#/g; # @#
+
+# Remove any null flags ($$)
+
+$x =~ s/\$\$//g;
+
+$x;
+}
+
+
+##################################################
+# De-reference a paragraph #
+##################################################
+
+# Remove sentences or parenthetical comments that contain references.
+
+sub deref {
+my($t) = $_[0];
+
+$t =~ s/^(\n*)[^.()]+~~[^.]+\.\s*/$1/;
+$t =~ s/\s?\.[^.()]+~~[^.]+\././g;
+$t =~ s/\s?\([^~).]+~~[^)]+\)//g;
+
+$t;
+}
+
+
+##################################################
+# Quote what needs quoting #
+##################################################
+
+# This is for anything that must be quoted in the final output, independent
+# of whether it is in "asis" text or not.
+
+sub mustquote {
+my($t) = $_[0];
+$t =~ s/(?<!\\)-/\\-/g;
+
+$t;
+}
+
+
+
+##################################################
+# Main Program #
+##################################################
+
+open(IN, "spec.src") || die "Can't open spec.src\n";
+open(OUT, ">exim.8" ) || die "Can't open exim.8\n";
+
+print OUT <<End;
+.TH EXIM 8
+.SH NAME
+exim \\- a Mail Transfer Agent
+.SH SYNOPSIS
+.B exim [options] arguments ...
+.br
+.B mailq [options] arguments ...
+.br
+.B rsmtp [options] arguments ...
+.br
+.B rmail [options] arguments ...
+.br
+.B runq [options] arguments ...
+.br
+.B newaliases [options] arguments ...
+
+.SH DESCRIPTION
+Exim is a mail transfer agent (MTA) developed at the University of Cambridge.
+It is a large program with very many facilities. For a full specification, see
+the reference manual. This man page contains only a description of the command
+line options. It has been automatically generated from the reference manual
+source, which is why the formatting is poor in some places.
+
+.SH SETTING OPTIONS BY PROGRAM NAME
+.TP 10
+\\fBmailq\\fR
+Behave as if the option \\-bp were present before any other options. The \\-bp
+option requests a listing of the contents of the mail queue on the standard
+output.
+.TP
+\\fBrsmtp\\fR
+Behaves as if the option \\-bS were present before any other options, for
+compatibility with Smail. The \\-bS option is used for reading in a number of
+messages in batched SMTP format.
+.TP
+\\fBrmail\\fR
+Behave as if the \\-i and \\-oee options were present before any other options,
+for compatibility with Smail. The name \\fBrmail\\fR is used as an interface by
+some UUCP systems. The \\-i option specifies that a dot on a line by itself
+does not terminate a non\\-SMTP message; \\-oee requests that errors detected in
+non\\-SMTP messages be reported by emailing the sender.
+.TP
+\\fBrunq\\fR
+Behave as if the option \\-q were present before any other options, for
+compatibility with Smail. The \\-q option causes a single queue runner process
+to be started. It processes the queue once, then exits.
+.TP
+\\fBnewaliases\\fR
+Behave as if the option \\-bi were present before any other options, for
+compatibility with Sendmail. This option is used for rebuilding Sendmail's
+alias file. Exim does not have the concept of a single alias file, but can be
+configured to run a specified command if called with the \\-bi option.
+
+
+.SH OPTIONS
+.TP 10
+End
+
+while (<IN>) { last if /^\.startoptions/; }
+die "Can't find start of options\n" if ! defined $_;
+
+# Find the start of the first option
+
+while (<IN>) { last if /^\.option/; }
+die "Can't find start of first option\n" if ! defined $_;
+
+# Loop for each individual option
+
+while (/^\.option (.*)/)
+ {
+ $nlpending = 0;
+ $itemtext = "";
+
+ printf OUT ("\\fB\\-%s\\fR\n", &mustquote(&process($1)));
+
+ # Process the data for the option
+
+ while (<IN>)
+ {
+ last if /^\.(?:option|endoptions)/;
+ next if /^\.index/;
+ next if /^\.em\s*$/;
+ next if /^\.nem\s*$/;
+
+ if (/^\.display(?:\s+flow)?(?:\s+rm)?\s*$/)
+ {
+ print OUT &mustquote(&deref($itemtext));
+ $itemtext = "";
+ print OUT "\n";
+ while (($_ = <IN>) !~ /^\.endd/)
+ {
+ print OUT " ", &mustquote(&deref(&process($_))) if ! /^\./;
+ }
+ $nlpending = 1;
+ }
+
+ elsif (/^\.display asis\s*$/)
+ {
+ print OUT &mustquote(&deref($itemtext));
+ $itemtext = "";
+ print OUT "\n";
+ while (($_ = <IN>) !~ /^\.endd/)
+ {
+ print OUT &mustquote(" $_");
+ }
+ $nlpending = 1;
+ }
+
+ elsif (/^\s*$/)
+ {
+ print OUT &mustquote(&deref($itemtext));
+ $itemtext = "";
+ $nlpending++;
+ }
+
+ else
+ {
+ while ($nlpending > 0)
+ {
+ $itemtext .= "\n";
+ $nlpending--;
+ }
+ $itemtext .= &process($_);
+ }
+ }
+
+ print OUT &mustquote(&deref($itemtext));
+ print OUT ".TP\n";
+ }
+
+# End of g2man
diff --git a/doc/doc-scripts/g2t b/doc/doc-scripts/g2t
new file mode 100755
index 000000000..30c713c08
--- /dev/null
+++ b/doc/doc-scripts/g2t
@@ -0,0 +1,1347 @@
+#! /usr/bin/perl -w
+# $Cambridge: exim/doc/doc-scripts/g2t,v 1.1 2004/10/07 15:04:35 ph10 Exp $
+
+# A Perl script to turn the SGCAL source of the Exim documentation into
+# Texinfo input, more or less...
+
+# Supply the source file names as arguments.
+# The output goes to the standard output.
+
+
+##################################################
+# Ensure unique node name #
+##################################################
+
+# Node names must be unique. Occasionally in the Exim spec there are duplicate
+# section names, and it's become too much of a hassle to keep them distinct
+# manually. So it is now automated.
+
+########### Never really got this working. Abandoned ###############
+
+sub unique {
+my($node) = $_[0];
+if (defined $node_names{$node})
+ {
+ $node_names{$node} += 1;
+ $node = "$node ($node_names{$node})";
+
+print STDERR "+++ $node\n";
+
+ }
+else
+ {
+ $node_names{$node} = 0;
+ }
+$node;
+}
+
+
+
+##################################################
+# De-comma a node name #
+##################################################
+
+# Commas, colons, and apostrophes are not permitted in Texinfo
+# node names. I find this incredible, but it is clearly documented.
+# The Exim manual has been re-organized not to have colons or
+# apostrophes in any chapter or section titles, but I can't manage
+# without commas. This function turns "," into " and", which is
+# the best that can be done; we can use some clever Perlery to
+# just take out commas before "and".
+
+# Sigh. The Sendmail option -p<rval>:<sval> now means that there's a colon
+# in the node name for that option. Turn the colon into <colon>. This is also
+# done for menus.
+
+# Another thing that causes problems in node names in some versions of
+# Texinfo is the use of @sc{xxx} for small caps. Just turn these all into
+# real caps. This is also done for menus.
+
+sub decomma {
+$_[0] =~ s/,(?!\sand)/ and/g;
+$_[0] =~ s/,//g;
+$_[0] =~ s/\@sc\{([^}]*)\}/\U$1/g;
+$_[0] =~ s/:/<colon>/g;
+$_[0];
+}
+
+
+
+##################################################
+# De-quote a string #
+##################################################
+
+# @x is turned into x, except when x=@, or when asis is set,
+# in which case single @ must turn into @@. A single substitute
+# doesn't work in the non-asis case, because of the problems of
+# handling things like @@@$, so we do it the hard way.
+
+sub dequote {
+if ($asis) { $_[0] =~ s/@/@@/g; } else
+ {
+ $_[0] =~ s/@@/&at&/g;
+ $_[0] =~ s/@([^@])/$1/g;
+ $_[0] =~ s/&at&/@@/g;
+ }
+$_[0];
+}
+
+
+##################################################
+# Get next line #
+##################################################
+
+# Called from handle_directive, to get the next source line
+# into $_.
+
+sub get_next_line {
+if ($processing_subsection)
+ { return $_ = shift @SUBBUFFER; }
+else
+ { return $_ = <>; }
+}
+
+
+
+##################################################
+# Handle text lines #
+##################################################
+
+# This function is handed whole paragraphs, and we assume that
+# SGCAL font changing markup is always complete within a paragraph.
+# We have to replace escaped versions of significant characters with
+# some magic before performing general transformations, and then
+# put them back afterwards. The character & is not common in the text,
+# and && is unknown, so we use that.
+
+sub handle_text {
+$_ = $_[0];
+
+if ($asis)
+ {
+ $_ = dequote($_);
+ s/(\{|\})/\@$1/g;
+ return $_;
+ }
+
+while (/~~/)
+ {
+ $left = $`;
+ ($name) = $' =~ /^(\w+)/;
+ $right = $';
+
+ $value = $references{$name};
+ $value = "" if !defined($value);
+
+ if ($value =~ /\*\*\*\*/)
+ {
+ $value = ($` eq $current_chapter)? "\"$'\"" :
+ "\"$'\" in chapter \"$`\"";
+ $value = "" if $value eq "\"\"";
+ }
+ elsif ($value !~ /^[0-9]+\.[0-9]+$/) # quote unless version number
+ {
+ $value = "\"$value\"";
+ }
+
+ $_ = "${left}${value}${right}";
+ }
+
+s/\@\@/&&a/g; # @@
+s/\@\\/&&b/g; # @\
+s/\@</&&l/g; # @<
+s/\@>/&&g/g; # @>
+s/\@\{/&&c/g; # @{
+s/\@\}/&&d/g; # @}
+s/\@#/&&s/g; # @#
+
+# Now remove all other @'s
+
+$_ = dequote($_);
+
+# Convert SGCAL markup
+
+s/#/ /g; # turn # into a space
+s/\$~//g; # turn $~ into nothing
+s/__/_/g; # turn __ into _
+s/\$sm\{//g; # turn $sm{ into nothing
+s/\$sc\{([^\}]*?)\}/$1/g; # turn $sc{xxx} into xxx
+s/\$st\{([^\}]*?)\}/$1/g; # turn $st{xxx} into xxx
+s/\$si\{([^\}]*?)\}/$1/g; # turn $si{xxx} into xxx
+s/\$tt\{([^\}]*?)\}/$1/g; # turn $tt{xxx} into xxx
+
+s/\$it\{([^\}]*?)\}/$1/g; # turn $it{xxx} into xxx
+
+s/\$bf\{([^\}]*?)\}/$1/g; # turn $bf{xxx} into xxx
+s/\$rm\{([^\}]*?)\}/$1/g; # turn $rm{xxx} into xxx
+s/\$cb\{([^\}]*?)\}/$1/g; # turn $cb{xxx} into xxx
+
+# This is a fudge for some specific usages of $<; can't just do a global
+# is it occurs in things like $<variable name> as well.
+
+s/\[\$<\]/[]/g; # turn [$<] into []
+s/&&b\$<\./&&b./g; # turn \$<. into \. (\ == &&b by now)
+s/(\d)\$<-/$1-/g; # turn 0$<- into 0-
+
+# There is one case where the terminating } of an escape sequence is
+# in another paragraph - this follows $sm{ - it can be fixed by
+# removing any stray } in a paragraph that contains no { chars.
+
+s/\}//g if !/\{/;
+
+# Any remaining {} must be escaped to prevent Texinfo from complaining
+
+s/(\{|\})/\@$1/g;
+
+# Now to conversions that put {} into the file.
+# Change <<..>> from @var to just <...> as the caps that Texinfo
+# uses look far too shouty.
+
+s/\\\\([^\\]*?)\\\\/\@sc\{\L$1\}/g; # turn \\xxx\\ into @sc{xxx}
+s/\\\(([^)]*?)\)\\/\@file\{$1\}/g; # turn \(xxx)\ into @file{xxx}
+s/\\\"([^\"]*?)\"\\/\@file\{$1\}/g; # turn \"xxx"\ into @file{xxx}
+
+s/\\\?([^?]*?)\?\\/$1/g; # turn \?URL?\ into URL
+s/<<([^>]*?)>>/<$1>/g; # turn <<xxx>> into <xxx>
+s/\\\$([^\$]*?)\$\\/\$$1/g; # turn \$xxx$\ into $xxx
+s/\\\-([^-]*?)\-\\/\-$1/g; # turn \-xxx-\ into -xxx
+s/\\\*\*([^*]*?)\*\*\\/$1/g; # turn \**xxx**\ into xxx
+s/\[\(([\w\/]*)\)\]//g; # remove inline HTML
+
+s/\\\*([^*]*?)\*\\/\@dfn\{$1\}/g; # turn \*xxx*\ into @dfn{xxx}
+s/\\%([^*]*?)%\\/\@dfn\{$1\}/g; # turn \%xxx%\ into @dfn{xxx}
+s/:::([^:]*?)::/\@dfn\{:$1:\}/g; # turn :::xxx:: into @dfn{:xxx:}
+s/::([^:]*?)::/\@dfn\{$1:\}/g; # turn ::xxx:: into @dfn{xxx:}
+s/\\([^\\]*?)\\/\@dfn\{$1\}/g; # turn \xxx\ into @dfn{xxx}
+s/\$\*\$/\*/g; # turn $*$ into *
+
+# Put back escaped SGCAL specials
+
+s/&&a/\@\@/g;
+s/&&b/\\/g;
+s/&&l/</g;
+s/&&g/>/g;
+s/&&c/\@{/g;
+s/&&rc/{/g;
+s/&&rd/}/g;
+s/&&d/\@}/g;
+s/&&s/#/g;
+
+# Remove any null flags ($$)
+
+s/\$\$//g;
+
+# If the paragraph starts with $c\b, change this into @center. Assume
+# we don't ever get two of these in a row.
+
+s/^\$c\b/\@center /;
+
+# If the paragraph starts with $e\b, stuff some tabs in there, as
+# Texinfo can't do this on its own (as far as I can see). They must
+# tabs; Texinfo treats them as different to spaces. Sigh.
+
+s/^\$e\b/\t\t\t\t\t\t\t/;
+
+# Handle $t. The Exim spec only ever has one tab per line. Er, not
+# quite true, but a good enough assumption. $t is always followed
+# by a non-word character.
+
+# The .tabs directive has stashed the value in the $tab variable.
+# Don't count Texinfo font chars.
+
+while (/(^|.+?\n)(.+?)\$t(\W.*\n)/)
+ {
+ $before = $` . $1;
+ $after = $';
+ $left = $2;
+ $right = $3;
+
+ $left =~ s/\s$//;
+ $right =~ s/^\s+//;
+
+ $plainleft = $left;
+ $plainleft =~ s/\@[a-z]+\{([^}]+?)\}/$1/g;
+ $plainleft =~ s/\@//g;
+
+ $_ = $before . $left . (" " x ($tab - length($plainleft))) . $right . $after;
+
+ # Fudge for the one case where there are two tabs
+
+ if ($tab2 != 0)
+ {
+ $temp = $tab;
+ $tab = $tab2;
+ $tab2 = $temp;
+ }
+ }
+
+# Return the new line (paragraph)
+
+$_;
+}
+
+
+
+##################################################
+# Handle directive lines #
+##################################################
+
+# Use get_next_line() instead of <> because this is called to process
+# stacked up subsection lines
+
+sub handle_directive {
+
+my($new_lastwasitem) = 0;
+
+# Chapter directives just require . => @; however, dequoting the
+# line thereafter will remove the first @, so just force it back
+# afterwards. If the chapter is is one describing a driver, set
+# the driver name.
+
+if (/\.chapter/)
+ {
+ tr/./@/;
+ push(@ONESECTION, "@" . &dequote("$_\n"));
+ $driver_name = (/The\s+(\S+)\s+(director|router|transport|authenticator)/)? $1 :
+ (/Generic options common to both directors and routers/)?
+ "director or router" :
+ (/[Gg]eneric\s+options for (\S+)s/)? $1 : "";
+ $driver_name = &dequote($driver_name);
+ }
+
+# Section directives just require . => @; however, dequoting the
+# line thereafter will remove the first @, so just force it back
+# afterwards. Remove any colons in section titles as they cause
+# Texinfo trouble. Also remove any \\ (small caps) markup, which
+# appears in a couple of cases.
+
+elsif (/\.section/)
+ {
+ tr/./@/;
+ s/://;
+ s"\\\\""g;
+ push(@ONESECTION, "@" . &dequote("$_\n"));
+
+ # Horrible magic fudge to cope with the fact that exim_lock has
+ # -v and -q options, just like the main program.
+
+ $driver_name = "exim_lock" if /Mailbox maintenance/;
+
+ # Similar magic for exiqgrep, which also duplicates options
+
+ $driver_name = "exiqgrep" if /Selective queue listing/;
+ }
+
+# .newline must put @* on the end of the previous line, if any, except
+# inside a display, where it causes trouble.
+
+elsif (/\.newline/)
+ {
+ if (@ONESECTION > 0 && ! $indisplay)
+ {
+ $_ = pop(@ONESECTION);
+ s/(\n*)$/\@*$1/;
+ push(@ONESECTION, $_);
+ }
+ }
+
+# .blank turns into @sp, adding 1 if no argument
+
+elsif (/\.blank/)
+ {
+ s/\.blank\s+(\d+)/\@sp $1/;
+ s/\.blank/\@sp 1/;
+ push(@ONESECTION, $_);
+ }
+
+# .rule turns into a line of hyphens
+
+elsif (/\.rule/)
+ {
+ push(@ONESECTION, ("-" x ($in_itemize? 68 : 73)) . "\@*\n");
+ }
+
+# There's one explicit .tabset setting for two tab stops
+
+elsif (/\.tabset\s*/)
+ {
+ $rest = $';
+ ($first,$second) = $rest =~ /(\d+)em\s+(\d+)em/;
+ $tab = ($first * 7)/6;
+ $tab2 = $tab + ($second * 7)/6;
+ }
+
+# .tabs remembers the first (and only) tab setting
+
+elsif (/\.tabs\s*/)
+ {
+ $tab = ($' * 7)/6;
+ $tab2 = 0;
+ }
+
+# .tempindent is used only to align some of the expansion stuff nicely;
+# just ignore it. It is used in conjunction with .push/.pop.
+
+elsif (/\.(tempindent|push|pop)\s*/)
+ {
+ }
+
+# There are some instances of .if ~~sys.fancy in the source. Some of these
+# are two-part things, in which case we just keep the non-fancy. For diagrams,
+# however, they are in three parts:
+#
+# .if ~~sys.fancy
+# <aspic drawing stuff>
+# .elif ~~nothtml
+# <ascii art for txt and Texinfo>
+# .else
+# <HTML instructions for including a gif>
+# .fi
+
+elsif (/\.if \~\~sys\.fancy/)
+ {
+ while (&get_next_line())
+ { last if /\.else\b/ || /\.elif\s+\~\~nothtml/ || /\.fi\b/; }
+
+ if (/\.elif/)
+ {
+ $skip_else = 1;
+ }
+ }
+
+# There are occasional requirements to do things differently for
+# Texinfo/HTML and the PS/txt versions, and there are also some
+# HTML-specific things.
+
+elsif (/\.if\s+~~sgcal/ || /\.if\s+~~html/)
+ {
+ while (&get_next_line()) { last if /\.else\b/ || /\.fi\b/; }
+ }
+
+# We may also have Texinfo-specific bits
+
+elsif (/^\.if\s+~~texinfo/)
+ {
+ $skip_else = 1;
+ }
+
+# Ignore any other .if directives
+
+elsif (/\.if/) {}
+
+# Skip else part if flag set
+
+elsif (/\.else/ && $skip_else)
+ {
+ while (&get_next_line()) { last if /\.fi\b/; }
+ $skip_else = 0;
+ }
+
+# Ignore other .fi and .else as any .if directives are handled specially
+
+elsif (/\.fi/ || /\.else/) {}
+
+# Ignore .indent
+
+elsif (/\.indent/) {}
+
+# Plain .index goes to @cindex - the "concept" index. Also, there
+# are some calls to vindex and findex in the SGCAL source - treated
+# as synonymous with .index - which are split into the equivalent
+# indexes here.
+
+elsif (/\.(.?)index/)
+ {
+ $rest = $';
+ $letter = ($1 eq "")? "c" : $1;
+ tr/./@/; # .index -> @index
+
+ $rest =~ s/\\\(//g; # Remove markup
+ $rest =~ s/\)\\//g;
+ $rest =~ s/\\%//g;
+ $rest =~ s/%\\//g;
+ $rest =~ s/\\\*//g;
+ $rest =~ s/\*\\//g;
+ $rest =~ s/\\"//g;
+ $rest =~ s/"\\//g;
+ $rest =~ s/:://g;
+ $rest =~ s/\\-/-/g;
+ $rest =~ s/-\\//g;
+ $rest =~ s/~~//g;
+
+ $rest =~ tr/\\//d; # Remove \
+
+ $rest =~ s/\@\$/\$/g; # @$ -> $
+ $rest =~ s/\@_/_/g; # @_ -> _
+ $rest =~ s/\@\+/+/g; # @+ -> +
+ $rest =~ s/\$\*\$/\*/g; # $*$ -> *
+ $rest =~ s/\$([^\$]+)\$/\$$1/g; # $x$ -> $x
+
+ $rest =~ s/^\s+//; # Remove leading spaces
+ $rest =~ s/\s+$//; # Remove trailing spaces
+ $rest =~ s/\|\|/:/; # || -> :
+ push(@ONESECTION, "\@${letter}index $rest\n");
+
+ # Duplicate entries for things that were listed as "x see y"
+
+ if (defined $indirections{$rest})
+ {
+ push(@ONESECTION, "\@${letter}index $indirections{$rest}\n");
+ }
+ }
+
+# Various flavours of numberpars map to itemize and enumerate.
+# Haven't found a way of having a blank space 'bullet' yet, so
+# currently using minus.
+
+elsif (/\.numberpars/)
+ {
+ $rest = $';
+ $type = "enumerate";
+ $flag = "";
+
+ if ($rest =~ /\$\./) { $flag = " \@bullet"; $type = "itemize" }
+ elsif ($rest =~ /\" \"/) { $flag = " \@minus"; $type = "itemize"; }
+ elsif ($rest =~ /roman/) { $flag = " a"; $type = "enumerate"; }
+
+ push(@ONESECTION, "\n\@$type$flag\n\n\@item\n");
+ push(@ENDLIST, $type);
+ $in_itemize++;
+ }
+
+elsif (/\.nextp/)
+ {
+ push(@ONESECTION, "\n\@item\n");
+ }
+
+elsif (/\.endp/)
+ {
+ $endname = pop(@ENDLIST);
+ push(@ONESECTION, "\@end $endname\n\n");
+ $in_itemize--;
+ }
+
+# The normal .display (typewriter font) => @example, while the rm
+# form goes to @display (no change of font). For Texinfo we need a
+# blank line after @display.
+
+elsif (/\.display/)
+ {
+ $type = /rm/? "display" : "example";
+ $asis = 1 if /asis/;
+ $indisplay = 1;
+ push(@ONESECTION, "\@$type\n\n");
+ push(@ENDLIST, $type);
+ }
+
+elsif (/\.endd/)
+ {
+ $asis = 0;
+ $indisplay = 0;
+ $endname = pop(@ENDLIST);
+ push(@ONESECTION, "\@end $endname\n\n");
+ }
+
+elsif (/\.conf/)
+ {
+ ($option, $type, $default) =
+ /\.conf\s+(\S+)\s+("(?:[^"]|"")+"|\S+)\s+("(?:[^"]|"")+"|.*)/;
+
+ $option = &dequote($option);
+
+ # If $type ends with $**$ (turned into a dagger for PS version),
+ # replace with ", expanded". Remove any surrounding quotes.
+
+ $type =~ s/^"([^"]+)"/$1/;
+ $type =~ s/\$\*\*\$/, expanded/;
+
+ # Default may be quoted, and it may also have quotes that are required,
+ # if it is a string.
+
+ $default =~ s/^"(.*)"$/$1/;
+ $default =~ s/""/"/g;
+ $default = &handle_text($default);
+
+ push(@ONESECTION, "\nType: $type\@*\nDefault: $default\n\n");
+ }
+
+# Handle .startitems, .enditems, and .item
+
+elsif (/\.startitem/ || /\.enditem/) {}
+
+elsif (/\.item/)
+ {
+ $arg = $';
+ $arg =~ s/^\s*"//;
+ $arg =~ s/"\s*$//;
+ $arg = &dequote($arg);
+ $arg = &handle_text("\\**$arg**\\");
+
+ # If there are two .items in a row, we don't want to put in the
+ # separator line.
+
+# push(@ONESECTION, "\n\@example\n");
+ push(@ONESECTION, "\n");
+ if (! $lastwasitem)
+ {
+ push(@ONESECTION, "_" x 75, "\n\n");
+ }
+# push(@ONESECTION, "$arg\n\@end example\n\n");
+ push(@ONESECTION, "$arg\n\n");
+ $new_lastwasitem = 1;
+ }
+
+elsif (/\.option/)
+ {
+ chomp($arg = $');
+ $arg =~ s/^\s*//;
+ $arg = &dequote("-$arg");
+ $arg = &handle_text($arg);
+ }
+
+# Texinfo has no facility for emphasis bars.
+
+elsif (/\.em/) {}
+elsif (/\.nem/) {}
+
+# Just ignore any .(r)set directives pro tem (or maybe always!)
+
+elsif (/\.r?set/) {}
+
+# Ignore .space, .linelength, and .justify
+
+elsif (/\.space/ || /\.justify/ || /\.linelength/) {}
+
+# Found an SGCAL directive that isn't dealt with. Oh dear.
+# Turn the embarrassing characters into question marks and
+# output it in an emphasized way.
+
+else
+ {
+ tr/@{}/???/;
+ push(@ONESECTION, "\n\>>>>>>> $_\n") if ! /^\.( |$)/;
+ }
+
+$lastwasitem = $new_lastwasitem;
+}
+
+
+
+##################################################
+# Flush a section #
+##################################################
+
+# $section_name is the name of the next section.
+# $current_section is the name of the one we have buffered up.
+# If it is unset, we are at the first section of a chapter.
+# $previous_node is the section we last flushed if it was a node.
+
+sub flush_section {
+
+# If there is no text in the section, omit it entirely. However, it
+# will have had a pointer set up at the start of the previous section.
+# Remember what to replace this with when the chapter gets flushed.
+
+my($skip) = 1;
+foreach $s (@ONESECTION)
+ {
+ if ($s !~ /^(\@cindex|\@section|\s*$)/) { $skip = 0; last }
+ }
+
+if ($skip)
+ {
+ pop @section_list;
+ $rewrite{$current_section} = $section_name;
+ @ONESECTION = ();
+ return;
+ }
+
+# There is data in the section: write it out to the chapter file
+
+if ($current_section)
+ {
+ printf ONECHAPTER ("\@node %s, %s, %s, %s\n",
+ &decomma($current_section), &decomma($section_name),
+ &decomma($previous_node), &decomma($current_up));
+ $previous_node = $current_section;
+ while(scalar(@ONESECTION))
+ { print ONECHAPTER shift(@ONESECTION); }
+ }
+else
+ {
+ while(scalar(@ONESECTION))
+ { push(@TOPSECTION, shift(@ONESECTION)); }
+ }
+}
+
+
+
+##################################################
+# Handle a "subsection" #
+##################################################
+
+# A "subsection" is a set of options that must have their own
+# local menu. Do two passes; the first just collects the names
+# for the menu. This is called for .conf and .option items.
+
+sub handle_subsection{
+my($type) = $_[0];
+my($save_up) = $current_up;
+
+$current_up = $current_section? $current_section : $current_chapter;
+
+@sublist = ();
+@SUBBUFFER = ();
+
+while (<>)
+ {
+ last if /^\.end$type/;
+ push(@SUBBUFFER, $_);
+
+ # .conf takes the first non-space string as the name, but as there are
+ # duplicate confs in various parts of the spec, use the driver name to
+ # de-duplicate; .option takes the entire rest of arg as the name, but
+ # removes any sequence of ... because this disturbs TexInfo. Also, it
+ # turns @- into -.
+
+ if (/^\.$type\s+(\S+)(.*)/)
+ {
+ if ($type eq "conf")
+ {
+ $name = &handle_text($1);
+ $name .= " ($driver_name)" if ($driver_name ne "");
+ }
+ else
+ {
+ chomp($name = &handle_text("-$1$2"));
+ $name =~ s/\s*\.\.\.//g;
+
+ $name .= " ($driver_name)" if ($driver_name ne "");
+
+ # There seems to be a major problem in texinfo with the string "--".
+ # In the text it gets turned into a single hyphen. This happens if it
+ # is used as a menu item, but *not* as a node name. Exim has a command
+ # line option "--". With no special action, this appears in the menu
+ # as "-", but then the info software complains there is no node called
+ # "-". If we triple it in the menu it gets displayed OK, but building
+ # software complains about non-existent cross references etc.
+
+ # I have gone for the horrid kludge of turning it into "-<hyhen>"
+ # in the menus and nodes.
+
+ # Exim 4 has added --help, which has the same problem.
+
+ $name = "-<hyphen>" if ($name eq "--");
+ $name = "-<hyphen>help" if ($name eq "--help");
+ }
+ push(@sublist, $name);
+ }
+ }
+
+push (@ONESECTION, "\n\@sp 2\n\@menu\n");
+for ($i = 0; $i < scalar(@sublist); $i++)
+ {
+ $mitem = $sublist[$i];
+ $mitem =~ s/\@sc\{([^}]*)\}/\U$1/g; # Get rid of small caps
+ $mitem =~ s/:/<colon>/g; # Get rid of colons
+ push (@ONESECTION, "* ${mitem}::\n");
+ }
+push (@ONESECTION, "\@end menu\n\n");
+
+$prevsub = $current_up;
+$processing_subsection = 1;
+while ($_ = shift(@SUBBUFFER))
+ {
+ if (/^\.$type\s+(\S+)/)
+ {
+ $name = shift @sublist;
+ $next = (scalar(@sublist))? $sublist[0] : "";
+ push @ONESECTION, sprintf("\@node %s, %s, %s, %s\n",
+ &decomma($name), &decomma($next), &decomma($prevsub),
+ &decomma($current_up));
+
+ if ($name eq "-<hyphen>") # Fudge for Texinfo
+ {
+ push(@ONESECTION,
+ "\@findex $name\n",
+ "\@unnumberedsubsec --- option\n");
+ push(@ONESECTION,
+ "This option consists of two consecutive hyphens. It appears in\n",
+ "the menu as \"-<hyphen>\" because otherwise Texinfo gets\n",
+ "confused with its cross-referencing.\n");
+ }
+ elsif ($name eq "-<hyphen>help") # Fudge for Texinfo
+ {
+ push(@ONESECTION,
+ "\@findex $name\n",
+ "\@unnumberedsubsec ---help option\n");
+ push(@ONESECTION,
+ "This option consists of \"help\" preceded by two consecutive\n" .
+ "hyphens. It appears in the menu as \"-<hyphen>help\" because\n" .
+ "otherwise Texinfo gets confused with its cross-referencing.\n");
+ }
+ else
+ {
+ push(@ONESECTION,
+ "\@findex $name\n",
+ "\@unnumberedsubsec $name option\n");
+ }
+
+ $prevsub = $name;
+ }
+
+ # Then handle as text or directive
+
+ if (substr($_, 0, 1) eq ".")
+ { handle_directive(); }
+ else
+ {
+ while($nextline = shift(@SUBBUFFER))
+ {
+ last if $nextline =~ /^(\.|\s*$)/;
+ $_ .= $nextline;
+ }
+ push(@ONESECTION, handle_text($_));
+ $_ = $nextline;
+ last if !defined($_);
+ redo;
+ }
+ }
+
+$processing_subsection = 0;
+$section_pending = 1;
+$current_up = $save_up;
+}
+
+
+
+
+##################################################
+# Handle a single chapter #
+##################################################
+
+sub handle_chapter{
+chop;
+($current_chapter) = /^\.chapter (.*)/;
+$current_chapter = &dequote($current_chapter);
+
+$current_chapter = $current_chapter;
+
+my($tmp) = $current_chapter;
+$tmp =~ s/\[\[\[\]\]\]/./;
+print STDERR "processing chapter: $tmp\n";
+
+# Remember the chapter name for the top-level menu
+
+push(@chapter_list, $current_chapter);
+
+# Open a temporary file to hold the chapter's text while collecting
+# all its sections for a chapter-level menu.
+
+$ONECHAPTER = "/tmp/ONECHAPTER.$$";
+open(ONECHAPTER, ">$ONECHAPTER") || die "Can't open $ONECHAPTER for writing";
+
+# Initialize for handling sections
+
+@section_list = ();
+%rewrite = ();
+@ONESECTION = ();
+@TOPSECTION = ();
+undef $current_section;
+undef $next_node;
+
+$processing_subsection = 0;
+
+$previous_node = $current_up = $current_chapter;
+$section_pending = 0;
+
+# Handle the .chapter directive as the first text of a section without
+# a section title.
+
+handle_directive();
+
+# Loop, handling each section. Assume they are sufficiently short that
+# we can buffer the text in store, in an array called ONESECTION, instead
+# of thrashing yet another file.
+
+while (<>)
+ {
+ last if /^\.chapter /;
+
+ # Handle a new section, preserving $_ (handle_text flattens it).
+ # It seems we cannot get a fullstop into a Texinfo node name; use a magic
+ # character string that gets turned back into a dot by the post-processing.
+
+ if (/^\.section\s+/)
+ {
+ $save = $_;
+ $section_name = $';
+ $section_name =~ s/(\s|\n)+$//;
+ $section_name =~ s/://;
+ $section_name = &handle_text($section_name);
+ flush_section();
+ push(@section_list, $section_name);
+ $current_section = $section_name;
+ $next_node = $section_name if !$next_node;
+ $section_pending = 0;
+ $_ = $save;
+ }
+
+ # The .startconf macro introduces a set of .conf's which must have
+ # their own local set of menus. Suspend processing the section while
+ # we sort out the menu and copy their data. This is all done in a
+ # subroutine that is shared with options.
+
+ elsif (/^\.startconf/)
+ {
+ handle_subsection("conf");
+ next;
+ }
+
+ elsif (/^\.startoption/)
+ {
+ handle_subsection("option");
+ next;
+ }
+
+ # Deal with the actual data lines; if there's a section pending
+ # start a new section on hitting some text. We hope this happens
+ # only once per chapter...
+
+ if (substr($_, 0, 1) eq ".")
+ {
+ handle_directive();
+ }
+ else
+ {
+ while($nextline = <>)
+ {
+ last if $nextline =~ /^(\.|\s*$)/;
+ $_ .= $nextline;
+ }
+ if ($section_pending && !/^\s*$/)
+ {
+ $section_name = (defined $current_section)?
+ "$current_section (continued)" :
+ "$current_chapter (continued)" ;
+ flush_section();
+ push(@section_list, $section_name);
+ $current_section = $section_name;
+ $next_node = $section_name if !$next_node;
+ $section_pending = 0;
+ }
+
+ push(@ONESECTION, handle_text($_));
+ $_ = $nextline;
+ last if !defined($_);
+ redo;
+ }
+ }
+
+# Flush any pending text, making its next field null.
+# and fudging section_name for the final section of the previous.
+
+$section_name = "";
+flush_section();
+
+# Set up section name as the start of the next chapter
+
+$section_name = "Concept Index" if (!$doing_filter);
+
+if (defined $_ && /^\.chapter (.*)/)
+ {
+ $section_name = $1;
+ $section_name = &dequote($section_name);
+ }
+$next_node = $section_name;
+
+# Write out the chapter to the CHAPTERS file, sticking the chapter
+# menu after the text that came before the first section heading. This
+# will always at least contain the chapter title.
+
+printf CHAPTERS ("\@node %s, %s, %s, Top\n",
+ &decomma($current_chapter), &decomma($next_node),
+ &decomma($previous_chapter));
+
+# The pre-section stuff; if we hit an @end menu line, it is the menu of
+# a "subsection" before the first section. In that case, we need to put
+# the chapter's menu one the end of it, and then resume with the rest of
+# the TOPSECTION data afterwards. We also need to thread together this
+# "subsection"s nodes because they are all at the same level under the
+# chapter.
+
+$in_menu = 0;
+while(scalar(@TOPSECTION))
+ {
+ $s = shift(@TOPSECTION);
+ if ($s =~ /^\@end menu/)
+ {
+ $in_menu = 1;
+ last;
+ }
+ print CHAPTERS $s;
+ }
+
+# Menu for sections
+
+undef $next_actual_section;
+undef $point_back;
+
+if (scalar(@section_list))
+ {
+ print CHAPTERS "\n\@sp 2\n\@menu\n" if ! $in_menu;
+ $next_actual_section = $section_list[0];
+ for ($i = 0; $i < scalar(@section_list); $i++)
+ {
+ $section_name = $section_list[$i];
+ $section_name =~ s/\@sc\{([^}]*)\}/\U$1/g;
+ print CHAPTERS "* ${section_name}::\n";
+ }
+ $in_menu = 1;
+ }
+print CHAPTERS "\@end menu\n\n" if $in_menu;
+
+# Remainder of topsection; we must arrange that the final @node in
+# it (which will have a blank "next" field) actually points on to
+# the next section, if any. If this happens, then the next section
+# must point back to the final @node.
+
+while(scalar(@TOPSECTION))
+ {
+ $s = shift(@TOPSECTION);
+ if ($next_actual_section && $s =~
+ /^\@node\s+([^,]+),\s*,\s*([^,]*),\s*(.*)/)
+ {
+ my($t1, $t2, $t3) = ($1, $2, $3); # So can be decomma'd
+ printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1),
+ &decomma($next_actual_section), &decomma($t2), &decomma($t3));
+ $point_back = $1;
+ }
+ else { print CHAPTERS $s; }
+ }
+
+close(ONECHAPTER);
+open(ONECHAPTER, "$ONECHAPTER") || die "Can't open $ONECHAPTER for reading";
+
+# While copying the chapter data, check for node references to empty
+# sections that got omitted and correct them, and correct the prev pointer
+# in the first node if necessary.
+
+while ($buff = <ONECHAPTER>)
+ {
+ foreach $key (keys %rewrite)
+ {
+ $buff =~ s/$key/$rewrite{$key}/;
+ }
+ if ($point_back && $buff =~ /^\@node\s+([^,]+),\s*([^,]*),\s*([^,]*),\s*(.*)/)
+ {
+ my($t1, $t2, $t4) = ($1, $2, $4); # so can be decomma'd
+ printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1),
+ &decomma($t2), &decomma($point_back), &decomma($t4));
+ undef $point_back;
+ }
+ else { print CHAPTERS $buff; }
+ }
+
+$previous_chapter = $current_chapter;
+
+close(ONECHAPTER);
+unlink($ONECHAPTER);
+}
+
+
+
+##################################################
+# Main Program #
+##################################################
+
+# This is a two-pass algorithm. The first pass goes through and gets the
+# variable names for cross references. The second pass does the real work,
+# but we can't just read through doing the translation in one pass. We need
+# to know the list of chapters in order to build a top-level menu, and for
+# each chapter we need to know the sections in order to build a section
+# menu. Consequently, make use of temporary files to buffer things.
+
+# This script is used for the filter document and the overview as well;
+# flags tell it if it is doing one of them.
+
+$doing_filter = 0;
+$skip_else = 0;
+$in_itemize = 0;
+$lastwasitem = 0;
+
+$chapter_number = 0;
+$section_number = 0;
+
+if ($#ARGV >= 0 && $ARGV[0] eq "-filter")
+ {
+ $doing_filter = 1;
+ shift @ARGV;
+ }
+
+# First pass: Just fish out variable settings. Save the arguments so that
+# they can be reinstated for a second pass.
+
+print STDERR "Scanning for references\n";
+@save_argv = @ARGV;
+
+# Pick up any .set directives right at the very start
+
+while (<>)
+ {
+ last if ! /^\.set\s+(\S+)\s+(.+)$/;
+ $name = $1;
+ $value = $2;
+ $value =~ s/^\"?(.*?)\"?\s*$/$1/;
+ $references{$name} = $value;
+ }
+
+# Now skip everything before the first .chapter except for
+# .index lines that set up indirections. Save these so that
+# the relevant index entries can be duplicated.
+
+while (<>)
+ {
+ if (/^\.chapter\s+(.+)$/)
+ {
+ $chapter_number++;
+ $section_number = 0;
+ $current_chapter = $1;
+ $current_chapter = $current_chapter;
+ $current_section = "";
+ last;
+ }
+
+ if (/^\.index\s+([^\$]+)\s+\$it\{see\s+([^}]+)\}\s*$/)
+ {
+ $indirections{"$2"} = $1;
+ }
+ }
+
+# Do the business
+
+while (<>)
+ {
+ if (/^\.chapter\s+(.+)$/)
+ {
+ $current_chapter = $1;
+ $current_chapter = &dequote($current_chapter);
+ $current_section = "";
+ }
+ elsif (/^\.section\s+(.+)$/)
+ {
+ $current_section = $1;
+ $current_section = &dequote($current_section);
+ $current_section =~ s/://;
+ }
+ elsif (/^\.r?set\s+(\S+)\s+(.+)$/ && $1 ne "runningfoot")
+ {
+ $name = $1;
+ $value = $2;
+
+ # Only set the first time. This handles a few special cases in part2
+ # which is included in the filter text as well.
+
+ if (!exists($references{$name}))
+ {
+ $value =~ s/^\"?(.*?)\"?\s*$/$1/;
+ $value =~ s/~~chapter\./~~chapter****/;
+ $value =~ s/~~chapter/$current_chapter/;
+ $value =~ s/~~section/$current_section/;
+ $references{$name} = $value;
+ }
+ }
+ }
+
+$final_chapter = defined($current_chapter)? $current_chapter : "";
+
+# Reinstate ARGV with the list of files and proceed to the main pass
+
+@ARGV = @save_argv;
+
+# $asis is set true when processing .display asis blocks, to stop
+# characters getting interpreted.
+
+$asis = 0;
+
+# $indisplay is set true while processing .display blocks, to stop
+# .newlines being handled therein (adding @* wrecks alignment)
+
+$indisplay = 0;
+
+# $tab is set to the value of the tab stop - only one stop is ever used
+# in the Exim source.
+
+$tab = 0;
+
+# Current driver name, for disambiguating nodes
+
+$driver_name = "";
+
+# $section_pending is set if a new section is to be started on hitting
+# any data lines.
+
+$section_pending = 0;
+
+# Open a file to buffer up the entire set of chapters
+
+$CHAPTERS = "/tmp/CHAPTERS.$$";
+open(CHAPTERS, ">$CHAPTERS") || die "Can't open $CHAPTERS for writing";
+
+# Skip everything before the first .chapter
+
+while (<>) { last if /^\.chapter /; }
+
+# Loop, handling each chapter
+
+$current_up = "";
+$previous_chapter = "Top";
+$previous_node = "Top";
+
+$chapter_number = 0;
+$section_number = 0;
+
+while (defined ($_) && /^\.chapter /)
+ {
+ handle_chapter();
+ }
+
+# Output the stuff at the start of the file
+
+print "\\input texinfo\n";
+
+print "\@set{wmYear} 2003\n";
+print "\@set{wmAuthor} Philip Hazel\n";
+print "\@set{wmAuthor_email} <ph10\@\@cus.cam.ac.uk>\n";
+print "\@set{COPYRIGHT1} Copyright \@copyright{} \@value{wmYear} University of Cambridge\n";
+
+print "\@c %**start of header\n";
+
+if (!$doing_filter)
+ {
+ print "\@setfilename spec.info\n";
+ print "\@settitle Exim Specification\n";
+ }
+else
+ {
+ print "\@setfilename filter.info\n";
+ print "\@settitle Exim Filter Specification\n";
+ }
+
+print "\@paragraphindent 0\n";
+print "\@c %**end of header\n\n";
+
+
+print "\@titlepage\n";
+print "\@title The Exim Mail Transfer Agent\n";
+print "\@author \@value{wmAuthor}\n";
+
+print "\@page\n";
+print "\@vskip 0pt plus 1filll\n";
+
+print "Permission is granted to make and distribute verbatim copies of this manual provided the\n";
+print "copyright notice and this permission notice are preserved on all copies.\n";
+
+print "\@sp2\n";
+print "\@value{COPYRIGHT1}\@*\n";
+
+print "\@end titlepage\n\n";
+
+# Output the top-level node and its introductory blurb
+
+print "\@node Top, $chapter_list[0], (dir), (dir)\n";
+print "\@top\n";
+
+if (!$doing_filter)
+{
+print <<End;
+The Exim Mail Transfer Agent\@*
+****************************
+
+The specification of the Exim Mail Transfer Agent is converted mechanically
+into Texinfo format from its original marked-up source. Some typographic
+representations are changed, chapters and sections cannot be numbered, and
+Texinfo lacks the ability to mark updated parts of the specification with
+change bars.
+
+Because the chapters and sections are unnumbered, cross references are set to
+their names. This makes the English a bit odd, with phrases like \`see chapter
+\"Retry configuration\"\' but it seemed very cumbersome to change this to \`see
+the chapter entitled \"Retry configuration\"\' each time.
+
+Each chapter, section, and configuration option has been placed in a separate
+Texinfo node. Texinfo doesn\'t allow commas, colons, or apostrophes in node
+names, which is a rather nasty restriction. I have arranged not to use colons
+or apostrophes in section titles, but cannot bring myself to omit them from
+titles such as \"The foo, bar and baz commands\". For the corresponding node
+names I have just used multiple occurrences of \"and\", though it looks very
+ugly.
+
+If a chapter or section continues after a list of configuration options that is
+not in a new section, a new node is started, using the chapter\'s or section\'s
+name plus \`(continued)\'. The \`Up\' operation from a section or configuration
+option returns to the start of the current chapter; the \`Up\' operation at a
+chapter start returns to the top of the document; the \`Up\' in a list of
+configuration options within a section returns to the top of that section.
+
+A number of drivers have options with the same name, so they have been
+disambiguated by adding the name of the driver to its option names in order to
+create node names. Thus, for example, the specification of the \`command\'
+options of the \`lmtp\' and \`pipe\' transports are in nodes called \`command
+(lmtp)\' and \`command (pipe)\', respectively.
+
+End
+}
+
+else
+{
+print <<End;
+Filtering with the Exim Mail Transfer Agent\@*
+*******************************************
+
+The specifications of the Exim Mail Transfer Agent\'s filtering facility is
+converted mechanically into Texinfo format from its original marked-up source.
+Some typographic representations are changed, chapters and sections cannot be
+numbered, and Texinfo lacks the ability to mark updated parts of the
+specification with change bars.
+
+Because the chapters and sections are unnumbered, cross references are set to
+their names. This makes the English a bit odd, with phrases like \`see section
+\"Multiple personal mailboxes\"\' but it seemed very cumbersome to change this to
+\`see the section entitled \"Multiple personal mailboxes\"\' each time.
+
+End
+}
+
+# Output the top-level menu
+
+print "\@menu\n";
+
+while (scalar(@chapter_list))
+ {
+ $name = &decomma(shift(@chapter_list));
+ print "* ${name}::\n";
+ }
+print "* Concept Index::\n" if (!$doing_filter);
+print "\@end menu\n\n";
+
+# Copy the chapters, then delete the temporary file
+
+close(CHAPTERS);
+open(CHAPTERS, "$CHAPTERS") || die "Can't open $CHAPTERS for reading";
+print $buff while($buff = <CHAPTERS>);
+close(CHAPTERS);
+unlink($CHAPTERS);
+
+# Output the finishing off stuff
+
+if (!$doing_filter)
+ {
+ print "\@node Concept Index, , $final_chapter, Top\n";
+ print "\@chapter Concept Index\n\@printindex cp\n";
+ print "\@chapter Function Index\n\@printindex fn\n";
+ }
+print "\@contents\n";
+print "\@bye\n";
+
+# End