Commit | Line | Data |
---|---|---|
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 | ||
20 | sub unique { | |
21 | my($node) = $_[0]; | |
22 | if (defined $node_names{$node}) | |
23 | { | |
24 | $node_names{$node} += 1; | |
25 | $node = "$node ($node_names{$node})"; | |
26 | ||
27 | print STDERR "+++ $node\n"; | |
28 | ||
29 | } | |
30 | else | |
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 | ||
59 | sub 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 | ||
78 | sub dequote { | |
79 | if ($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 | ||
96 | sub get_next_line { | |
97 | if ($processing_subsection) | |
98 | { return $_ = shift @SUBBUFFER; } | |
99 | else | |
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 | ||
116 | sub handle_text { | |
117 | $_ = $_[0]; | |
118 | ||
119 | if ($asis) | |
120 | { | |
121 | $_ = dequote($_); | |
122 | s/(\{|\})/\@$1/g; | |
123 | return $_; | |
124 | } | |
125 | ||
126 | while (/~~/) | |
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 | ||
149 | s/\@\@/&&a/g; # @@ | |
150 | s/\@\\/&&b/g; # @\ | |
151 | s/\@</&&l/g; # @< | |
152 | s/\@>/&&g/g; # @> | |
153 | s/\@\{/&&c/g; # @{ | |
154 | s/\@\}/&&d/g; # @} | |
155 | s/\@#/&&s/g; # @# | |
156 | ||
157 | # Now remove all other @'s | |
158 | ||
159 | $_ = dequote($_); | |
160 | ||
161 | # Convert SGCAL markup | |
162 | ||
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 | |
171 | ||
172 | s/\$it\{([^\}]*?)\}/$1/g; # turn $it{xxx} into xxx | |
173 | ||
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 | |
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 | ||
181 | s/\[\$<\]/[]/g; # turn [$<] into [] | |
182 | s/&&b\$<\./&&b./g; # turn \$<. into \. (\ == &&b by now) | |
183 | s/(\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 | ||
189 | s/\}//g if !/\{/; | |
190 | ||
191 | # Any remaining {} must be escaped to prevent Texinfo from complaining | |
192 | ||
193 | s/(\{|\})/\@$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 | ||
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} | |
202 | ||
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 | |
209 | ||
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 * | |
216 | ||
217 | # Put back escaped SGCAL specials | |
218 | ||
219 | s/&&a/\@\@/g; | |
220 | s/&&b/\\/g; | |
221 | s/&&l/</g; | |
222 | s/&&g/>/g; | |
223 | s/&&c/\@{/g; | |
224 | s/&&rc/{/g; | |
225 | s/&&rd/}/g; | |
226 | s/&&d/\@}/g; | |
227 | s/&&s/#/g; | |
228 | ||
229 | # Remove any null flags ($$) | |
230 | ||
231 | s/\$\$//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 | ||
236 | s/^\$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 | ||
242 | s/^\$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 | ||
251 | while (/(^|.+?\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 | ||
291 | sub handle_directive { | |
292 | ||
293 | my($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 | ||
300 | if (/\.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 | ||
317 | elsif (/\.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 | ||
337 | elsif (/\.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 | ||
349 | elsif (/\.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 | ||
358 | elsif (/\.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 | ||
365 | elsif (/\.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 | ||
375 | elsif (/\.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 | ||
384 | elsif (/\.(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 | ||
400 | elsif (/\.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 | ||
415 | elsif (/\.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 | ||
422 | elsif (/^\.if\s+~~texinfo/) | |
423 | { | |
424 | $skip_else = 1; | |
425 | } | |
426 | ||
427 | # Ignore any other .if directives | |
428 | ||
429 | elsif (/\.if/) {} | |
430 | ||
431 | # Skip else part if flag set | |
432 | ||
433 | elsif (/\.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 | ||
441 | elsif (/\.fi/ || /\.else/) {} | |
442 | ||
443 | # Ignore .indent | |
444 | ||
445 | elsif (/\.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 | ||
452 | elsif (/\.(.?)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 | ||
496 | elsif (/\.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 | ||
511 | elsif (/\.nextp/) | |
512 | { | |
513 | push(@ONESECTION, "\n\@item\n"); | |
514 | } | |
515 | ||
516 | elsif (/\.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 | ||
527 | elsif (/\.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 | ||
536 | elsif (/\.endd/) | |
537 | { | |
538 | $asis = 0; | |
539 | $indisplay = 0; | |
540 | $endname = pop(@ENDLIST); | |
541 | push(@ONESECTION, "\@end $endname\n\n"); | |
542 | } | |
543 | ||
544 | elsif (/\.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 | ||
569 | elsif (/\.startitem/ || /\.enditem/) {} | |
570 | ||
571 | elsif (/\.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 | ||
593 | elsif (/\.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 | ||
603 | elsif (/\.em/) {} | |
604 | elsif (/\.nem/) {} | |
605 | ||
606 | # Just ignore any .(r)set directives pro tem (or maybe always!) | |
607 | ||
608 | elsif (/\.r?set/) {} | |
609 | ||
610 | # Ignore .space, .linelength, and .justify | |
611 | ||
612 | elsif (/\.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 | ||
618 | else | |
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 | ||
638 | sub 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 | ||
644 | my($skip) = 1; | |
645 | foreach $s (@ONESECTION) | |
646 | { | |
647 | if ($s !~ /^(\@cindex|\@section|\s*$)/) { $skip = 0; last } | |
648 | } | |
649 | ||
650 | if ($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 | ||
660 | if ($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 | } | |
669 | else | |
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 | ||
686 | sub handle_subsection{ | |
687 | my($type) = $_[0]; | |
688 | my($save_up) = $current_up; | |
689 | ||
690 | $current_up = $current_section? $current_section : $current_chapter; | |
691 | ||
692 | @sublist = (); | |
693 | @SUBBUFFER = (); | |
694 | ||
695 | while (<>) | |
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 | ||
728 | # I have gone for the horrid kludge of turning it into "-<hyhen>" | |
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 | ||
740 | push (@ONESECTION, "\n\@sp 2\n\@menu\n"); | |
741 | for ($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 | } | |
748 | push (@ONESECTION, "\@end menu\n\n"); | |
749 | ||
750 | $prevsub = $current_up; | |
751 | $processing_subsection = 1; | |
752 | while ($_ = 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 | ||
822 | sub handle_chapter{ | |
823 | chop; | |
824 | ($current_chapter) = /^\.chapter (.*)/; | |
825 | $current_chapter = &dequote($current_chapter); | |
826 | ||
827 | $current_chapter = $current_chapter; | |
828 | ||
829 | my($tmp) = $current_chapter; | |
830 | $tmp =~ s/\[\[\[\]\]\]/./; | |
831 | print STDERR "processing chapter: $tmp\n"; | |
832 | ||
833 | # Remember the chapter name for the top-level menu | |
834 | ||
835 | push(@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.$$"; | |
841 | open(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 = (); | |
849 | undef $current_section; | |
850 | undef $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 | ||
860 | handle_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 | ||
866 | while (<>) | |
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 = ""; | |
946 | flush_section(); | |
947 | ||
948 | # Set up section name as the start of the next chapter | |
949 | ||
950 | $section_name = "Concept Index" if (!$doing_filter); | |
951 | ||
952 | if (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 | ||
963 | printf 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; | |
975 | while(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 | ||
988 | undef $next_actual_section; | |
989 | undef $point_back; | |
990 | ||
991 | if (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 | } | |
1003 | print 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 | ||
1010 | while(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 | ||
1024 | close(ONECHAPTER); | |
1025 | open(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 | ||
1031 | while ($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 | ||
1049 | close(ONECHAPTER); | |
1050 | unlink($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 | ||
1078 | if ($#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 | ||
1087 | print STDERR "Scanning for references\n"; | |
1088 | @save_argv = @ARGV; | |
1089 | ||
1090 | # Pick up any .set directives right at the very start | |
1091 | ||
1092 | while (<>) | |
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 | ||
1105 | while (<>) | |
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 | ||
1125 | while (<>) | |
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.$$"; | |
1191 | open(CHAPTERS, ">$CHAPTERS") || die "Can't open $CHAPTERS for writing"; | |
1192 | ||
1193 | # Skip everything before the first .chapter | |
1194 | ||
1195 | while (<>) { 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 | ||
1206 | while (defined ($_) && /^\.chapter /) | |
1207 | { | |
1208 | handle_chapter(); | |
1209 | } | |
1210 | ||
1211 | # Output the stuff at the start of the file | |
1212 | ||
1213 | print "\\input texinfo\n"; | |
1214 | ||
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"; | |
1219 | ||
1220 | print "\@c %**start of header\n"; | |
1221 | ||
1222 | if (!$doing_filter) | |
1223 | { | |
1224 | print "\@setfilename spec.info\n"; | |
1225 | print "\@settitle Exim Specification\n"; | |
1226 | } | |
1227 | else | |
1228 | { | |
1229 | print "\@setfilename filter.info\n"; | |
1230 | print "\@settitle Exim Filter Specification\n"; | |
1231 | } | |
1232 | ||
1233 | print "\@paragraphindent 0\n"; | |
1234 | print "\@c %**end of header\n\n"; | |
1235 | ||
1236 | ||
1237 | print "\@titlepage\n"; | |
1238 | print "\@title The Exim Mail Transfer Agent\n"; | |
1239 | print "\@author \@value{wmAuthor}\n"; | |
1240 | ||
1241 | print "\@page\n"; | |
1242 | print "\@vskip 0pt plus 1filll\n"; | |
1243 | ||
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"; | |
1246 | ||
1247 | print "\@sp2\n"; | |
1248 | print "\@value{COPYRIGHT1}\@*\n"; | |
1249 | ||
1250 | print "\@end titlepage\n\n"; | |
1251 | ||
1252 | # Output the top-level node and its introductory blurb | |
1253 | ||
1254 | print "\@node Top, $chapter_list[0], (dir), (dir)\n"; | |
1255 | print "\@top\n"; | |
1256 | ||
1257 | if (!$doing_filter) | |
1258 | { | |
1259 | print <<End; | |
1260 | The Exim Mail Transfer Agent\@* | |
1261 | **************************** | |
1262 | ||
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 | |
1267 | change bars. | |
1268 | ||
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. | |
1273 | ||
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 | |
1280 | ugly. | |
1281 | ||
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. | |
1288 | ||
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. | |
1294 | ||
1295 | End | |
1296 | } | |
1297 | ||
1298 | else | |
1299 | { | |
1300 | print <<End; | |
1301 | Filtering with the Exim Mail Transfer Agent\@* | |
1302 | ******************************************* | |
1303 | ||
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. | |
1309 | ||
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. | |
1314 | ||
1315 | End | |
1316 | } | |
1317 | ||
1318 | # Output the top-level menu | |
1319 | ||
1320 | print "\@menu\n"; | |
1321 | ||
1322 | while (scalar(@chapter_list)) | |
1323 | { | |
1324 | $name = &decomma(shift(@chapter_list)); | |
1325 | print "* ${name}::\n"; | |
1326 | } | |
1327 | print "* Concept Index::\n" if (!$doing_filter); | |
1328 | print "\@end menu\n\n"; | |
1329 | ||
1330 | # Copy the chapters, then delete the temporary file | |
1331 | ||
1332 | close(CHAPTERS); | |
1333 | open(CHAPTERS, "$CHAPTERS") || die "Can't open $CHAPTERS for reading"; | |
1334 | print $buff while($buff = <CHAPTERS>); | |
1335 | close(CHAPTERS); | |
1336 | unlink($CHAPTERS); | |
1337 | ||
1338 | # Output the finishing off stuff | |
1339 | ||
1340 | if (!$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 | } | |
1346 | print "\@contents\n"; | |
1347 | print "\@bye\n"; | |
1348 | ||
1349 | # End |