Buffer overrun fix. fixes: bug #787
[users/heiko/exim.git] / doc / doc-scripts / g2t
1 #! /usr/bin/perl -w
2 # $Cambridge: exim/doc/doc-scripts/g2t,v 1.2 2005/01/27 10:25:35 ph10 Exp $
3
4 # A Perl script to turn the SGCAL source of the Exim documentation into
5 # Texinfo input, more or less...
6
7 # Supply the source file names as arguments.
8 # The output goes to the standard output.
9
10
11 ##################################################
12 #         Ensure unique node name                #
13 ##################################################
14
15 # Node names must be unique. Occasionally in the Exim spec there are duplicate
16 # section names, and it's become too much of a hassle to keep them distinct
17 # manually. So it is now automated.
18
19 ########### Never really got this working. Abandoned ###############
20
21 sub unique {
22 my($node) = $_[0];
23 if (defined $node_names{$node})
24   {
25   $node_names{$node} += 1; 
26   $node = "$node ($node_names{$node})"; 
27   
28 print STDERR "+++ $node\n";
29  
30   }
31 else
32   {
33   $node_names{$node} = 0;
34   }
35 $node;
36 }  
37
38
39
40 ##################################################
41 #         De-comma a node name                   #
42 ##################################################
43
44 # Commas, colons, and apostrophes are not permitted in Texinfo
45 # node names. I find this incredible, but it is clearly documented.
46 # The Exim manual has been re-organized not to have colons or
47 # apostrophes in any chapter or section titles, but I can't manage
48 # without commas. This function turns "," into " and", which is
49 # the best that can be done; we can use some clever Perlery to
50 # just take out commas before "and".
51
52 # Sigh. The Sendmail option -p<rval>:<sval> now means that there's a colon
53 # in the node name for that option. Turn the colon into <colon>. This is also
54 # done for menus.
55
56 # Another thing that causes problems in node names in some versions of
57 # Texinfo is the use of @sc{xxx} for small caps. Just turn these all into
58 # real caps. This is also done for menus.
59
60 sub decomma {
61 $_[0] =~ s/,(?!\sand)/ and/g;
62 $_[0] =~ s/,//g;
63 $_[0] =~ s/\@sc\{([^}]*)\}/\U$1/g;
64 $_[0] =~ s/:/<colon>/g;
65 $_[0];
66 }
67
68
69
70 ##################################################
71 #           De-quote a string                    #
72 ##################################################
73
74 # @x is turned into x, except when x=@, or when asis is set,
75 # in which case single @ must turn into @@. A single substitute
76 # doesn't work in the non-asis case, because of the problems of
77 # handling things like @@@$, so we do it the hard way.
78
79 sub dequote {
80 if ($asis) { $_[0] =~ s/@/@@/g; } else
81   {
82   $_[0] =~ s/@@/&at&/g;
83   $_[0] =~ s/@([^@])/$1/g;
84   $_[0] =~ s/&at&/@@/g;
85   }
86 $_[0];
87 }
88
89
90 ##################################################
91 #           Get next line                        #
92 ##################################################
93
94 # Called from handle_directive, to get the next source line
95 # into $_.
96
97 sub get_next_line {
98 if ($processing_subsection)
99   { return $_ = shift @SUBBUFFER; }
100 else
101   { return $_ = <>; }
102 }
103
104
105
106 ##################################################
107 #           Handle text lines                    #
108 ##################################################
109
110 # This function is handed whole paragraphs, and we assume that
111 # SGCAL font changing markup is always complete within a paragraph.
112 # We have to replace escaped versions of significant characters with
113 # some magic before performing general transformations, and then
114 # put them back afterwards. The character & is not common in the text,
115 # and && is unknown, so we use that.
116
117 sub handle_text {
118 $_ = $_[0];
119
120 if ($asis)
121   {
122   $_ = dequote($_);
123   s/(\{|\})/\@$1/g;
124   return $_;
125   }
126
127 while (/~~/)
128   {
129   $left = $`;
130   ($name) = $' =~ /^(\w+)/;
131   $right = $';
132
133   $value = $references{$name};
134   $value = "" if !defined($value);
135
136   if ($value =~ /\*\*\*\*/)
137     {
138     $value = ($` eq $current_chapter)? "\"$'\"" :
139       "\"$'\" in chapter \"$`\"";
140     $value = "" if $value eq "\"\"";
141     }
142   elsif ($value !~ /^[0-9]+\.[0-9]+$/)   # quote unless version number
143     {                                                
144     $value = "\"$value\"";                          
145     }
146
147   $_ = "${left}${value}${right}";
148   }
149
150 s/\@\@/&&a/g;         # @@
151 s/\@\\/&&b/g;         # @\
152 s/\@</&&l/g;          # @<
153 s/\@>/&&g/g;          # @>
154 s/\@\{/&&c/g;         # @{
155 s/\@\}/&&d/g;         # @}
156 s/\@#/&&s/g;          # @#
157
158 # Now remove all other @'s
159
160 $_ = dequote($_);
161
162 # Convert SGCAL markup
163
164 s/#/ /g;                            # turn #   into a space
165 s/\$~//g;                           # turn $~  into nothing
166 s/__/_/g;                           # turn __  into _
167 s/\$sm\{//g;                        # turn $sm{     into nothing
168 s/\$sc\{([^\}]*?)\}/$1/g;           # turn $sc{xxx} into xxx
169 s/\$st\{([^\}]*?)\}/$1/g;           # turn $st{xxx} into xxx
170 s/\$si\{([^\}]*?)\}/$1/g;           # turn $si{xxx} into xxx
171 s/\$tt\{([^\}]*?)\}/$1/g;           # turn $tt{xxx} into xxx
172
173 s/\$it\{([^\}]*?)\}/$1/g;           # turn $it{xxx} into xxx
174
175 s/\$bf\{([^\}]*?)\}/$1/g;           # turn $bf{xxx} into xxx
176 s/\$rm\{([^\}]*?)\}/$1/g;           # turn $rm{xxx} into xxx
177 s/\$cb\{([^\}]*?)\}/$1/g;           # turn $cb{xxx} into xxx
178
179 # This is a fudge for some specific usages of $<; can't just do a global
180 # is it occurs in things like $<variable name> as well.
181
182 s/\[\$<\]/[]/g;                     # turn [$<]     into []
183 s/&&b\$<\./&&b./g;                  # turn \$<.     into \.  (\ == &&b by now)
184 s/(\d)\$<-/$1-/g;                   # turn 0$<-     into 0-
185
186 # There is one case where the terminating } of an escape sequence is
187 # in another paragraph - this follows $sm{ - it can be fixed by
188 # removing any stray } in a paragraph that contains no { chars.
189
190 s/\}//g if !/\{/;
191
192 # Any remaining {} must be escaped to prevent Texinfo from complaining
193
194 s/(\{|\})/\@$1/g;
195
196 # Now to conversions that put {} into the file.
197 # Change <<..>> from @var to just <...> as the caps that Texinfo
198 # uses look far too shouty.
199
200 s/\\\\([^\\]*?)\\\\/\@sc\{\L$1\}/g; # turn \\xxx\\  into @sc{xxx}
201 s/\\\(([^)]*?)\)\\/\@file\{$1\}/g;  # turn \(xxx)\  into @file{xxx}
202 s/\\\"([^\"]*?)\"\\/\@file\{$1\}/g; # turn \"xxx"\  into @file{xxx}
203
204 s/\\\?([^?]*?)\?\\/$1/g;            # turn \?URL?\    into URL
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/\\\*\*([^*]*?)\*\*\\/$1/g;        # turn \**xxx**\  into xxx
209 s/\[\(([\w\/]*)\)\]//g;             # remove inline HTML
210
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/\\([^\\]*?)\\/\@dfn\{$1\}/g;        # turn \xxx\      into @dfn{xxx}
216 s/\$\*\$/\*/g;                        # turn $*$        into *
217
218 # Put back escaped SGCAL specials
219
220 s/&&a/\@\@/g;
221 s/&&b/\\/g;
222 s/&&l/</g;
223 s/&&g/>/g;
224 s/&&c/\@{/g;
225 s/&&rc/{/g;
226 s/&&rd/}/g;
227 s/&&d/\@}/g;
228 s/&&s/#/g;
229
230 # Remove any null flags ($$)
231
232 s/\$\$//g;
233
234 # If the paragraph starts with $c\b, change this into @center. Assume
235 # we don't ever get two of these in a row.
236
237 s/^\$c\b/\@center /;
238
239 # If the paragraph starts with $e\b, stuff some tabs in there, as
240 # Texinfo can't do this on its own (as far as I can see). They must
241 # tabs; Texinfo treats them as different to spaces. Sigh.
242
243 s/^\$e\b/\t\t\t\t\t\t\t/;
244
245 # Handle $t. The Exim spec only ever has one tab per line. Er, not
246 # quite true, but a good enough assumption. $t is always followed
247 # by a non-word character.
248
249 # The .tabs directive has stashed the value in the $tab variable.
250 # Don't count Texinfo font chars.
251
252 while (/(^|.+?\n)(.+?)\$t(\W.*\n)/)
253   {
254   $before = $` . $1;
255   $after = $';
256   $left = $2;
257   $right = $3;
258
259   $left =~ s/\s$//;
260   $right =~ s/^\s+//;
261
262   $plainleft = $left;
263   $plainleft =~ s/\@[a-z]+\{([^}]+?)\}/$1/g;
264   $plainleft =~ s/\@//g;
265
266   $_ = $before . $left . (" " x ($tab - length($plainleft))) . $right . $after;
267
268   # Fudge for the one case where there are two tabs
269
270   if ($tab2 != 0)
271     {
272     $temp = $tab;
273     $tab = $tab2;
274     $tab2 = $temp;
275     }
276   }
277
278 # Return the new line (paragraph)
279
280 $_;
281 }
282
283
284
285 ##################################################
286 #           Handle directive lines               #
287 ##################################################
288
289 # Use get_next_line() instead of <> because this is called to process
290 # stacked up subsection lines
291
292 sub handle_directive {
293
294 my($new_lastwasitem) = 0;
295
296 # Chapter directives just require . => @; however, dequoting the
297 # line thereafter will remove the first @, so just force it back
298 # afterwards. If the chapter is is one describing a driver, set
299 # the driver name.
300
301 if (/\.chapter/)
302   {
303   tr/./@/;
304   push(@ONESECTION, "@" . &dequote("$_\n"));
305   $driver_name = (/The\s+(\S+)\s+(director|router|transport|authenticator)/)? $1 :
306     (/Generic options common to both directors and routers/)?
307       "director or router" :
308     (/[Gg]eneric\s+options for (\S+)s/)? $1 : "";
309   $driver_name = &dequote($driver_name);
310   }
311
312 # Section directives just require . => @; however, dequoting the
313 # line thereafter will remove the first @, so just force it back
314 # afterwards. Remove any colons in section titles as they cause
315 # Texinfo trouble. Also remove any \\ (small caps) markup, which
316 # appears in a couple of cases.
317
318 elsif (/\.section/)
319   {
320   tr/./@/;
321   s/://;
322   s"\\\\""g;
323   push(@ONESECTION, "@" . &dequote("$_\n"));
324
325   # Horrible magic fudge to cope with the fact that exim_lock has
326   # -v and -q options, just like the main program.
327
328   $driver_name = "exim_lock" if /Mailbox maintenance/;
329   
330   # Similar magic for exiqgrep, which also duplicates options
331   
332   $driver_name = "exiqgrep" if /Selective queue listing/;  
333   }
334
335 # .newline must put @* on the end of the previous line, if any, except
336 # inside a display, where it causes trouble.
337
338 elsif (/\.newline/)
339   {
340   if (@ONESECTION > 0 && ! $indisplay)
341     {
342     $_ = pop(@ONESECTION);
343     s/(\n*)$/\@*$1/;
344     push(@ONESECTION, $_);
345     }
346   }
347
348 # .blank turns into @sp, adding 1 if no argument
349
350 elsif (/\.blank/)
351   {
352   s/\.blank\s+(\d+)/\@sp $1/;
353   s/\.blank/\@sp 1/;
354   push(@ONESECTION, $_);
355   }
356
357 # .rule turns into a line of hyphens
358
359 elsif (/\.rule/)
360   {
361   push(@ONESECTION, ("-" x ($in_itemize? 68 : 73)) . "\@*\n");
362   }
363
364 # There's one explicit .tabset setting for two tab stops
365
366 elsif (/\.tabset\s*/)
367   {
368   $rest = $';
369   ($first,$second) = $rest =~ /(\d+)em\s+(\d+)em/;
370   $tab = ($first * 7)/6;
371   $tab2 = $tab + ($second * 7)/6;
372   }
373
374 # .tabs remembers the first (and only) tab setting
375
376 elsif (/\.tabs\s*/)
377   {
378   $tab = ($' * 7)/6;
379   $tab2 = 0;
380   }
381
382 # .tempindent is used only to align some of the expansion stuff nicely;
383 # just ignore it. It is used in conjunction with .push/.pop.
384
385 elsif (/\.(tempindent|push|pop)\s*/)
386   {
387   }
388
389 # There are some instances of .if ~~sys.fancy in the source. Some of these
390 # are two-part things, in which case we just keep the non-fancy. For diagrams,
391 # however, they are in three parts:
392 #
393 # .if ~~sys.fancy
394 # <aspic drawing stuff>
395 # .elif ~~nothtml
396 # <ascii art for txt and Texinfo>
397 # .else
398 # <HTML instructions for including a gif>
399 # .fi
400
401 elsif (/\.if \~\~sys\.fancy/)
402   {
403   while (&get_next_line())
404     { last if /\.else\b/ || /\.elif\s+\~\~nothtml/ || /\.fi\b/; }
405
406   if (/\.elif/)
407     {
408     $skip_else = 1;
409     }
410   }
411
412 # There are occasional requirements to do things differently for
413 # Texinfo/HTML and the PS/txt versions, and there are also some
414 # HTML-specific things.
415
416 elsif (/\.if\s+~~sgcal/ || /\.if\s+~~html/)
417   {
418   while (&get_next_line()) { last if /\.else\b/ || /\.fi\b/; }
419   }
420
421 # We may also have Texinfo-specific bits
422
423 elsif (/^\.if\s+~~texinfo/)
424   {
425   $skip_else = 1;
426   }
427
428 # Ignore any other .if directives
429
430 elsif (/\.if/) {}
431
432 # Skip else part if flag set
433
434 elsif (/\.else/ && $skip_else)
435   {
436   while (&get_next_line()) { last if /\.fi\b/; }
437   $skip_else = 0;
438   }
439
440 # Ignore other .fi and .else as any .if directives are handled specially
441
442 elsif (/\.fi/ || /\.else/) {}
443
444 # Ignore .indent
445
446 elsif (/\.indent/) {}
447
448 # Plain .index goes to @cindex - the "concept" index. Also, there
449 # are some calls to vindex and findex in the SGCAL source - treated
450 # as synonymous with .index - which are split into the equivalent
451 # indexes here.
452
453 elsif (/\.(.?)index/)
454   {
455   $rest = $';
456   $letter = ($1 eq "")? "c" : $1;
457   tr/./@/;                           # .index -> @index
458   
459   $rest =~ s/\\\(//g;                # Remove markup
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   $rest =~ s/~~//g;     
471  
472   $rest =~ tr/\\//d;                 # Remove \
473    
474   $rest =~ s/\@\$/\$/g;              # @$  -> $
475   $rest =~ s/\@_/_/g;                # @_  -> _
476   $rest =~ s/\@\+/+/g;               # @+  -> +
477   $rest =~ s/\$\*\$/\*/g;            # $*$ -> *
478   $rest =~ s/\$([^\$]+)\$/\$$1/g;    # $x$ -> $x
479    
480   $rest =~ s/^\s+//;                 # Remove leading spaces
481   $rest =~ s/\s+$//;                 # Remove trailing spaces
482   $rest =~ s/\|\|/:/;                # || -> : 
483   push(@ONESECTION, "\@${letter}index $rest\n");
484
485   # Duplicate entries for things that were listed as "x see y"
486
487   if (defined $indirections{$rest})
488     {
489     push(@ONESECTION, "\@${letter}index $indirections{$rest}\n");
490     }
491   }
492
493 # Various flavours of numberpars map to itemize and enumerate.
494 # Haven't found a way of having a blank space 'bullet' yet, so
495 # currently using minus.
496
497 elsif (/\.numberpars/)
498   {
499   $rest = $';
500   $type = "enumerate";
501   $flag = "";
502
503   if    ($rest =~ /\$\./)  { $flag = " \@bullet"; $type = "itemize" }
504   elsif ($rest =~ /\" \"/) { $flag = " \@minus";  $type = "itemize"; }
505   elsif ($rest =~ /roman/) { $flag = " a"; $type = "enumerate"; }
506
507   push(@ONESECTION, "\n\@$type$flag\n\n\@item\n");
508   push(@ENDLIST, $type);
509   $in_itemize++;
510   }
511
512 elsif (/\.nextp/)
513   {
514   push(@ONESECTION, "\n\@item\n");
515   }
516
517 elsif (/\.endp/)
518   {
519   $endname = pop(@ENDLIST);
520   push(@ONESECTION, "\@end $endname\n\n");
521   $in_itemize--;
522   }
523
524 # The normal .display (typewriter font) => @example, while the rm
525 # form goes to @display (no change of font). For Texinfo we need a
526 # blank line after @display.
527
528 elsif (/\.display/)
529   {
530   $type = /rm/? "display" : "example";
531   $asis = 1 if /asis/;
532   $indisplay = 1;
533   push(@ONESECTION, "\@$type\n\n");
534   push(@ENDLIST, $type);
535   }
536
537 elsif (/\.endd/)
538   {
539   $asis = 0;
540   $indisplay = 0;
541   $endname = pop(@ENDLIST);
542   push(@ONESECTION, "\@end $endname\n\n");
543   }
544
545 elsif (/\.conf/)
546   {
547   ($option, $type, $default) =
548     /\.conf\s+(\S+)\s+("(?:[^"]|"")+"|\S+)\s+("(?:[^"]|"")+"|.*)/;
549
550   $option = &dequote($option);
551
552   # If $type ends with $**$ (turned into a dagger for PS version),
553   # replace with ", expanded". Remove any surrounding quotes.
554
555   $type =~ s/^"([^"]+)"/$1/;
556   $type =~ s/\$\*\*\$/, expanded/;
557
558   # Default may be quoted, and it may also have quotes that are required,
559   # if it is a string.
560
561   $default =~ s/^"(.*)"$/$1/;
562   $default =~ s/""/"/g;
563   $default = &handle_text($default);
564
565   push(@ONESECTION, "\nType: $type\@*\nDefault: $default\n\n");
566   }
567
568 # Handle .startitems, .enditems, and .item
569
570 elsif (/\.startitem/ || /\.enditem/) {}
571
572 elsif (/\.item/)
573   {
574   $arg = $';
575   $arg =~ s/^\s*"//;
576   $arg =~ s/"\s*$//;
577   $arg = &dequote($arg);
578   $arg = &handle_text("\\**$arg**\\");
579
580   # If there are two .items in a row, we don't want to put in the
581   # separator line.
582
583 #  push(@ONESECTION, "\n\@example\n");
584   push(@ONESECTION, "\n");
585   if (! $lastwasitem)
586     {
587     push(@ONESECTION, "_" x 75, "\n\n");
588     }
589 #  push(@ONESECTION, "$arg\n\@end example\n\n");
590   push(@ONESECTION, "$arg\n\n");
591   $new_lastwasitem = 1;
592   }
593
594 elsif (/\.option/)
595   {
596   chomp($arg = $');
597   $arg =~ s/^\s*//;
598   $arg = &dequote("-$arg");
599   $arg = &handle_text($arg);
600   }
601
602 # Texinfo has no facility for emphasis bars.
603
604 elsif (/\.em/) {}
605 elsif (/\.nem/) {}
606
607 # Just ignore any .(r)set directives pro tem (or maybe always!)
608
609 elsif (/\.r?set/) {}
610
611 # Ignore .space, .linelength, and .justify
612
613 elsif (/\.space/ || /\.justify/ || /\.linelength/) {}
614
615 # Found an SGCAL directive that isn't dealt with. Oh dear.
616 # Turn the embarrassing characters into question marks and
617 # output it in an emphasized way.
618
619 else
620   {
621   tr/@{}/???/;
622   push(@ONESECTION, "\n\>>>>>>> $_\n") if ! /^\.( |$)/;
623   }
624
625 $lastwasitem = $new_lastwasitem;
626 }
627
628
629
630 ##################################################
631 #             Flush a section                    #
632 ##################################################
633
634 # $section_name is the name of the next section.
635 # $current_section is the name of the one we have buffered up.
636 # If it is unset, we are at the first section of a chapter.
637 # $previous_node is the section we last flushed if it was a node.
638
639 sub flush_section {
640
641 # If there is no text in the section, omit it entirely. However, it
642 # will have had a pointer set up at the start of the previous section.
643 # Remember what to replace this with when the chapter gets flushed.
644
645 my($skip) = 1;
646 foreach $s (@ONESECTION)
647   {
648   if ($s !~ /^(\@cindex|\@section|\s*$)/) { $skip = 0; last }
649   }
650
651 if ($skip)
652   {
653   pop @section_list;
654   $rewrite{$current_section} = $section_name;
655   @ONESECTION = ();
656   return;
657   }
658
659 # There is data in the section: write it out to the chapter file
660
661 if ($current_section)
662   {
663   printf ONECHAPTER ("\@node %s, %s, %s, %s\n",
664     &decomma($current_section), &decomma($section_name),
665     &decomma($previous_node), &decomma($current_up));
666   $previous_node = $current_section;
667   while(scalar(@ONESECTION))
668     { print ONECHAPTER shift(@ONESECTION); }
669   }
670 else
671   {
672   while(scalar(@ONESECTION))
673     { push(@TOPSECTION, shift(@ONESECTION)); }
674   }
675 }
676
677
678
679 ##################################################
680 #          Handle a "subsection"                 #
681 ##################################################
682
683 # A "subsection" is a set of options that must have their own
684 # local menu. Do two passes; the first just collects the names
685 # for the menu. This is called for .conf and .option items.
686
687 sub handle_subsection{
688 my($type) = $_[0];
689 my($save_up) = $current_up;
690
691 $current_up = $current_section? $current_section : $current_chapter;
692
693 @sublist = ();
694 @SUBBUFFER = ();
695
696 while (<>)
697   {
698   last if /^\.end$type/;
699   push(@SUBBUFFER, $_);
700
701   # .conf takes the first non-space string as the name, but as there are
702   # duplicate confs in various parts of the spec, use the driver name to
703   # de-duplicate; .option takes the entire rest of arg as the name, but
704   # removes any sequence of ... because this disturbs TexInfo. Also, it
705   # turns @- into -.
706
707   if (/^\.$type\s+(\S+)(.*)/)
708     {
709     if ($type eq "conf")
710       {
711       $name = &handle_text($1);
712       $name .= " ($driver_name)" if ($driver_name ne "");
713       }
714     else
715       {
716       chomp($name = &handle_text("-$1$2"));
717       $name =~ s/\s*\.\.\.//g;
718
719       $name .= " ($driver_name)" if ($driver_name ne "");
720
721       # There seems to be a major problem in texinfo with the string "--".
722       # In the text it gets turned into a single hyphen. This happens if it
723       # is used as a menu item, but *not* as a node name. Exim has a command
724       # line option "--". With no special action, this appears in the menu
725       # as "-", but then the info software complains there is no node called
726       # "-". If we triple it in the menu it gets displayed OK, but building
727       # software complains about non-existent cross references etc.
728
729       # I have gone for the horrid kludge of turning it into "-<hyhen>"
730       # in the menus and nodes.
731
732       # Exim 4 has added --help, which has the same problem.
733
734       $name = "-<hyphen>" if ($name eq "--");
735       $name = "-<hyphen>help" if ($name eq "--help");
736       }
737     push(@sublist, $name);
738     }
739   }
740
741 push (@ONESECTION, "\n\@sp 2\n\@menu\n");
742 for ($i = 0; $i < scalar(@sublist); $i++)
743   {
744   $mitem = $sublist[$i];
745   $mitem =~ s/\@sc\{([^}]*)\}/\U$1/g;       # Get rid of small caps
746   $mitem =~ s/:/<colon>/g;                  # Get rid of colons
747   push (@ONESECTION, "* ${mitem}::\n");
748   }
749 push (@ONESECTION, "\@end menu\n\n");
750
751 $prevsub = $current_up;
752 $processing_subsection = 1;
753 while ($_ = shift(@SUBBUFFER))
754   {
755   if (/^\.$type\s+(\S+)/)
756     {
757     $name = shift @sublist;
758     $next = (scalar(@sublist))? $sublist[0] : "";
759     push @ONESECTION, sprintf("\@node %s, %s, %s, %s\n",
760       &decomma($name), &decomma($next), &decomma($prevsub),
761       &decomma($current_up));
762
763     if ($name eq "-<hyphen>")    # Fudge for Texinfo
764       {
765       push(@ONESECTION,
766            "\@findex $name\n",
767            "\@unnumberedsubsec --- option\n");
768       push(@ONESECTION,
769            "This option consists of two consecutive hyphens. It appears in\n",
770            "the menu as \"-<hyphen>\" because otherwise Texinfo gets\n",
771            "confused with its cross-referencing.\n");
772       }
773     elsif ($name eq "-<hyphen>help")    # Fudge for Texinfo
774       {
775       push(@ONESECTION,
776            "\@findex $name\n",
777            "\@unnumberedsubsec ---help option\n");
778       push(@ONESECTION,
779            "This option consists of \"help\" preceded by two consecutive\n" .
780            "hyphens. It appears in the menu as \"-<hyphen>help\" because\n" .
781            "otherwise Texinfo gets confused with its cross-referencing.\n");
782       }
783     else
784       {
785       push(@ONESECTION,
786            "\@findex $name\n",
787            "\@unnumberedsubsec $name option\n");
788       }
789
790     $prevsub = $name;
791     }
792
793   # Then handle as text or directive
794
795   if (substr($_, 0, 1) eq ".")
796     { handle_directive(); }
797   else
798     {
799     while($nextline = shift(@SUBBUFFER))
800       {
801       last if $nextline =~ /^(\.|\s*$)/;
802       $_ .= $nextline;
803       }
804     push(@ONESECTION, handle_text($_));
805     $_ = $nextline;
806     last if !defined($_);
807     redo;
808     }
809   }
810
811 $processing_subsection = 0;
812 $section_pending = 1;
813 $current_up = $save_up;
814 }
815
816
817
818
819 ##################################################
820 #            Handle a single chapter             #
821 ##################################################
822
823 sub handle_chapter{
824 chop;
825 ($current_chapter) = /^\.chapter (.*)/;
826 $current_chapter = &dequote($current_chapter);
827
828 $current_chapter = $current_chapter;
829
830 my($tmp) = $current_chapter;
831 $tmp =~ s/\[\[\[\]\]\]/./;
832 print STDERR "processing chapter: $tmp\n";
833
834 # Remember the chapter name for the top-level menu
835
836 push(@chapter_list, $current_chapter);
837
838 # Open a temporary file to hold the chapter's text while collecting
839 # all its sections for a chapter-level menu.
840
841 $ONECHAPTER = "/tmp/ONECHAPTER.$$";
842 open(ONECHAPTER, ">$ONECHAPTER") || die "Can't open $ONECHAPTER for writing";
843
844 # Initialize for handling sections
845
846 @section_list = ();
847 %rewrite = ();
848 @ONESECTION = ();
849 @TOPSECTION = ();
850 undef $current_section;
851 undef $next_node;
852
853 $processing_subsection = 0;
854
855 $previous_node = $current_up = $current_chapter;
856 $section_pending = 0;
857
858 # Handle the .chapter directive as the first text of a section without
859 # a section title.
860
861 handle_directive();
862
863 # Loop, handling each section. Assume they are sufficiently short that
864 # we can buffer the text in store, in an array called ONESECTION, instead
865 # of thrashing yet another file.
866
867 while (<>)
868   {
869   last if /^\.chapter /;
870
871   # Handle a new section, preserving $_ (handle_text flattens it).
872   # It seems we cannot get a fullstop into a Texinfo node name; use a magic
873   # character string that gets turned back into a dot by the post-processing.
874
875   if (/^\.section\s+/)
876     {
877     $save = $_;
878     $section_name = $';
879     $section_name =~ s/(\s|\n)+$//;
880     $section_name =~ s/://;
881     $section_name = &handle_text($section_name);
882     flush_section();
883     push(@section_list, $section_name);
884     $current_section = $section_name;
885     $next_node = $section_name if !$next_node;
886     $section_pending = 0;
887     $_ = $save;
888     }
889
890   # The .startconf macro introduces a set of .conf's which must have
891   # their own local set of menus. Suspend processing the section while
892   # we sort out the menu and copy their data. This is all done in a
893   # subroutine that is shared with options.
894
895   elsif (/^\.startconf\s+(.*)/)
896     {
897     $confuse = $1; 
898     $confuse = &dequote($confuse); 
899     handle_subsection("conf");
900     next;
901     }
902
903   elsif (/^\.startoption/)
904     {
905     handle_subsection("option");
906     next;
907     }
908
909   # Deal with the actual data lines; if there's a section pending
910   # start a new section on hitting some text. We hope this happens
911   # only once per chapter...
912
913   if (substr($_, 0, 1) eq ".")
914     {
915     handle_directive();
916     }
917   else
918     {
919     while($nextline = <>)
920       {
921       last if $nextline =~ /^(\.|\s*$)/;
922       $_ .= $nextline;
923       }
924     if ($section_pending && !/^\s*$/)
925       {
926       $section_name = (defined $current_section)?
927         "$current_section (continued)" :
928         "$current_chapter (continued)" ;
929       flush_section();
930       push(@section_list, $section_name);
931       $current_section = $section_name;
932       $next_node = $section_name if !$next_node;
933       $section_pending = 0;
934       }
935
936     push(@ONESECTION, handle_text($_));
937     $_ = $nextline;
938     last if !defined($_);
939     redo;
940     }
941   }
942
943 # Flush any pending text, making its next field null.
944 # and fudging section_name for the final section of the previous.
945
946 $section_name = "";
947 flush_section();
948
949 # Set up section name as the start of the next chapter
950
951 $section_name = "Concept Index" if (!$doing_filter);
952
953 if (defined $_ && /^\.chapter (.*)/)
954   {
955   $section_name = $1;
956   $section_name = &dequote($section_name);
957   }
958 $next_node = $section_name;
959
960 # Write out the chapter to the CHAPTERS file, sticking the chapter
961 # menu after the text that came before the first section heading. This
962 # will always at least contain the chapter title.
963
964 printf CHAPTERS ("\@node %s, %s, %s, Top\n",
965   &decomma($current_chapter), &decomma($next_node),
966   &decomma($previous_chapter));
967
968 # The pre-section stuff; if we hit an @end menu line, it is the menu of
969 # a "subsection" before the first section. In that case, we need to put
970 # the chapter's menu one the end of it, and then resume with the rest of
971 # the TOPSECTION data afterwards. We also need to thread together this
972 # "subsection"s nodes because they are all at the same level under the
973 # chapter.
974
975 $in_menu = 0;
976 while(scalar(@TOPSECTION))
977   {
978   $s = shift(@TOPSECTION);
979   if ($s =~ /^\@end menu/)
980     {
981     $in_menu = 1;
982     last;
983     }
984   print CHAPTERS $s;
985   }
986
987 # Menu for sections
988
989 undef $next_actual_section;
990 undef $point_back;
991
992 if (scalar(@section_list))
993   {
994   print CHAPTERS "\n\@sp 2\n\@menu\n" if ! $in_menu;
995   $next_actual_section = $section_list[0];
996   for ($i = 0; $i < scalar(@section_list); $i++)
997     {
998     $section_name = $section_list[$i];
999     $section_name =~ s/\@sc\{([^}]*)\}/\U$1/g;
1000     print CHAPTERS "* ${section_name}::\n";
1001     }
1002   $in_menu = 1;
1003   }
1004 print CHAPTERS "\@end menu\n\n" if $in_menu;
1005
1006 # Remainder of topsection; we must arrange that the final @node in
1007 # it (which will have a blank "next" field) actually points on to
1008 # the next section, if any. If this happens, then the next section
1009 # must point back to the final @node.
1010
1011 while(scalar(@TOPSECTION))
1012   {
1013   $s = shift(@TOPSECTION);
1014   if ($next_actual_section && $s =~
1015          /^\@node\s+([^,]+),\s*,\s*([^,]*),\s*(.*)/)
1016     {
1017     my($t1, $t2, $t3) = ($1, $2, $3);    # So can be decomma'd
1018     printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1),
1019       &decomma($next_actual_section), &decomma($t2), &decomma($t3));
1020     $point_back = $1;
1021     }
1022   else { print CHAPTERS $s; }
1023   }
1024
1025 close(ONECHAPTER);
1026 open(ONECHAPTER, "$ONECHAPTER") || die "Can't open $ONECHAPTER for reading";
1027
1028 # While copying the chapter data, check for node references to empty
1029 # sections that got omitted and correct them, and correct the prev pointer
1030 # in the first node if necessary.
1031
1032 while ($buff = <ONECHAPTER>)
1033   {
1034   foreach $key (keys %rewrite)
1035     {
1036     $buff =~ s/$key/$rewrite{$key}/;
1037     }
1038   if ($point_back && $buff =~ /^\@node\s+([^,]+),\s*([^,]*),\s*([^,]*),\s*(.*)/)
1039     {
1040     my($t1, $t2, $t4) = ($1, $2, $4);   # so can be decomma'd
1041     printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1),
1042       &decomma($t2), &decomma($point_back), &decomma($t4));
1043     undef $point_back;
1044     }
1045   else { print CHAPTERS $buff; }
1046   }
1047
1048 $previous_chapter = $current_chapter;
1049
1050 close(ONECHAPTER);
1051 unlink($ONECHAPTER);
1052 }
1053
1054
1055
1056 ##################################################
1057 #                Main Program                    #
1058 ##################################################
1059
1060 # This is a two-pass algorithm. The first pass goes through and gets the
1061 # variable names for cross references. The second pass does the real work,
1062 # but we can't just read through doing the translation in one pass. We need
1063 # to know the list of chapters in order to build a top-level menu, and for
1064 # each chapter we need to know the sections in order to build a section
1065 # menu. Consequently, make use of temporary files to buffer things.
1066
1067 # This script is used for the filter document and the overview as well;
1068 # flags tell it if it is doing one of them.
1069
1070 $doing_filter = 0;
1071 $skip_else = 0;
1072 $in_itemize = 0;
1073 $lastwasitem = 0;
1074 $confuse = "";
1075
1076 $chapter_number = 0;
1077 $section_number = 0;
1078
1079 if ($#ARGV >= 0 && $ARGV[0] eq "-filter")
1080   {
1081   $doing_filter = 1;
1082   shift @ARGV;
1083   }
1084
1085 # First pass: Just fish out variable settings. Save the arguments so that
1086 # they can be reinstated for a second pass.
1087
1088 print STDERR "Scanning for references\n";
1089 @save_argv = @ARGV;
1090
1091 # Pick up any .set directives right at the very start
1092
1093 while (<>)
1094   {
1095   last if ! /^\.set\s+(\S+)\s+(.+)$/;
1096   $name = $1;
1097   $value = $2;
1098   $value =~ s/^\"?(.*?)\"?\s*$/$1/;
1099   $references{$name} = $value;
1100   }
1101
1102 # Now skip everything before the first .chapter except for
1103 # .index lines that set up indirections. Save these so that
1104 # the relevant index entries can be duplicated.
1105
1106 while (<>)
1107   {
1108   if (/^\.chapter\s+(.+)$/)
1109     {
1110     $chapter_number++;
1111     $section_number = 0;
1112     $current_chapter = $1;
1113     $current_chapter = $current_chapter;
1114     $current_section = "";
1115     last;
1116     }
1117
1118   if (/^\.index\s+([^\$]+)\s+\$it\{see\s+([^}]+)\}\s*$/)
1119     {
1120     $indirections{"$2"} = $1;
1121     }
1122   }
1123
1124 # Do the business
1125
1126 while (<>)
1127   {
1128   if (/^\.chapter\s+(.+)$/)
1129     {
1130     $current_chapter = $1;
1131     $current_chapter = &dequote($current_chapter);
1132     $current_section = "";
1133     }
1134   elsif (/^\.section\s+(.+)$/)
1135     {
1136     $current_section = $1;
1137     $current_section = &dequote($current_section);
1138     $current_section =~ s/://;
1139     }
1140   elsif (/^\.r?set\s+(\S+)\s+(.+)$/ && $1 ne "runningfoot")
1141     {
1142     $name = $1;
1143     $value = $2;
1144
1145     # Only set the first time. This handles a few special cases in part2
1146     # which is included in the filter text as well.
1147
1148     if (!exists($references{$name}))
1149       {
1150       $value =~ s/^\"?(.*?)\"?\s*$/$1/;
1151       $value =~ s/~~chapter\./~~chapter****/;
1152       $value =~ s/~~chapter/$current_chapter/;
1153       $value =~ s/~~section/$current_section/;
1154       $references{$name} = $value;
1155       }
1156     }
1157   }
1158
1159 $final_chapter = defined($current_chapter)? $current_chapter : "";
1160
1161 # Reinstate ARGV with the list of files and proceed to the main pass
1162
1163 @ARGV = @save_argv;
1164
1165 # $asis is set true when processing .display asis blocks, to stop
1166 # characters getting interpreted.
1167
1168 $asis = 0;
1169
1170 # $indisplay is set true while processing .display blocks, to stop
1171 # .newlines being handled therein (adding @* wrecks alignment)
1172
1173 $indisplay = 0;
1174
1175 # $tab is set to the value of the tab stop - only one stop is ever used
1176 # in the Exim source.
1177
1178 $tab = 0;
1179
1180 # Current driver name, for disambiguating nodes
1181
1182 $driver_name = "";
1183
1184 # $section_pending is set if a new section is to be started on hitting
1185 # any data lines.
1186
1187 $section_pending = 0;
1188
1189 # Open a file to buffer up the entire set of chapters
1190
1191 $CHAPTERS = "/tmp/CHAPTERS.$$";
1192 open(CHAPTERS, ">$CHAPTERS") || die "Can't open $CHAPTERS for writing";
1193
1194 # Skip everything before the first .chapter
1195
1196 while (<>) { last if /^\.chapter /; }
1197
1198 # Loop, handling each chapter
1199
1200 $current_up = "";
1201 $previous_chapter = "Top";
1202 $previous_node = "Top";
1203
1204 $chapter_number = 0;
1205 $section_number = 0;
1206
1207 while (defined ($_) && /^\.chapter /)
1208   {
1209   handle_chapter();
1210   }
1211
1212 # Output the stuff at the start of the file
1213
1214 print "\\input texinfo\n";
1215
1216 print "\@set{wmYear} 2003\n";
1217 print "\@set{wmAuthor} Philip Hazel\n";
1218 print "\@set{wmAuthor_email} <ph10\@\@cus.cam.ac.uk>\n";
1219 print "\@set{COPYRIGHT1} Copyright \@copyright{} \@value{wmYear} University of Cambridge\n";
1220
1221 print "\@c %**start of header\n";
1222
1223 if (!$doing_filter)
1224   {
1225   print "\@setfilename spec.info\n";
1226   print "\@settitle Exim Specification\n";
1227   }
1228 else
1229   {
1230   print "\@setfilename filter.info\n";
1231   print "\@settitle Exim Filter Specification\n";
1232   }
1233
1234 print "\@paragraphindent 0\n";
1235 print "\@c %**end of header\n\n";
1236
1237
1238 print "\@titlepage\n";
1239 print "\@title The Exim Mail Transfer Agent\n";
1240 print "\@author \@value{wmAuthor}\n";
1241
1242 print "\@page\n";
1243 print "\@vskip 0pt plus 1filll\n";
1244
1245 print "Permission is granted to make and distribute verbatim copies of this manual provided the\n";
1246 print "copyright notice and this permission notice are preserved on all copies.\n";
1247
1248 print "\@sp2\n";
1249 print "\@value{COPYRIGHT1}\@*\n";
1250
1251 print "\@end titlepage\n\n";
1252
1253 # Output the top-level node and its introductory blurb
1254
1255 print "\@node       Top,       $chapter_list[0], (dir), (dir)\n";
1256 print "\@top\n";
1257
1258 if (!$doing_filter)
1259 {
1260 print <<End;
1261 The Exim Mail Transfer Agent\@*
1262 ****************************
1263
1264 The specification of the Exim Mail Transfer Agent is converted mechanically
1265 into Texinfo format from its original marked-up source. Some typographic
1266 representations are changed, chapters and sections cannot be numbered, and
1267 Texinfo lacks the ability to mark updated parts of the specification with
1268 change bars.
1269
1270 Because the chapters and sections are unnumbered, cross references are set to
1271 their names. This makes the English a bit odd, with phrases like \`see chapter
1272 \"Retry configuration\"\' but it seemed very cumbersome to change this to \`see
1273 the chapter entitled \"Retry configuration\"\' each time.
1274
1275 Each chapter, section, and configuration option has been placed in a separate
1276 Texinfo node. Texinfo doesn\'t allow commas, colons, or apostrophes in node
1277 names, which is a rather nasty restriction. I have arranged not to use colons
1278 or apostrophes in section titles, but cannot bring myself to omit them from
1279 titles such as \"The foo, bar and baz commands\". For the corresponding node
1280 names I have just used multiple occurrences of \"and\", though it looks very
1281 ugly.
1282
1283 If a chapter or section continues after a list of configuration options that is
1284 not in a new section, a new node is started, using the chapter\'s or section\'s
1285 name plus \`(continued)\'. The \`Up\' operation from a section or configuration
1286 option returns to the start of the current chapter; the \`Up\' operation at a
1287 chapter start returns to the top of the document; the \`Up\' in a list of
1288 configuration options within a section returns to the top of that section.
1289
1290 A number of drivers have options with the same name, so they have been
1291 disambiguated by adding the name of the driver to its option names in order to
1292 create node names. Thus, for example, the specification of the \`command\'
1293 options of the \`lmtp\' and \`pipe\' transports are in nodes called \`command
1294 (lmtp)\' and \`command (pipe)\', respectively.
1295
1296 End
1297 }
1298
1299 else
1300 {
1301 print <<End;
1302 Filtering with the Exim Mail Transfer Agent\@*
1303 *******************************************
1304
1305 The specifications of the Exim Mail Transfer Agent\'s filtering facility is
1306 converted mechanically into Texinfo format from its original marked-up source.
1307 Some typographic representations are changed, chapters and sections cannot be
1308 numbered, and Texinfo lacks the ability to mark updated parts of the
1309 specification with change bars.
1310
1311 Because the chapters and sections are unnumbered, cross references are set to
1312 their names. This makes the English a bit odd, with phrases like \`see section
1313 \"Multiple personal mailboxes\"\' but it seemed very cumbersome to change this to
1314 \`see the section entitled \"Multiple personal mailboxes\"\' each time.
1315
1316 End
1317 }
1318
1319 # Output the top-level menu
1320
1321 print "\@menu\n";
1322
1323 while (scalar(@chapter_list))
1324   {
1325   $name = &decomma(shift(@chapter_list));
1326   print "* ${name}::\n";
1327   }
1328 print "* Concept Index::\n" if (!$doing_filter);
1329 print "\@end menu\n\n";
1330
1331 # Copy the chapters, then delete the temporary file
1332
1333 close(CHAPTERS);
1334 open(CHAPTERS, "$CHAPTERS") || die "Can't open $CHAPTERS for reading";
1335 print $buff while($buff = <CHAPTERS>);
1336 close(CHAPTERS);
1337 unlink($CHAPTERS);
1338
1339 # Output the finishing off stuff
1340
1341 if (!$doing_filter)
1342   {
1343   print "\@node Concept Index, , $final_chapter, Top\n";
1344   print "\@chapter Concept Index\n\@printindex cp\n";
1345   print "\@chapter Function Index\n\@printindex fn\n";
1346   }
1347 print "\@contents\n";
1348 print "\@bye\n";
1349
1350 # End