diff options
Diffstat (limited to 'doc/doc-scripts')
-rwxr-xr-x | doc/doc-scripts/BuildFAQ | 59 | ||||
-rwxr-xr-x | doc/doc-scripts/BuildHTML | 12 | ||||
-rwxr-xr-x | doc/doc-scripts/BuildInfo | 32 | ||||
-rwxr-xr-x | doc/doc-scripts/BuildPDF | 10 | ||||
-rwxr-xr-x | doc/doc-scripts/DoConts | 71 | ||||
-rwxr-xr-x | doc/doc-scripts/DoIndex | 430 | ||||
-rwxr-xr-x | doc/doc-scripts/JoinPS | 130 | ||||
-rw-r--r-- | doc/doc-scripts/Makefile | 31 | ||||
-rwxr-xr-x | doc/doc-scripts/f2h | 338 | ||||
-rwxr-xr-x | doc/doc-scripts/f2txt | 107 | ||||
-rwxr-xr-x | doc/doc-scripts/faqchk | 102 | ||||
-rwxr-xr-x | doc/doc-scripts/fc2k | 344 | ||||
-rwxr-xr-x | doc/doc-scripts/g2h | 1451 | ||||
-rwxr-xr-x | doc/doc-scripts/g2man | 251 | ||||
-rwxr-xr-x | doc/doc-scripts/g2t | 1347 |
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/</</g; # Deal with < and > +$s =~ s/>/>/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/\\#/ /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/``(.*?)''/“$1”/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 /; # 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/^: /: /; + $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>", " " x 4, "</td>\n"; + print OUT " <td> $seclist[$i]</td>\n"; + print OUT " <td>", " " 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> \n"; + if ($sec > 0) + { + printf OUT ("<a href=\"FAQ_%d.html\">Previous</a> \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> \n"; + if ($sec > 0) + { + printf OUT ("<a href=\"FAQ_%d.html\">Previous</a> \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: /; + + 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> \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 "; + +foreach $initial (sort keys %initials) + { + if ($initial eq $this_initial) + { + print OUT " <font size=7 color=\"#FF0A0A\"><b>$initial</b></font> "; + } + else + { + print OUT "<a href=\"FAQ-KWIC_$initial.html\"> $initial</a>"; + } + } + +print OUT " "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>:(?: )*)(.*)<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> \n", + $thischapter - 1) if $thischapter > 1; + printf OUT ("<a href=\"${file_base}_%s.html\">Next</a> \n", + $thischapter + 1) if $thischapter < $maxchapter; + print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n"; + print OUT " " 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> \n", + $thissection - 1) if $thissection > 1; + printf OUT ("<a href=\"${file_base}_%s.html\">Next</a> \n", + $thissection + 1) if $thissection < $maxsection; + print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n"; + print OUT " " 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> ", + $thischapter - 2) if ($thischapter > 2); + print OUT "<a href=\"${file_base}_$thischapter.html\">Next</a> "; + print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n"; + print OUT " " 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> ", + $thissection - 2) if ($thissection > 2); + print OUT "<a href=\"${file_base}_$thissection.html\">Next</a> "; + print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n"; + print OUT " " 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> ", + $thischapter - 1) if ($thischapter > 1); + print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n"; + print OUT " " x 6, "($doctitle)\n</font>\n"; + } + elsif ($sectsplit) + { + print OUT "<font size=2>\n"; + printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a> ", + $thissection - 1) if ($thissection > 1); + print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n"; + print OUT " " 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 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/ (?=[^<>]*(?:<|$))/ /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} .= " <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/#/ /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)/–/mg; # turn -- into endash in text or number range +$s =~ s/\(c\)/©/g; # turn (c) into copyright symbol + +# Use double quotes + +# $s =~ s/`([^']+)'/``$1''/g; + +$s =~ s/`([^']+)'/“$1”/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/</</g; +$s =~ s/>/>/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/ /; + +# 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> </tt>"; + } + + # For asis displays, use <pre> and no explicit breaks + + else + { + print OUT "<pre>\n"; + $eol = ""; + $indent = " "; + } + + # 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/&/&/g; + s/</</g; + s/>/>/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*/ <\/td><td>/g; + } + else + { + s/\s*\$t\s*/ <\/tt><\/td><td><tt>/g; + } + s/<tt><\/tt>//g; + print OUT "<tr><td> $_</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> $type<br><i>Default:</i> $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/ / /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 " <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 |