DKIM: enforce limit of 20 on received DKIM-Signature: headers. Bug 2269
[exim.git] / doc / doc-scripts / g2t
CommitLineData
495ae4b0 1#! /usr/bin/perl -w
495ae4b0
PH
2
3# A Perl script to turn the SGCAL source of the Exim documentation into
4# Texinfo input, more or less...
5
6# Supply the source file names as arguments.
7# The output goes to the standard output.
8
9
10##################################################
11# Ensure unique node name #
12##################################################
13
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.
17
18########### Never really got this working. Abandoned ###############
19
20sub unique {
21my($node) = $_[0];
22if (defined $node_names{$node})
23 {
24 $node_names{$node} += 1;
25 $node = "$node ($node_names{$node})";
26
27print STDERR "+++ $node\n";
28
29 }
30else
31 {
32 $node_names{$node} = 0;
33 }
34$node;
35}
36
37
38
39##################################################
40# De-comma a node name #
41##################################################
42
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".
50
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
53# done for menus.
54
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.
58
59sub decomma {
60$_[0] =~ s/,(?!\sand)/ and/g;
61$_[0] =~ s/,//g;
62$_[0] =~ s/\@sc\{([^}]*)\}/\U$1/g;
63$_[0] =~ s/:/<colon>/g;
64$_[0];
65}
66
67
68
69##################################################
70# De-quote a string #
71##################################################
72
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.
77
78sub dequote {
79if ($asis) { $_[0] =~ s/@/@@/g; } else
80 {
81 $_[0] =~ s/@@/&at&/g;
82 $_[0] =~ s/@([^@])/$1/g;
83 $_[0] =~ s/&at&/@@/g;
84 }
85$_[0];
86}
87
88
89##################################################
90# Get next line #
91##################################################
92
93# Called from handle_directive, to get the next source line
94# into $_.
95
96sub get_next_line {
97if ($processing_subsection)
98 { return $_ = shift @SUBBUFFER; }
99else
100 { return $_ = <>; }
101}
102
103
104
105##################################################
106# Handle text lines #
107##################################################
108
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.
115
116sub handle_text {
117$_ = $_[0];
118
119if ($asis)
120 {
121 $_ = dequote($_);
122 s/(\{|\})/\@$1/g;
123 return $_;
124 }
125
126while (/~~/)
127 {
128 $left = $`;
129 ($name) = $' =~ /^(\w+)/;
130 $right = $';
131
132 $value = $references{$name};
133 $value = "" if !defined($value);
134
135 if ($value =~ /\*\*\*\*/)
136 {
137 $value = ($` eq $current_chapter)? "\"$'\"" :
138 "\"$'\" in chapter \"$`\"";
139 $value = "" if $value eq "\"\"";
140 }
141 elsif ($value !~ /^[0-9]+\.[0-9]+$/) # quote unless version number
142 {
143 $value = "\"$value\"";
144 }
145
146 $_ = "${left}${value}${right}";
147 }
148
149s/\@\@/&&a/g; # @@
150s/\@\\/&&b/g; # @\
151s/\@</&&l/g; # @<
152s/\@>/&&g/g; # @>
153s/\@\{/&&c/g; # @{
154s/\@\}/&&d/g; # @}
155s/\@#/&&s/g; # @#
156
157# Now remove all other @'s
158
159$_ = dequote($_);
160
161# Convert SGCAL markup
162
163s/#/ /g; # turn # into a space
164s/\$~//g; # turn $~ into nothing
165s/__/_/g; # turn __ into _
166s/\$sm\{//g; # turn $sm{ into nothing
167s/\$sc\{([^\}]*?)\}/$1/g; # turn $sc{xxx} into xxx
168s/\$st\{([^\}]*?)\}/$1/g; # turn $st{xxx} into xxx
169s/\$si\{([^\}]*?)\}/$1/g; # turn $si{xxx} into xxx
170s/\$tt\{([^\}]*?)\}/$1/g; # turn $tt{xxx} into xxx
171
172s/\$it\{([^\}]*?)\}/$1/g; # turn $it{xxx} into xxx
173
174s/\$bf\{([^\}]*?)\}/$1/g; # turn $bf{xxx} into xxx
175s/\$rm\{([^\}]*?)\}/$1/g; # turn $rm{xxx} into xxx
176s/\$cb\{([^\}]*?)\}/$1/g; # turn $cb{xxx} into xxx
177
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.
180
181s/\[\$<\]/[]/g; # turn [$<] into []
182s/&&b\$<\./&&b./g; # turn \$<. into \. (\ == &&b by now)
183s/(\d)\$<-/$1-/g; # turn 0$<- into 0-
184
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.
188
189s/\}//g if !/\{/;
190
191# Any remaining {} must be escaped to prevent Texinfo from complaining
192
193s/(\{|\})/\@$1/g;
194
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.
198
199s/\\\\([^\\]*?)\\\\/\@sc\{\L$1\}/g; # turn \\xxx\\ into @sc{xxx}
200s/\\\(([^)]*?)\)\\/\@file\{$1\}/g; # turn \(xxx)\ into @file{xxx}
201s/\\\"([^\"]*?)\"\\/\@file\{$1\}/g; # turn \"xxx"\ into @file{xxx}
202
203s/\\\?([^?]*?)\?\\/$1/g; # turn \?URL?\ into URL
204s/<<([^>]*?)>>/<$1>/g; # turn <<xxx>> into <xxx>
205s/\\\$([^\$]*?)\$\\/\$$1/g; # turn \$xxx$\ into $xxx
206s/\\\-([^-]*?)\-\\/\-$1/g; # turn \-xxx-\ into -xxx
207s/\\\*\*([^*]*?)\*\*\\/$1/g; # turn \**xxx**\ into xxx
208s/\[\(([\w\/]*)\)\]//g; # remove inline HTML
209
210s/\\\*([^*]*?)\*\\/\@dfn\{$1\}/g; # turn \*xxx*\ into @dfn{xxx}
211s/\\%([^*]*?)%\\/\@dfn\{$1\}/g; # turn \%xxx%\ into @dfn{xxx}
212s/:::([^:]*?)::/\@dfn\{:$1:\}/g; # turn :::xxx:: into @dfn{:xxx:}
213s/::([^:]*?)::/\@dfn\{$1:\}/g; # turn ::xxx:: into @dfn{xxx:}
214s/\\([^\\]*?)\\/\@dfn\{$1\}/g; # turn \xxx\ into @dfn{xxx}
215s/\$\*\$/\*/g; # turn $*$ into *
216
217# Put back escaped SGCAL specials
218
219s/&&a/\@\@/g;
220s/&&b/\\/g;
221s/&&l/</g;
222s/&&g/>/g;
223s/&&c/\@{/g;
224s/&&rc/{/g;
225s/&&rd/}/g;
226s/&&d/\@}/g;
227s/&&s/#/g;
228
229# Remove any null flags ($$)
230
231s/\$\$//g;
232
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.
235
236s/^\$c\b/\@center /;
237
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.
241
242s/^\$e\b/\t\t\t\t\t\t\t/;
243
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.
247
248# The .tabs directive has stashed the value in the $tab variable.
249# Don't count Texinfo font chars.
250
251while (/(^|.+?\n)(.+?)\$t(\W.*\n)/)
252 {
253 $before = $` . $1;
254 $after = $';
255 $left = $2;
256 $right = $3;
257
258 $left =~ s/\s$//;
259 $right =~ s/^\s+//;
260
261 $plainleft = $left;
262 $plainleft =~ s/\@[a-z]+\{([^}]+?)\}/$1/g;
263 $plainleft =~ s/\@//g;
264
265 $_ = $before . $left . (" " x ($tab - length($plainleft))) . $right . $after;
266
267 # Fudge for the one case where there are two tabs
268
269 if ($tab2 != 0)
270 {
271 $temp = $tab;
272 $tab = $tab2;
273 $tab2 = $temp;
274 }
275 }
276
277# Return the new line (paragraph)
278
279$_;
280}
281
282
283
284##################################################
285# Handle directive lines #
286##################################################
287
288# Use get_next_line() instead of <> because this is called to process
289# stacked up subsection lines
290
291sub handle_directive {
292
293my($new_lastwasitem) = 0;
294
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
298# the driver name.
299
300if (/\.chapter/)
301 {
302 tr/./@/;
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);
309 }
310
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.
316
317elsif (/\.section/)
318 {
319 tr/./@/;
320 s/://;
321 s"\\\\""g;
322 push(@ONESECTION, "@" . &dequote("$_\n"));
323
324 # Horrible magic fudge to cope with the fact that exim_lock has
325 # -v and -q options, just like the main program.
326
327 $driver_name = "exim_lock" if /Mailbox maintenance/;
328
329 # Similar magic for exiqgrep, which also duplicates options
330
331 $driver_name = "exiqgrep" if /Selective queue listing/;
332 }
333
334# .newline must put @* on the end of the previous line, if any, except
335# inside a display, where it causes trouble.
336
337elsif (/\.newline/)
338 {
339 if (@ONESECTION > 0 && ! $indisplay)
340 {
341 $_ = pop(@ONESECTION);
342 s/(\n*)$/\@*$1/;
343 push(@ONESECTION, $_);
344 }
345 }
346
347# .blank turns into @sp, adding 1 if no argument
348
349elsif (/\.blank/)
350 {
351 s/\.blank\s+(\d+)/\@sp $1/;
352 s/\.blank/\@sp 1/;
353 push(@ONESECTION, $_);
354 }
355
356# .rule turns into a line of hyphens
357
358elsif (/\.rule/)
359 {
360 push(@ONESECTION, ("-" x ($in_itemize? 68 : 73)) . "\@*\n");
361 }
362
363# There's one explicit .tabset setting for two tab stops
364
365elsif (/\.tabset\s*/)
366 {
367 $rest = $';
368 ($first,$second) = $rest =~ /(\d+)em\s+(\d+)em/;
369 $tab = ($first * 7)/6;
370 $tab2 = $tab + ($second * 7)/6;
371 }
372
373# .tabs remembers the first (and only) tab setting
374
375elsif (/\.tabs\s*/)
376 {
377 $tab = ($' * 7)/6;
378 $tab2 = 0;
379 }
380
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.
383
384elsif (/\.(tempindent|push|pop)\s*/)
385 {
386 }
387
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:
391#
392# .if ~~sys.fancy
393# <aspic drawing stuff>
394# .elif ~~nothtml
395# <ascii art for txt and Texinfo>
396# .else
397# <HTML instructions for including a gif>
398# .fi
399
400elsif (/\.if \~\~sys\.fancy/)
401 {
402 while (&get_next_line())
403 { last if /\.else\b/ || /\.elif\s+\~\~nothtml/ || /\.fi\b/; }
404
405 if (/\.elif/)
406 {
407 $skip_else = 1;
408 }
409 }
410
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.
414
415elsif (/\.if\s+~~sgcal/ || /\.if\s+~~html/)
416 {
417 while (&get_next_line()) { last if /\.else\b/ || /\.fi\b/; }
418 }
419
420# We may also have Texinfo-specific bits
421
422elsif (/^\.if\s+~~texinfo/)
423 {
424 $skip_else = 1;
425 }
426
427# Ignore any other .if directives
428
429elsif (/\.if/) {}
430
431# Skip else part if flag set
432
433elsif (/\.else/ && $skip_else)
434 {
435 while (&get_next_line()) { last if /\.fi\b/; }
436 $skip_else = 0;
437 }
438
439# Ignore other .fi and .else as any .if directives are handled specially
440
441elsif (/\.fi/ || /\.else/) {}
442
443# Ignore .indent
444
445elsif (/\.indent/) {}
446
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
450# indexes here.
451
452elsif (/\.(.?)index/)
453 {
454 $rest = $';
455 $letter = ($1 eq "")? "c" : $1;
456 tr/./@/; # .index -> @index
457
458 $rest =~ s/\\\(//g; # Remove markup
459 $rest =~ s/\)\\//g;
460 $rest =~ s/\\%//g;
461 $rest =~ s/%\\//g;
462 $rest =~ s/\\\*//g;
463 $rest =~ s/\*\\//g;
464 $rest =~ s/\\"//g;
465 $rest =~ s/"\\//g;
466 $rest =~ s/:://g;
467 $rest =~ s/\\-/-/g;
468 $rest =~ s/-\\//g;
469 $rest =~ s/~~//g;
470
471 $rest =~ tr/\\//d; # Remove \
472
473 $rest =~ s/\@\$/\$/g; # @$ -> $
474 $rest =~ s/\@_/_/g; # @_ -> _
475 $rest =~ s/\@\+/+/g; # @+ -> +
476 $rest =~ s/\$\*\$/\*/g; # $*$ -> *
477 $rest =~ s/\$([^\$]+)\$/\$$1/g; # $x$ -> $x
478
479 $rest =~ s/^\s+//; # Remove leading spaces
480 $rest =~ s/\s+$//; # Remove trailing spaces
481 $rest =~ s/\|\|/:/; # || -> :
482 push(@ONESECTION, "\@${letter}index $rest\n");
483
484 # Duplicate entries for things that were listed as "x see y"
485
486 if (defined $indirections{$rest})
487 {
488 push(@ONESECTION, "\@${letter}index $indirections{$rest}\n");
489 }
490 }
491
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.
495
496elsif (/\.numberpars/)
497 {
498 $rest = $';
499 $type = "enumerate";
500 $flag = "";
501
502 if ($rest =~ /\$\./) { $flag = " \@bullet"; $type = "itemize" }
503 elsif ($rest =~ /\" \"/) { $flag = " \@minus"; $type = "itemize"; }
504 elsif ($rest =~ /roman/) { $flag = " a"; $type = "enumerate"; }
505
506 push(@ONESECTION, "\n\@$type$flag\n\n\@item\n");
507 push(@ENDLIST, $type);
508 $in_itemize++;
509 }
510
511elsif (/\.nextp/)
512 {
513 push(@ONESECTION, "\n\@item\n");
514 }
515
516elsif (/\.endp/)
517 {
518 $endname = pop(@ENDLIST);
519 push(@ONESECTION, "\@end $endname\n\n");
520 $in_itemize--;
521 }
522
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.
526
527elsif (/\.display/)
528 {
529 $type = /rm/? "display" : "example";
530 $asis = 1 if /asis/;
531 $indisplay = 1;
532 push(@ONESECTION, "\@$type\n\n");
533 push(@ENDLIST, $type);
534 }
535
536elsif (/\.endd/)
537 {
538 $asis = 0;
539 $indisplay = 0;
540 $endname = pop(@ENDLIST);
541 push(@ONESECTION, "\@end $endname\n\n");
542 }
543
544elsif (/\.conf/)
545 {
546 ($option, $type, $default) =
547 /\.conf\s+(\S+)\s+("(?:[^"]|"")+"|\S+)\s+("(?:[^"]|"")+"|.*)/;
548
549 $option = &dequote($option);
550
551 # If $type ends with $**$ (turned into a dagger for PS version),
552 # replace with ", expanded". Remove any surrounding quotes.
553
554 $type =~ s/^"([^"]+)"/$1/;
555 $type =~ s/\$\*\*\$/, expanded/;
556
557 # Default may be quoted, and it may also have quotes that are required,
558 # if it is a string.
559
560 $default =~ s/^"(.*)"$/$1/;
561 $default =~ s/""/"/g;
562 $default = &handle_text($default);
563
564 push(@ONESECTION, "\nType: $type\@*\nDefault: $default\n\n");
565 }
566
567# Handle .startitems, .enditems, and .item
568
569elsif (/\.startitem/ || /\.enditem/) {}
570
571elsif (/\.item/)
572 {
573 $arg = $';
574 $arg =~ s/^\s*"//;
575 $arg =~ s/"\s*$//;
576 $arg = &dequote($arg);
577 $arg = &handle_text("\\**$arg**\\");
578
579 # If there are two .items in a row, we don't want to put in the
580 # separator line.
581
582# push(@ONESECTION, "\n\@example\n");
583 push(@ONESECTION, "\n");
584 if (! $lastwasitem)
585 {
586 push(@ONESECTION, "_" x 75, "\n\n");
587 }
588# push(@ONESECTION, "$arg\n\@end example\n\n");
589 push(@ONESECTION, "$arg\n\n");
590 $new_lastwasitem = 1;
591 }
592
593elsif (/\.option/)
594 {
595 chomp($arg = $');
596 $arg =~ s/^\s*//;
597 $arg = &dequote("-$arg");
598 $arg = &handle_text($arg);
599 }
600
601# Texinfo has no facility for emphasis bars.
602
603elsif (/\.em/) {}
604elsif (/\.nem/) {}
605
606# Just ignore any .(r)set directives pro tem (or maybe always!)
607
608elsif (/\.r?set/) {}
609
610# Ignore .space, .linelength, and .justify
611
612elsif (/\.space/ || /\.justify/ || /\.linelength/) {}
613
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.
617
618else
619 {
620 tr/@{}/???/;
621 push(@ONESECTION, "\n\>>>>>>> $_\n") if ! /^\.( |$)/;
622 }
623
624$lastwasitem = $new_lastwasitem;
625}
626
627
628
629##################################################
630# Flush a section #
631##################################################
632
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.
637
638sub flush_section {
639
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.
643
644my($skip) = 1;
645foreach $s (@ONESECTION)
646 {
647 if ($s !~ /^(\@cindex|\@section|\s*$)/) { $skip = 0; last }
648 }
649
650if ($skip)
651 {
652 pop @section_list;
653 $rewrite{$current_section} = $section_name;
654 @ONESECTION = ();
655 return;
656 }
657
658# There is data in the section: write it out to the chapter file
659
660if ($current_section)
661 {
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); }
668 }
669else
670 {
671 while(scalar(@ONESECTION))
672 { push(@TOPSECTION, shift(@ONESECTION)); }
673 }
674}
675
676
677
678##################################################
679# Handle a "subsection" #
680##################################################
681
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.
685
686sub handle_subsection{
687my($type) = $_[0];
688my($save_up) = $current_up;
689
690$current_up = $current_section? $current_section : $current_chapter;
691
692@sublist = ();
693@SUBBUFFER = ();
694
695while (<>)
696 {
697 last if /^\.end$type/;
698 push(@SUBBUFFER, $_);
699
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
704 # turns @- into -.
705
706 if (/^\.$type\s+(\S+)(.*)/)
707 {
708 if ($type eq "conf")
709 {
710 $name = &handle_text($1);
711 $name .= " ($driver_name)" if ($driver_name ne "");
712 }
713 else
714 {
715 chomp($name = &handle_text("-$1$2"));
716 $name =~ s/\s*\.\.\.//g;
717
718 $name .= " ($driver_name)" if ($driver_name ne "");
719
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.
727
4c04137d 728 # I have gone for the horrid kludge of turning it into "-<hyphen>"
495ae4b0
PH
729 # in the menus and nodes.
730
731 # Exim 4 has added --help, which has the same problem.
732
733 $name = "-<hyphen>" if ($name eq "--");
734 $name = "-<hyphen>help" if ($name eq "--help");
735 }
736 push(@sublist, $name);
737 }
738 }
739
740push (@ONESECTION, "\n\@sp 2\n\@menu\n");
741for ($i = 0; $i < scalar(@sublist); $i++)
742 {
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");
747 }
748push (@ONESECTION, "\@end menu\n\n");
749
750$prevsub = $current_up;
751$processing_subsection = 1;
752while ($_ = shift(@SUBBUFFER))
753 {
754 if (/^\.$type\s+(\S+)/)
755 {
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));
761
762 if ($name eq "-<hyphen>") # Fudge for Texinfo
763 {
764 push(@ONESECTION,
765 "\@findex $name\n",
766 "\@unnumberedsubsec --- option\n");
767 push(@ONESECTION,
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");
771 }
772 elsif ($name eq "-<hyphen>help") # Fudge for Texinfo
773 {
774 push(@ONESECTION,
775 "\@findex $name\n",
776 "\@unnumberedsubsec ---help option\n");
777 push(@ONESECTION,
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");
781 }
782 else
783 {
784 push(@ONESECTION,
785 "\@findex $name\n",
786 "\@unnumberedsubsec $name option\n");
787 }
788
789 $prevsub = $name;
790 }
791
792 # Then handle as text or directive
793
794 if (substr($_, 0, 1) eq ".")
795 { handle_directive(); }
796 else
797 {
798 while($nextline = shift(@SUBBUFFER))
799 {
800 last if $nextline =~ /^(\.|\s*$)/;
801 $_ .= $nextline;
802 }
803 push(@ONESECTION, handle_text($_));
804 $_ = $nextline;
805 last if !defined($_);
806 redo;
807 }
808 }
809
810$processing_subsection = 0;
811$section_pending = 1;
812$current_up = $save_up;
813}
814
815
816
817
818##################################################
819# Handle a single chapter #
820##################################################
821
822sub handle_chapter{
823chop;
824($current_chapter) = /^\.chapter (.*)/;
825$current_chapter = &dequote($current_chapter);
826
827$current_chapter = $current_chapter;
828
829my($tmp) = $current_chapter;
830$tmp =~ s/\[\[\[\]\]\]/./;
831print STDERR "processing chapter: $tmp\n";
832
833# Remember the chapter name for the top-level menu
834
835push(@chapter_list, $current_chapter);
836
837# Open a temporary file to hold the chapter's text while collecting
838# all its sections for a chapter-level menu.
839
840$ONECHAPTER = "/tmp/ONECHAPTER.$$";
841open(ONECHAPTER, ">$ONECHAPTER") || die "Can't open $ONECHAPTER for writing";
842
843# Initialize for handling sections
844
845@section_list = ();
846%rewrite = ();
847@ONESECTION = ();
848@TOPSECTION = ();
849undef $current_section;
850undef $next_node;
851
852$processing_subsection = 0;
853
854$previous_node = $current_up = $current_chapter;
855$section_pending = 0;
856
857# Handle the .chapter directive as the first text of a section without
858# a section title.
859
860handle_directive();
861
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.
865
866while (<>)
867 {
868 last if /^\.chapter /;
869
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.
873
874 if (/^\.section\s+/)
875 {
876 $save = $_;
877 $section_name = $';
878 $section_name =~ s/(\s|\n)+$//;
879 $section_name =~ s/://;
880 $section_name = &handle_text($section_name);
881 flush_section();
882 push(@section_list, $section_name);
883 $current_section = $section_name;
884 $next_node = $section_name if !$next_node;
885 $section_pending = 0;
886 $_ = $save;
887 }
888
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.
893
f055f31e 894 elsif (/^\.startconf\s+(.*)/)
495ae4b0 895 {
f055f31e
PH
896 $confuse = $1;
897 $confuse = &dequote($confuse);
495ae4b0
PH
898 handle_subsection("conf");
899 next;
900 }
901
902 elsif (/^\.startoption/)
903 {
904 handle_subsection("option");
905 next;
906 }
907
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...
911
912 if (substr($_, 0, 1) eq ".")
913 {
914 handle_directive();
915 }
916 else
917 {
918 while($nextline = <>)
919 {
920 last if $nextline =~ /^(\.|\s*$)/;
921 $_ .= $nextline;
922 }
923 if ($section_pending && !/^\s*$/)
924 {
925 $section_name = (defined $current_section)?
926 "$current_section (continued)" :
927 "$current_chapter (continued)" ;
928 flush_section();
929 push(@section_list, $section_name);
930 $current_section = $section_name;
931 $next_node = $section_name if !$next_node;
932 $section_pending = 0;
933 }
934
935 push(@ONESECTION, handle_text($_));
936 $_ = $nextline;
937 last if !defined($_);
938 redo;
939 }
940 }
941
942# Flush any pending text, making its next field null.
943# and fudging section_name for the final section of the previous.
944
945$section_name = "";
946flush_section();
947
948# Set up section name as the start of the next chapter
949
950$section_name = "Concept Index" if (!$doing_filter);
951
952if (defined $_ && /^\.chapter (.*)/)
953 {
954 $section_name = $1;
955 $section_name = &dequote($section_name);
956 }
957$next_node = $section_name;
958
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.
962
963printf CHAPTERS ("\@node %s, %s, %s, Top\n",
964 &decomma($current_chapter), &decomma($next_node),
965 &decomma($previous_chapter));
966
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
972# chapter.
973
974$in_menu = 0;
975while(scalar(@TOPSECTION))
976 {
977 $s = shift(@TOPSECTION);
978 if ($s =~ /^\@end menu/)
979 {
980 $in_menu = 1;
981 last;
982 }
983 print CHAPTERS $s;
984 }
985
986# Menu for sections
987
988undef $next_actual_section;
989undef $point_back;
990
991if (scalar(@section_list))
992 {
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++)
996 {
997 $section_name = $section_list[$i];
998 $section_name =~ s/\@sc\{([^}]*)\}/\U$1/g;
999 print CHAPTERS "* ${section_name}::\n";
1000 }
1001 $in_menu = 1;
1002 }
1003print CHAPTERS "\@end menu\n\n" if $in_menu;
1004
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.
1009
1010while(scalar(@TOPSECTION))
1011 {
1012 $s = shift(@TOPSECTION);
1013 if ($next_actual_section && $s =~
1014 /^\@node\s+([^,]+),\s*,\s*([^,]*),\s*(.*)/)
1015 {
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));
1019 $point_back = $1;
1020 }
1021 else { print CHAPTERS $s; }
1022 }
1023
1024close(ONECHAPTER);
1025open(ONECHAPTER, "$ONECHAPTER") || die "Can't open $ONECHAPTER for reading";
1026
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.
1030
1031while ($buff = <ONECHAPTER>)
1032 {
1033 foreach $key (keys %rewrite)
1034 {
1035 $buff =~ s/$key/$rewrite{$key}/;
1036 }
1037 if ($point_back && $buff =~ /^\@node\s+([^,]+),\s*([^,]*),\s*([^,]*),\s*(.*)/)
1038 {
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));
1042 undef $point_back;
1043 }
1044 else { print CHAPTERS $buff; }
1045 }
1046
1047$previous_chapter = $current_chapter;
1048
1049close(ONECHAPTER);
1050unlink($ONECHAPTER);
1051}
1052
1053
1054
1055##################################################
1056# Main Program #
1057##################################################
1058
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.
1065
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.
1068
1069$doing_filter = 0;
1070$skip_else = 0;
1071$in_itemize = 0;
1072$lastwasitem = 0;
f055f31e 1073$confuse = "";
495ae4b0
PH
1074
1075$chapter_number = 0;
1076$section_number = 0;
1077
1078if ($#ARGV >= 0 && $ARGV[0] eq "-filter")
1079 {
1080 $doing_filter = 1;
1081 shift @ARGV;
1082 }
1083
1084# First pass: Just fish out variable settings. Save the arguments so that
1085# they can be reinstated for a second pass.
1086
1087print STDERR "Scanning for references\n";
1088@save_argv = @ARGV;
1089
1090# Pick up any .set directives right at the very start
1091
1092while (<>)
1093 {
1094 last if ! /^\.set\s+(\S+)\s+(.+)$/;
1095 $name = $1;
1096 $value = $2;
1097 $value =~ s/^\"?(.*?)\"?\s*$/$1/;
1098 $references{$name} = $value;
1099 }
1100
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.
1104
1105while (<>)
1106 {
1107 if (/^\.chapter\s+(.+)$/)
1108 {
1109 $chapter_number++;
1110 $section_number = 0;
1111 $current_chapter = $1;
1112 $current_chapter = $current_chapter;
1113 $current_section = "";
1114 last;
1115 }
1116
1117 if (/^\.index\s+([^\$]+)\s+\$it\{see\s+([^}]+)\}\s*$/)
1118 {
1119 $indirections{"$2"} = $1;
1120 }
1121 }
1122
1123# Do the business
1124
1125while (<>)
1126 {
1127 if (/^\.chapter\s+(.+)$/)
1128 {
1129 $current_chapter = $1;
1130 $current_chapter = &dequote($current_chapter);
1131 $current_section = "";
1132 }
1133 elsif (/^\.section\s+(.+)$/)
1134 {
1135 $current_section = $1;
1136 $current_section = &dequote($current_section);
1137 $current_section =~ s/://;
1138 }
1139 elsif (/^\.r?set\s+(\S+)\s+(.+)$/ && $1 ne "runningfoot")
1140 {
1141 $name = $1;
1142 $value = $2;
1143
1144 # Only set the first time. This handles a few special cases in part2
1145 # which is included in the filter text as well.
1146
1147 if (!exists($references{$name}))
1148 {
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;
1154 }
1155 }
1156 }
1157
1158$final_chapter = defined($current_chapter)? $current_chapter : "";
1159
1160# Reinstate ARGV with the list of files and proceed to the main pass
1161
1162@ARGV = @save_argv;
1163
1164# $asis is set true when processing .display asis blocks, to stop
1165# characters getting interpreted.
1166
1167$asis = 0;
1168
1169# $indisplay is set true while processing .display blocks, to stop
1170# .newlines being handled therein (adding @* wrecks alignment)
1171
1172$indisplay = 0;
1173
1174# $tab is set to the value of the tab stop - only one stop is ever used
1175# in the Exim source.
1176
1177$tab = 0;
1178
1179# Current driver name, for disambiguating nodes
1180
1181$driver_name = "";
1182
1183# $section_pending is set if a new section is to be started on hitting
1184# any data lines.
1185
1186$section_pending = 0;
1187
1188# Open a file to buffer up the entire set of chapters
1189
1190$CHAPTERS = "/tmp/CHAPTERS.$$";
1191open(CHAPTERS, ">$CHAPTERS") || die "Can't open $CHAPTERS for writing";
1192
1193# Skip everything before the first .chapter
1194
1195while (<>) { last if /^\.chapter /; }
1196
1197# Loop, handling each chapter
1198
1199$current_up = "";
1200$previous_chapter = "Top";
1201$previous_node = "Top";
1202
1203$chapter_number = 0;
1204$section_number = 0;
1205
1206while (defined ($_) && /^\.chapter /)
1207 {
1208 handle_chapter();
1209 }
1210
1211# Output the stuff at the start of the file
1212
1213print "\\input texinfo\n";
1214
1215print "\@set{wmYear} 2003\n";
1216print "\@set{wmAuthor} Philip Hazel\n";
1217print "\@set{wmAuthor_email} <ph10\@\@cus.cam.ac.uk>\n";
1218print "\@set{COPYRIGHT1} Copyright \@copyright{} \@value{wmYear} University of Cambridge\n";
1219
1220print "\@c %**start of header\n";
1221
1222if (!$doing_filter)
1223 {
1224 print "\@setfilename spec.info\n";
1225 print "\@settitle Exim Specification\n";
1226 }
1227else
1228 {
1229 print "\@setfilename filter.info\n";
1230 print "\@settitle Exim Filter Specification\n";
1231 }
1232
1233print "\@paragraphindent 0\n";
1234print "\@c %**end of header\n\n";
1235
1236
1237print "\@titlepage\n";
1238print "\@title The Exim Mail Transfer Agent\n";
1239print "\@author \@value{wmAuthor}\n";
1240
1241print "\@page\n";
1242print "\@vskip 0pt plus 1filll\n";
1243
1244print "Permission is granted to make and distribute verbatim copies of this manual provided the\n";
1245print "copyright notice and this permission notice are preserved on all copies.\n";
1246
1247print "\@sp2\n";
1248print "\@value{COPYRIGHT1}\@*\n";
1249
1250print "\@end titlepage\n\n";
1251
1252# Output the top-level node and its introductory blurb
1253
1254print "\@node Top, $chapter_list[0], (dir), (dir)\n";
1255print "\@top\n";
1256
1257if (!$doing_filter)
1258{
1259print <<End;
1260The Exim Mail Transfer Agent\@*
1261****************************
1262
1263The specification of the Exim Mail Transfer Agent is converted mechanically
1264into Texinfo format from its original marked-up source. Some typographic
1265representations are changed, chapters and sections cannot be numbered, and
1266Texinfo lacks the ability to mark updated parts of the specification with
1267change bars.
1268
1269Because the chapters and sections are unnumbered, cross references are set to
1270their 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
1272the chapter entitled \"Retry configuration\"\' each time.
1273
1274Each chapter, section, and configuration option has been placed in a separate
1275Texinfo node. Texinfo doesn\'t allow commas, colons, or apostrophes in node
1276names, which is a rather nasty restriction. I have arranged not to use colons
1277or apostrophes in section titles, but cannot bring myself to omit them from
1278titles such as \"The foo, bar and baz commands\". For the corresponding node
1279names I have just used multiple occurrences of \"and\", though it looks very
1280ugly.
1281
1282If a chapter or section continues after a list of configuration options that is
1283not in a new section, a new node is started, using the chapter\'s or section\'s
1284name plus \`(continued)\'. The \`Up\' operation from a section or configuration
1285option returns to the start of the current chapter; the \`Up\' operation at a
1286chapter start returns to the top of the document; the \`Up\' in a list of
1287configuration options within a section returns to the top of that section.
1288
1289A number of drivers have options with the same name, so they have been
1290disambiguated by adding the name of the driver to its option names in order to
1291create node names. Thus, for example, the specification of the \`command\'
1292options of the \`lmtp\' and \`pipe\' transports are in nodes called \`command
1293(lmtp)\' and \`command (pipe)\', respectively.
1294
1295End
1296}
1297
1298else
1299{
1300print <<End;
1301Filtering with the Exim Mail Transfer Agent\@*
1302*******************************************
1303
1304The specifications of the Exim Mail Transfer Agent\'s filtering facility is
1305converted mechanically into Texinfo format from its original marked-up source.
1306Some typographic representations are changed, chapters and sections cannot be
1307numbered, and Texinfo lacks the ability to mark updated parts of the
1308specification with change bars.
1309
1310Because the chapters and sections are unnumbered, cross references are set to
1311their 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.
1314
1315End
1316}
1317
1318# Output the top-level menu
1319
1320print "\@menu\n";
1321
1322while (scalar(@chapter_list))
1323 {
1324 $name = &decomma(shift(@chapter_list));
1325 print "* ${name}::\n";
1326 }
1327print "* Concept Index::\n" if (!$doing_filter);
1328print "\@end menu\n\n";
1329
1330# Copy the chapters, then delete the temporary file
1331
1332close(CHAPTERS);
1333open(CHAPTERS, "$CHAPTERS") || die "Can't open $CHAPTERS for reading";
1334print $buff while($buff = <CHAPTERS>);
1335close(CHAPTERS);
1336unlink($CHAPTERS);
1337
1338# Output the finishing off stuff
1339
1340if (!$doing_filter)
1341 {
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";
1345 }
1346print "\@contents\n";
1347print "\@bye\n";
1348
1349# End