3 # A Perl script to turn the SGCAL source of the Exim documentation into
4 # Texinfo input, more or less...
6 # Supply the source file names as arguments.
7 # The output goes to the standard output.
10 ##################################################
11 # Ensure unique node name #
12 ##################################################
14 # Node names must be unique. Occasionally in the Exim spec there are duplicate
15 # section names, and it's become too much of a hassle to keep them distinct
16 # manually. So it is now automated.
18 ########### Never really got this working. Abandoned ###############
22 if (defined $node_names{$node})
24 $node_names{$node} += 1;
25 $node = "$node ($node_names{$node})";
27 print STDERR "+++ $node\n";
32 $node_names{$node} = 0;
39 ##################################################
40 # De-comma a node name #
41 ##################################################
43 # Commas, colons, and apostrophes are not permitted in Texinfo
44 # node names. I find this incredible, but it is clearly documented.
45 # The Exim manual has been re-organized not to have colons or
46 # apostrophes in any chapter or section titles, but I can't manage
47 # without commas. This function turns "," into " and", which is
48 # the best that can be done; we can use some clever Perlery to
49 # just take out commas before "and".
51 # Sigh. The Sendmail option -p<rval>:<sval> now means that there's a colon
52 # in the node name for that option. Turn the colon into <colon>. This is also
55 # Another thing that causes problems in node names in some versions of
56 # Texinfo is the use of @sc{xxx} for small caps. Just turn these all into
57 # real caps. This is also done for menus.
60 $_[0] =~ s/,(?!\sand)/ and/g;
62 $_[0] =~ s/\@sc\{([^}]*)\}/\U$1/g;
63 $_[0] =~ s/:/<colon>/g;
69 ##################################################
71 ##################################################
73 # @x is turned into x, except when x=@, or when asis is set,
74 # in which case single @ must turn into @@. A single substitute
75 # doesn't work in the non-asis case, because of the problems of
76 # handling things like @@@$, so we do it the hard way.
79 if ($asis) { $_[0] =~ s/@/@@/g; } else
82 $_[0] =~ s/@([^@])/$1/g;
89 ##################################################
91 ##################################################
93 # Called from handle_directive, to get the next source line
97 if ($processing_subsection)
98 { return $_ = shift @SUBBUFFER; }
105 ##################################################
106 # Handle text lines #
107 ##################################################
109 # This function is handed whole paragraphs, and we assume that
110 # SGCAL font changing markup is always complete within a paragraph.
111 # We have to replace escaped versions of significant characters with
112 # some magic before performing general transformations, and then
113 # put them back afterwards. The character & is not common in the text,
114 # and && is unknown, so we use that.
129 ($name) = $' =~ /^(\w+)/;
132 $value = $references{$name};
133 $value = "" if !defined($value);
135 if ($value =~ /\*\*\*\*/)
137 $value = ($` eq $current_chapter)? "\"$'\"" :
138 "\"$'\" in chapter \"$`\"";
139 $value = "" if $value eq "\"\"";
141 elsif ($value !~ /^[0-9]+\.[0-9]+$/) # quote unless version number
143 $value = "\"$value\"";
146 $_ = "${left}${value}${right}";
157 # Now remove all other @'s
161 # Convert SGCAL markup
163 s/#/ /g; # turn # into a space
164 s/\$~//g; # turn $~ into nothing
165 s/__/_/g; # turn __ into _
166 s/\$sm\{//g; # turn $sm{ into nothing
167 s/\$sc\{([^\}]*?)\}/$1/g; # turn $sc{xxx} into xxx
168 s/\$st\{([^\}]*?)\}/$1/g; # turn $st{xxx} into xxx
169 s/\$si\{([^\}]*?)\}/$1/g; # turn $si{xxx} into xxx
170 s/\$tt\{([^\}]*?)\}/$1/g; # turn $tt{xxx} into xxx
172 s/\$it\{([^\}]*?)\}/$1/g; # turn $it{xxx} into xxx
174 s/\$bf\{([^\}]*?)\}/$1/g; # turn $bf{xxx} into xxx
175 s/\$rm\{([^\}]*?)\}/$1/g; # turn $rm{xxx} into xxx
176 s/\$cb\{([^\}]*?)\}/$1/g; # turn $cb{xxx} into xxx
178 # This is a fudge for some specific usages of $<; can't just do a global
179 # is it occurs in things like $<variable name> as well.
181 s/\[\$<\]/[]/g; # turn [$<] into []
182 s/&&b\$<\./&&b./g; # turn \$<. into \. (\ == &&b by now)
183 s/(\d)\$<-/$1-/g; # turn 0$<- into 0-
185 # There is one case where the terminating } of an escape sequence is
186 # in another paragraph - this follows $sm{ - it can be fixed by
187 # removing any stray } in a paragraph that contains no { chars.
191 # Any remaining {} must be escaped to prevent Texinfo from complaining
195 # Now to conversions that put {} into the file.
196 # Change <<..>> from @var to just <...> as the caps that Texinfo
197 # uses look far too shouty.
199 s/\\\\([^\\]*?)\\\\/\@sc\{\L$1\}/g; # turn \\xxx\\ into @sc{xxx}
200 s/\\\(([^)]*?)\)\\/\@file\{$1\}/g; # turn \(xxx)\ into @file{xxx}
201 s/\\\"([^\"]*?)\"\\/\@file\{$1\}/g; # turn \"xxx"\ into @file{xxx}
203 s/\\\?([^?]*?)\?\\/$1/g; # turn \?URL?\ into URL
204 s/<<([^>]*?)>>/<$1>/g; # turn <<xxx>> into <xxx>
205 s/\\\$([^\$]*?)\$\\/\$$1/g; # turn \$xxx$\ into $xxx
206 s/\\\-([^-]*?)\-\\/\-$1/g; # turn \-xxx-\ into -xxx
207 s/\\\*\*([^*]*?)\*\*\\/$1/g; # turn \**xxx**\ into xxx
208 s/\[\(([\w\/]*)\)\]//g; # remove inline HTML
210 s/\\\*([^*]*?)\*\\/\@dfn\{$1\}/g; # turn \*xxx*\ into @dfn{xxx}
211 s/\\%([^*]*?)%\\/\@dfn\{$1\}/g; # turn \%xxx%\ into @dfn{xxx}
212 s/:::([^:]*?)::/\@dfn\{:$1:\}/g; # turn :::xxx:: into @dfn{:xxx:}
213 s/::([^:]*?)::/\@dfn\{$1:\}/g; # turn ::xxx:: into @dfn{xxx:}
214 s/\\([^\\]*?)\\/\@dfn\{$1\}/g; # turn \xxx\ into @dfn{xxx}
215 s/\$\*\$/\*/g; # turn $*$ into *
217 # Put back escaped SGCAL specials
229 # Remove any null flags ($$)
233 # If the paragraph starts with $c\b, change this into @center. Assume
234 # we don't ever get two of these in a row.
238 # If the paragraph starts with $e\b, stuff some tabs in there, as
239 # Texinfo can't do this on its own (as far as I can see). They must
240 # tabs; Texinfo treats them as different to spaces. Sigh.
242 s/^\$e\b/\t\t\t\t\t\t\t/;
244 # Handle $t. The Exim spec only ever has one tab per line. Er, not
245 # quite true, but a good enough assumption. $t is always followed
246 # by a non-word character.
248 # The .tabs directive has stashed the value in the $tab variable.
249 # Don't count Texinfo font chars.
251 while (/(^|.+?\n)(.+?)\$t(\W.*\n)/)
262 $plainleft =~ s/\@[a-z]+\{([^}]+?)\}/$1/g;
263 $plainleft =~ s/\@//g;
265 $_ = $before . $left . (" " x ($tab - length($plainleft))) . $right . $after;
267 # Fudge for the one case where there are two tabs
277 # Return the new line (paragraph)
284 ##################################################
285 # Handle directive lines #
286 ##################################################
288 # Use get_next_line() instead of <> because this is called to process
289 # stacked up subsection lines
291 sub handle_directive {
293 my($new_lastwasitem) = 0;
295 # Chapter directives just require . => @; however, dequoting the
296 # line thereafter will remove the first @, so just force it back
297 # afterwards. If the chapter is is one describing a driver, set
303 push(@ONESECTION, "@" . &dequote("$_\n"));
304 $driver_name = (/The\s+(\S+)\s+(director|router|transport|authenticator)/)? $1 :
305 (/Generic options common to both directors and routers/)?
306 "director or router" :
307 (/[Gg]eneric\s+options for (\S+)s/)? $1 : "";
308 $driver_name = &dequote($driver_name);
311 # Section directives just require . => @; however, dequoting the
312 # line thereafter will remove the first @, so just force it back
313 # afterwards. Remove any colons in section titles as they cause
314 # Texinfo trouble. Also remove any \\ (small caps) markup, which
315 # appears in a couple of cases.
322 push(@ONESECTION, "@" . &dequote("$_\n"));
324 # Horrible magic fudge to cope with the fact that exim_lock has
325 # -v and -q options, just like the main program.
327 $driver_name = "exim_lock" if /Mailbox maintenance/;
329 # Similar magic for exiqgrep, which also duplicates options
331 $driver_name = "exiqgrep" if /Selective queue listing/;
334 # .newline must put @* on the end of the previous line, if any, except
335 # inside a display, where it causes trouble.
339 if (@ONESECTION > 0 && ! $indisplay)
341 $_ = pop(@ONESECTION);
343 push(@ONESECTION, $_);
347 # .blank turns into @sp, adding 1 if no argument
351 s/\.blank\s+(\d+)/\@sp $1/;
353 push(@ONESECTION, $_);
356 # .rule turns into a line of hyphens
360 push(@ONESECTION, ("-" x ($in_itemize? 68 : 73)) . "\@*\n");
363 # There's one explicit .tabset setting for two tab stops
365 elsif (/\.tabset\s*/)
368 ($first,$second) = $rest =~ /(\d+)em\s+(\d+)em/;
369 $tab = ($first * 7)/6;
370 $tab2 = $tab + ($second * 7)/6;
373 # .tabs remembers the first (and only) tab setting
381 # .tempindent is used only to align some of the expansion stuff nicely;
382 # just ignore it. It is used in conjunction with .push/.pop.
384 elsif (/\.(tempindent|push|pop)\s*/)
388 # There are some instances of .if ~~sys.fancy in the source. Some of these
389 # are two-part things, in which case we just keep the non-fancy. For diagrams,
390 # however, they are in three parts:
393 # <aspic drawing stuff>
395 # <ascii art for txt and Texinfo>
397 # <HTML instructions for including a gif>
400 elsif (/\.if \~\~sys\.fancy/)
402 while (&get_next_line())
403 { last if /\.else\b/ || /\.elif\s+\~\~nothtml/ || /\.fi\b/; }
411 # There are occasional requirements to do things differently for
412 # Texinfo/HTML and the PS/txt versions, and there are also some
413 # HTML-specific things.
415 elsif (/\.if\s+~~sgcal/ || /\.if\s+~~html/)
417 while (&get_next_line()) { last if /\.else\b/ || /\.fi\b/; }
420 # We may also have Texinfo-specific bits
422 elsif (/^\.if\s+~~texinfo/)
427 # Ignore any other .if directives
431 # Skip else part if flag set
433 elsif (/\.else/ && $skip_else)
435 while (&get_next_line()) { last if /\.fi\b/; }
439 # Ignore other .fi and .else as any .if directives are handled specially
441 elsif (/\.fi/ || /\.else/) {}
445 elsif (/\.indent/) {}
447 # Plain .index goes to @cindex - the "concept" index. Also, there
448 # are some calls to vindex and findex in the SGCAL source - treated
449 # as synonymous with .index - which are split into the equivalent
452 elsif (/\.(.?)index/)
455 $letter = ($1 eq "")? "c" : $1;
456 tr/./@/; # .index -> @index
458 $rest =~ s/\\\(//g; # Remove markup
471 $rest =~ tr/\\//d; # Remove \
473 $rest =~ s/\@\$/\$/g; # @$ -> $
474 $rest =~ s/\@_/_/g; # @_ -> _
475 $rest =~ s/\@\+/+/g; # @+ -> +
476 $rest =~ s/\$\*\$/\*/g; # $*$ -> *
477 $rest =~ s/\$([^\$]+)\$/\$$1/g; # $x$ -> $x
479 $rest =~ s/^\s+//; # Remove leading spaces
480 $rest =~ s/\s+$//; # Remove trailing spaces
481 $rest =~ s/\|\|/:/; # || -> :
482 push(@ONESECTION, "\@${letter}index $rest\n");
484 # Duplicate entries for things that were listed as "x see y"
486 if (defined $indirections{$rest})
488 push(@ONESECTION, "\@${letter}index $indirections{$rest}\n");
492 # Various flavours of numberpars map to itemize and enumerate.
493 # Haven't found a way of having a blank space 'bullet' yet, so
494 # currently using minus.
496 elsif (/\.numberpars/)
502 if ($rest =~ /\$\./) { $flag = " \@bullet"; $type = "itemize" }
503 elsif ($rest =~ /\" \"/) { $flag = " \@minus"; $type = "itemize"; }
504 elsif ($rest =~ /roman/) { $flag = " a"; $type = "enumerate"; }
506 push(@ONESECTION, "\n\@$type$flag\n\n\@item\n");
507 push(@ENDLIST, $type);
513 push(@ONESECTION, "\n\@item\n");
518 $endname = pop(@ENDLIST);
519 push(@ONESECTION, "\@end $endname\n\n");
523 # The normal .display (typewriter font) => @example, while the rm
524 # form goes to @display (no change of font). For Texinfo we need a
525 # blank line after @display.
529 $type = /rm/? "display" : "example";
532 push(@ONESECTION, "\@$type\n\n");
533 push(@ENDLIST, $type);
540 $endname = pop(@ENDLIST);
541 push(@ONESECTION, "\@end $endname\n\n");
546 ($option, $type, $default) =
547 /\.conf\s+(\S+)\s+("(?:[^"]|"")+"|\S+)\s+("(?:[^"]|"")+"|.*)/;
549 $option = &dequote($option);
551 # If $type ends with $**$ (turned into a dagger for PS version),
552 # replace with ", expanded". Remove any surrounding quotes.
554 $type =~ s/^"([^"]+)"/$1/;
555 $type =~ s/\$\*\*\$/, expanded/;
557 # Default may be quoted, and it may also have quotes that are required,
560 $default =~ s/^"(.*)"$/$1/;
561 $default =~ s/""/"/g;
562 $default = &handle_text($default);
564 push(@ONESECTION, "\nType: $type\@*\nDefault: $default\n\n");
567 # Handle .startitems, .enditems, and .item
569 elsif (/\.startitem/ || /\.enditem/) {}
576 $arg = &dequote($arg);
577 $arg = &handle_text("\\**$arg**\\");
579 # If there are two .items in a row, we don't want to put in the
582 # push(@ONESECTION, "\n\@example\n");
583 push(@ONESECTION, "\n");
586 push(@ONESECTION, "_" x 75, "\n\n");
588 # push(@ONESECTION, "$arg\n\@end example\n\n");
589 push(@ONESECTION, "$arg\n\n");
590 $new_lastwasitem = 1;
597 $arg = &dequote("-$arg");
598 $arg = &handle_text($arg);
601 # Texinfo has no facility for emphasis bars.
606 # Just ignore any .(r)set directives pro tem (or maybe always!)
610 # Ignore .space, .linelength, and .justify
612 elsif (/\.space/ || /\.justify/ || /\.linelength/) {}
614 # Found an SGCAL directive that isn't dealt with. Oh dear.
615 # Turn the embarrassing characters into question marks and
616 # output it in an emphasized way.
621 push(@ONESECTION, "\n\>>>>>>> $_\n") if ! /^\.( |$)/;
624 $lastwasitem = $new_lastwasitem;
629 ##################################################
631 ##################################################
633 # $section_name is the name of the next section.
634 # $current_section is the name of the one we have buffered up.
635 # If it is unset, we are at the first section of a chapter.
636 # $previous_node is the section we last flushed if it was a node.
640 # If there is no text in the section, omit it entirely. However, it
641 # will have had a pointer set up at the start of the previous section.
642 # Remember what to replace this with when the chapter gets flushed.
645 foreach $s (@ONESECTION)
647 if ($s !~ /^(\@cindex|\@section|\s*$)/) { $skip = 0; last }
653 $rewrite{$current_section} = $section_name;
658 # There is data in the section: write it out to the chapter file
660 if ($current_section)
662 printf ONECHAPTER ("\@node %s, %s, %s, %s\n",
663 &decomma($current_section), &decomma($section_name),
664 &decomma($previous_node), &decomma($current_up));
665 $previous_node = $current_section;
666 while(scalar(@ONESECTION))
667 { print ONECHAPTER shift(@ONESECTION); }
671 while(scalar(@ONESECTION))
672 { push(@TOPSECTION, shift(@ONESECTION)); }
678 ##################################################
679 # Handle a "subsection" #
680 ##################################################
682 # A "subsection" is a set of options that must have their own
683 # local menu. Do two passes; the first just collects the names
684 # for the menu. This is called for .conf and .option items.
686 sub handle_subsection{
688 my($save_up) = $current_up;
690 $current_up = $current_section? $current_section : $current_chapter;
697 last if /^\.end$type/;
698 push(@SUBBUFFER, $_);
700 # .conf takes the first non-space string as the name, but as there are
701 # duplicate confs in various parts of the spec, use the driver name to
702 # de-duplicate; .option takes the entire rest of arg as the name, but
703 # removes any sequence of ... because this disturbs TexInfo. Also, it
706 if (/^\.$type\s+(\S+)(.*)/)
710 $name = &handle_text($1);
711 $name .= " ($driver_name)" if ($driver_name ne "");
715 chomp($name = &handle_text("-$1$2"));
716 $name =~ s/\s*\.\.\.//g;
718 $name .= " ($driver_name)" if ($driver_name ne "");
720 # There seems to be a major problem in texinfo with the string "--".
721 # In the text it gets turned into a single hyphen. This happens if it
722 # is used as a menu item, but *not* as a node name. Exim has a command
723 # line option "--". With no special action, this appears in the menu
724 # as "-", but then the info software complains there is no node called
725 # "-". If we triple it in the menu it gets displayed OK, but building
726 # software complains about non-existent cross references etc.
728 # I have gone for the horrid kludge of turning it into "-<hyhen>"
729 # in the menus and nodes.
731 # Exim 4 has added --help, which has the same problem.
733 $name = "-<hyphen>" if ($name eq "--");
734 $name = "-<hyphen>help" if ($name eq "--help");
736 push(@sublist, $name);
740 push (@ONESECTION, "\n\@sp 2\n\@menu\n");
741 for ($i = 0; $i < scalar(@sublist); $i++)
743 $mitem = $sublist[$i];
744 $mitem =~ s/\@sc\{([^}]*)\}/\U$1/g; # Get rid of small caps
745 $mitem =~ s/:/<colon>/g; # Get rid of colons
746 push (@ONESECTION, "* ${mitem}::\n");
748 push (@ONESECTION, "\@end menu\n\n");
750 $prevsub = $current_up;
751 $processing_subsection = 1;
752 while ($_ = shift(@SUBBUFFER))
754 if (/^\.$type\s+(\S+)/)
756 $name = shift @sublist;
757 $next = (scalar(@sublist))? $sublist[0] : "";
758 push @ONESECTION, sprintf("\@node %s, %s, %s, %s\n",
759 &decomma($name), &decomma($next), &decomma($prevsub),
760 &decomma($current_up));
762 if ($name eq "-<hyphen>") # Fudge for Texinfo
766 "\@unnumberedsubsec --- option\n");
768 "This option consists of two consecutive hyphens. It appears in\n",
769 "the menu as \"-<hyphen>\" because otherwise Texinfo gets\n",
770 "confused with its cross-referencing.\n");
772 elsif ($name eq "-<hyphen>help") # Fudge for Texinfo
776 "\@unnumberedsubsec ---help option\n");
778 "This option consists of \"help\" preceded by two consecutive\n" .
779 "hyphens. It appears in the menu as \"-<hyphen>help\" because\n" .
780 "otherwise Texinfo gets confused with its cross-referencing.\n");
786 "\@unnumberedsubsec $name option\n");
792 # Then handle as text or directive
794 if (substr($_, 0, 1) eq ".")
795 { handle_directive(); }
798 while($nextline = shift(@SUBBUFFER))
800 last if $nextline =~ /^(\.|\s*$)/;
803 push(@ONESECTION, handle_text($_));
805 last if !defined($_);
810 $processing_subsection = 0;
811 $section_pending = 1;
812 $current_up = $save_up;
818 ##################################################
819 # Handle a single chapter #
820 ##################################################
824 ($current_chapter) = /^\.chapter (.*)/;
825 $current_chapter = &dequote($current_chapter);
827 $current_chapter = $current_chapter;
829 my($tmp) = $current_chapter;
830 $tmp =~ s/\[\[\[\]\]\]/./;
831 print STDERR "processing chapter: $tmp\n";
833 # Remember the chapter name for the top-level menu
835 push(@chapter_list, $current_chapter);
837 # Open a temporary file to hold the chapter's text while collecting
838 # all its sections for a chapter-level menu.
840 $ONECHAPTER = "/tmp/ONECHAPTER.$$";
841 open(ONECHAPTER, ">$ONECHAPTER") || die "Can't open $ONECHAPTER for writing";
843 # Initialize for handling sections
849 undef $current_section;
852 $processing_subsection = 0;
854 $previous_node = $current_up = $current_chapter;
855 $section_pending = 0;
857 # Handle the .chapter directive as the first text of a section without
862 # Loop, handling each section. Assume they are sufficiently short that
863 # we can buffer the text in store, in an array called ONESECTION, instead
864 # of thrashing yet another file.
868 last if /^\.chapter /;
870 # Handle a new section, preserving $_ (handle_text flattens it).
871 # It seems we cannot get a fullstop into a Texinfo node name; use a magic
872 # character string that gets turned back into a dot by the post-processing.
878 $section_name =~ s/(\s|\n)+$//;
879 $section_name =~ s/://;
880 $section_name = &handle_text($section_name);
882 push(@section_list, $section_name);
883 $current_section = $section_name;
884 $next_node = $section_name if !$next_node;
885 $section_pending = 0;
889 # The .startconf macro introduces a set of .conf's which must have
890 # their own local set of menus. Suspend processing the section while
891 # we sort out the menu and copy their data. This is all done in a
892 # subroutine that is shared with options.
894 elsif (/^\.startconf\s+(.*)/)
897 $confuse = &dequote($confuse);
898 handle_subsection("conf");
902 elsif (/^\.startoption/)
904 handle_subsection("option");
908 # Deal with the actual data lines; if there's a section pending
909 # start a new section on hitting some text. We hope this happens
910 # only once per chapter...
912 if (substr($_, 0, 1) eq ".")
918 while($nextline = <>)
920 last if $nextline =~ /^(\.|\s*$)/;
923 if ($section_pending && !/^\s*$/)
925 $section_name = (defined $current_section)?
926 "$current_section (continued)" :
927 "$current_chapter (continued)" ;
929 push(@section_list, $section_name);
930 $current_section = $section_name;
931 $next_node = $section_name if !$next_node;
932 $section_pending = 0;
935 push(@ONESECTION, handle_text($_));
937 last if !defined($_);
942 # Flush any pending text, making its next field null.
943 # and fudging section_name for the final section of the previous.
948 # Set up section name as the start of the next chapter
950 $section_name = "Concept Index" if (!$doing_filter);
952 if (defined $_ && /^\.chapter (.*)/)
955 $section_name = &dequote($section_name);
957 $next_node = $section_name;
959 # Write out the chapter to the CHAPTERS file, sticking the chapter
960 # menu after the text that came before the first section heading. This
961 # will always at least contain the chapter title.
963 printf CHAPTERS ("\@node %s, %s, %s, Top\n",
964 &decomma($current_chapter), &decomma($next_node),
965 &decomma($previous_chapter));
967 # The pre-section stuff; if we hit an @end menu line, it is the menu of
968 # a "subsection" before the first section. In that case, we need to put
969 # the chapter's menu one the end of it, and then resume with the rest of
970 # the TOPSECTION data afterwards. We also need to thread together this
971 # "subsection"s nodes because they are all at the same level under the
975 while(scalar(@TOPSECTION))
977 $s = shift(@TOPSECTION);
978 if ($s =~ /^\@end menu/)
988 undef $next_actual_section;
991 if (scalar(@section_list))
993 print CHAPTERS "\n\@sp 2\n\@menu\n" if ! $in_menu;
994 $next_actual_section = $section_list[0];
995 for ($i = 0; $i < scalar(@section_list); $i++)
997 $section_name = $section_list[$i];
998 $section_name =~ s/\@sc\{([^}]*)\}/\U$1/g;
999 print CHAPTERS "* ${section_name}::\n";
1003 print CHAPTERS "\@end menu\n\n" if $in_menu;
1005 # Remainder of topsection; we must arrange that the final @node in
1006 # it (which will have a blank "next" field) actually points on to
1007 # the next section, if any. If this happens, then the next section
1008 # must point back to the final @node.
1010 while(scalar(@TOPSECTION))
1012 $s = shift(@TOPSECTION);
1013 if ($next_actual_section && $s =~
1014 /^\@node\s+([^,]+),\s*,\s*([^,]*),\s*(.*)/)
1016 my($t1, $t2, $t3) = ($1, $2, $3); # So can be decomma'd
1017 printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1),
1018 &decomma($next_actual_section), &decomma($t2), &decomma($t3));
1021 else { print CHAPTERS $s; }
1025 open(ONECHAPTER, "$ONECHAPTER") || die "Can't open $ONECHAPTER for reading";
1027 # While copying the chapter data, check for node references to empty
1028 # sections that got omitted and correct them, and correct the prev pointer
1029 # in the first node if necessary.
1031 while ($buff = <ONECHAPTER>)
1033 foreach $key (keys %rewrite)
1035 $buff =~ s/$key/$rewrite{$key}/;
1037 if ($point_back && $buff =~ /^\@node\s+([^,]+),\s*([^,]*),\s*([^,]*),\s*(.*)/)
1039 my($t1, $t2, $t4) = ($1, $2, $4); # so can be decomma'd
1040 printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1),
1041 &decomma($t2), &decomma($point_back), &decomma($t4));
1044 else { print CHAPTERS $buff; }
1047 $previous_chapter = $current_chapter;
1050 unlink($ONECHAPTER);
1055 ##################################################
1057 ##################################################
1059 # This is a two-pass algorithm. The first pass goes through and gets the
1060 # variable names for cross references. The second pass does the real work,
1061 # but we can't just read through doing the translation in one pass. We need
1062 # to know the list of chapters in order to build a top-level menu, and for
1063 # each chapter we need to know the sections in order to build a section
1064 # menu. Consequently, make use of temporary files to buffer things.
1066 # This script is used for the filter document and the overview as well;
1067 # flags tell it if it is doing one of them.
1075 $chapter_number = 0;
1076 $section_number = 0;
1078 if ($#ARGV >= 0 && $ARGV[0] eq "-filter")
1084 # First pass: Just fish out variable settings. Save the arguments so that
1085 # they can be reinstated for a second pass.
1087 print STDERR "Scanning for references\n";
1090 # Pick up any .set directives right at the very start
1094 last if ! /^\.set\s+(\S+)\s+(.+)$/;
1097 $value =~ s/^\"?(.*?)\"?\s*$/$1/;
1098 $references{$name} = $value;
1101 # Now skip everything before the first .chapter except for
1102 # .index lines that set up indirections. Save these so that
1103 # the relevant index entries can be duplicated.
1107 if (/^\.chapter\s+(.+)$/)
1110 $section_number = 0;
1111 $current_chapter = $1;
1112 $current_chapter = $current_chapter;
1113 $current_section = "";
1117 if (/^\.index\s+([^\$]+)\s+\$it\{see\s+([^}]+)\}\s*$/)
1119 $indirections{"$2"} = $1;
1127 if (/^\.chapter\s+(.+)$/)
1129 $current_chapter = $1;
1130 $current_chapter = &dequote($current_chapter);
1131 $current_section = "";
1133 elsif (/^\.section\s+(.+)$/)
1135 $current_section = $1;
1136 $current_section = &dequote($current_section);
1137 $current_section =~ s/://;
1139 elsif (/^\.r?set\s+(\S+)\s+(.+)$/ && $1 ne "runningfoot")
1144 # Only set the first time. This handles a few special cases in part2
1145 # which is included in the filter text as well.
1147 if (!exists($references{$name}))
1149 $value =~ s/^\"?(.*?)\"?\s*$/$1/;
1150 $value =~ s/~~chapter\./~~chapter****/;
1151 $value =~ s/~~chapter/$current_chapter/;
1152 $value =~ s/~~section/$current_section/;
1153 $references{$name} = $value;
1158 $final_chapter = defined($current_chapter)? $current_chapter : "";
1160 # Reinstate ARGV with the list of files and proceed to the main pass
1164 # $asis is set true when processing .display asis blocks, to stop
1165 # characters getting interpreted.
1169 # $indisplay is set true while processing .display blocks, to stop
1170 # .newlines being handled therein (adding @* wrecks alignment)
1174 # $tab is set to the value of the tab stop - only one stop is ever used
1175 # in the Exim source.
1179 # Current driver name, for disambiguating nodes
1183 # $section_pending is set if a new section is to be started on hitting
1186 $section_pending = 0;
1188 # Open a file to buffer up the entire set of chapters
1190 $CHAPTERS = "/tmp/CHAPTERS.$$";
1191 open(CHAPTERS, ">$CHAPTERS") || die "Can't open $CHAPTERS for writing";
1193 # Skip everything before the first .chapter
1195 while (<>) { last if /^\.chapter /; }
1197 # Loop, handling each chapter
1200 $previous_chapter = "Top";
1201 $previous_node = "Top";
1203 $chapter_number = 0;
1204 $section_number = 0;
1206 while (defined ($_) && /^\.chapter /)
1211 # Output the stuff at the start of the file
1213 print "\\input texinfo\n";
1215 print "\@set{wmYear} 2003\n";
1216 print "\@set{wmAuthor} Philip Hazel\n";
1217 print "\@set{wmAuthor_email} <ph10\@\@cus.cam.ac.uk>\n";
1218 print "\@set{COPYRIGHT1} Copyright \@copyright{} \@value{wmYear} University of Cambridge\n";
1220 print "\@c %**start of header\n";
1224 print "\@setfilename spec.info\n";
1225 print "\@settitle Exim Specification\n";
1229 print "\@setfilename filter.info\n";
1230 print "\@settitle Exim Filter Specification\n";
1233 print "\@paragraphindent 0\n";
1234 print "\@c %**end of header\n\n";
1237 print "\@titlepage\n";
1238 print "\@title The Exim Mail Transfer Agent\n";
1239 print "\@author \@value{wmAuthor}\n";
1242 print "\@vskip 0pt plus 1filll\n";
1244 print "Permission is granted to make and distribute verbatim copies of this manual provided the\n";
1245 print "copyright notice and this permission notice are preserved on all copies.\n";
1248 print "\@value{COPYRIGHT1}\@*\n";
1250 print "\@end titlepage\n\n";
1252 # Output the top-level node and its introductory blurb
1254 print "\@node Top, $chapter_list[0], (dir), (dir)\n";
1260 The Exim Mail Transfer Agent\@*
1261 ****************************
1263 The specification of the Exim Mail Transfer Agent is converted mechanically
1264 into Texinfo format from its original marked-up source. Some typographic
1265 representations are changed, chapters and sections cannot be numbered, and
1266 Texinfo lacks the ability to mark updated parts of the specification with
1269 Because the chapters and sections are unnumbered, cross references are set to
1270 their names. This makes the English a bit odd, with phrases like \`see chapter
1271 \"Retry configuration\"\' but it seemed very cumbersome to change this to \`see
1272 the chapter entitled \"Retry configuration\"\' each time.
1274 Each chapter, section, and configuration option has been placed in a separate
1275 Texinfo node. Texinfo doesn\'t allow commas, colons, or apostrophes in node
1276 names, which is a rather nasty restriction. I have arranged not to use colons
1277 or apostrophes in section titles, but cannot bring myself to omit them from
1278 titles such as \"The foo, bar and baz commands\". For the corresponding node
1279 names I have just used multiple occurrences of \"and\", though it looks very
1282 If a chapter or section continues after a list of configuration options that is
1283 not in a new section, a new node is started, using the chapter\'s or section\'s
1284 name plus \`(continued)\'. The \`Up\' operation from a section or configuration
1285 option returns to the start of the current chapter; the \`Up\' operation at a
1286 chapter start returns to the top of the document; the \`Up\' in a list of
1287 configuration options within a section returns to the top of that section.
1289 A number of drivers have options with the same name, so they have been
1290 disambiguated by adding the name of the driver to its option names in order to
1291 create node names. Thus, for example, the specification of the \`command\'
1292 options of the \`lmtp\' and \`pipe\' transports are in nodes called \`command
1293 (lmtp)\' and \`command (pipe)\', respectively.
1301 Filtering with the Exim Mail Transfer Agent\@*
1302 *******************************************
1304 The specifications of the Exim Mail Transfer Agent\'s filtering facility is
1305 converted mechanically into Texinfo format from its original marked-up source.
1306 Some typographic representations are changed, chapters and sections cannot be
1307 numbered, and Texinfo lacks the ability to mark updated parts of the
1308 specification with change bars.
1310 Because the chapters and sections are unnumbered, cross references are set to
1311 their names. This makes the English a bit odd, with phrases like \`see section
1312 \"Multiple personal mailboxes\"\' but it seemed very cumbersome to change this to
1313 \`see the section entitled \"Multiple personal mailboxes\"\' each time.
1318 # Output the top-level menu
1322 while (scalar(@chapter_list))
1324 $name = &decomma(shift(@chapter_list));
1325 print "* ${name}::\n";
1327 print "* Concept Index::\n" if (!$doing_filter);
1328 print "\@end menu\n\n";
1330 # Copy the chapters, then delete the temporary file
1333 open(CHAPTERS, "$CHAPTERS") || die "Can't open $CHAPTERS for reading";
1334 print $buff while($buff = <CHAPTERS>);
1338 # Output the finishing off stuff
1342 print "\@node Concept Index, , $final_chapter, Top\n";
1343 print "\@chapter Concept Index\n\@printindex cp\n";
1344 print "\@chapter Function Index\n\@printindex fn\n";
1346 print "\@contents\n";