Commit | Line | Data |
---|---|---|
495ae4b0 PH |
1 | #! /usr/bin/perl -w |
2 | # $Cambridge: exim/doc/doc-scripts/g2t,v 1.1 2004/10/07 15:04: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/) | |
896 | { | |
897 | handle_subsection("conf"); | |
898 | next; | |
899 | } | |
900 | ||
901 | elsif (/^\.startoption/) | |
902 | { | |
903 | handle_subsection("option"); | |
904 | next; | |
905 | } | |
906 | ||
907 | # Deal with the actual data lines; if there's a section pending | |
908 | # start a new section on hitting some text. We hope this happens | |
909 | # only once per chapter... | |
910 | ||
911 | if (substr($_, 0, 1) eq ".") | |
912 | { | |
913 | handle_directive(); | |
914 | } | |
915 | else | |
916 | { | |
917 | while($nextline = <>) | |
918 | { | |
919 | last if $nextline =~ /^(\.|\s*$)/; | |
920 | $_ .= $nextline; | |
921 | } | |
922 | if ($section_pending && !/^\s*$/) | |
923 | { | |
924 | $section_name = (defined $current_section)? | |
925 | "$current_section (continued)" : | |
926 | "$current_chapter (continued)" ; | |
927 | flush_section(); | |
928 | push(@section_list, $section_name); | |
929 | $current_section = $section_name; | |
930 | $next_node = $section_name if !$next_node; | |
931 | $section_pending = 0; | |
932 | } | |
933 | ||
934 | push(@ONESECTION, handle_text($_)); | |
935 | $_ = $nextline; | |
936 | last if !defined($_); | |
937 | redo; | |
938 | } | |
939 | } | |
940 | ||
941 | # Flush any pending text, making its next field null. | |
942 | # and fudging section_name for the final section of the previous. | |
943 | ||
944 | $section_name = ""; | |
945 | flush_section(); | |
946 | ||
947 | # Set up section name as the start of the next chapter | |
948 | ||
949 | $section_name = "Concept Index" if (!$doing_filter); | |
950 | ||
951 | if (defined $_ && /^\.chapter (.*)/) | |
952 | { | |
953 | $section_name = $1; | |
954 | $section_name = &dequote($section_name); | |
955 | } | |
956 | $next_node = $section_name; | |
957 | ||
958 | # Write out the chapter to the CHAPTERS file, sticking the chapter | |
959 | # menu after the text that came before the first section heading. This | |
960 | # will always at least contain the chapter title. | |
961 | ||
962 | printf CHAPTERS ("\@node %s, %s, %s, Top\n", | |
963 | &decomma($current_chapter), &decomma($next_node), | |
964 | &decomma($previous_chapter)); | |
965 | ||
966 | # The pre-section stuff; if we hit an @end menu line, it is the menu of | |
967 | # a "subsection" before the first section. In that case, we need to put | |
968 | # the chapter's menu one the end of it, and then resume with the rest of | |
969 | # the TOPSECTION data afterwards. We also need to thread together this | |
970 | # "subsection"s nodes because they are all at the same level under the | |
971 | # chapter. | |
972 | ||
973 | $in_menu = 0; | |
974 | while(scalar(@TOPSECTION)) | |
975 | { | |
976 | $s = shift(@TOPSECTION); | |
977 | if ($s =~ /^\@end menu/) | |
978 | { | |
979 | $in_menu = 1; | |
980 | last; | |
981 | } | |
982 | print CHAPTERS $s; | |
983 | } | |
984 | ||
985 | # Menu for sections | |
986 | ||
987 | undef $next_actual_section; | |
988 | undef $point_back; | |
989 | ||
990 | if (scalar(@section_list)) | |
991 | { | |
992 | print CHAPTERS "\n\@sp 2\n\@menu\n" if ! $in_menu; | |
993 | $next_actual_section = $section_list[0]; | |
994 | for ($i = 0; $i < scalar(@section_list); $i++) | |
995 | { | |
996 | $section_name = $section_list[$i]; | |
997 | $section_name =~ s/\@sc\{([^}]*)\}/\U$1/g; | |
998 | print CHAPTERS "* ${section_name}::\n"; | |
999 | } | |
1000 | $in_menu = 1; | |
1001 | } | |
1002 | print CHAPTERS "\@end menu\n\n" if $in_menu; | |
1003 | ||
1004 | # Remainder of topsection; we must arrange that the final @node in | |
1005 | # it (which will have a blank "next" field) actually points on to | |
1006 | # the next section, if any. If this happens, then the next section | |
1007 | # must point back to the final @node. | |
1008 | ||
1009 | while(scalar(@TOPSECTION)) | |
1010 | { | |
1011 | $s = shift(@TOPSECTION); | |
1012 | if ($next_actual_section && $s =~ | |
1013 | /^\@node\s+([^,]+),\s*,\s*([^,]*),\s*(.*)/) | |
1014 | { | |
1015 | my($t1, $t2, $t3) = ($1, $2, $3); # So can be decomma'd | |
1016 | printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1), | |
1017 | &decomma($next_actual_section), &decomma($t2), &decomma($t3)); | |
1018 | $point_back = $1; | |
1019 | } | |
1020 | else { print CHAPTERS $s; } | |
1021 | } | |
1022 | ||
1023 | close(ONECHAPTER); | |
1024 | open(ONECHAPTER, "$ONECHAPTER") || die "Can't open $ONECHAPTER for reading"; | |
1025 | ||
1026 | # While copying the chapter data, check for node references to empty | |
1027 | # sections that got omitted and correct them, and correct the prev pointer | |
1028 | # in the first node if necessary. | |
1029 | ||
1030 | while ($buff = <ONECHAPTER>) | |
1031 | { | |
1032 | foreach $key (keys %rewrite) | |
1033 | { | |
1034 | $buff =~ s/$key/$rewrite{$key}/; | |
1035 | } | |
1036 | if ($point_back && $buff =~ /^\@node\s+([^,]+),\s*([^,]*),\s*([^,]*),\s*(.*)/) | |
1037 | { | |
1038 | my($t1, $t2, $t4) = ($1, $2, $4); # so can be decomma'd | |
1039 | printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1), | |
1040 | &decomma($t2), &decomma($point_back), &decomma($t4)); | |
1041 | undef $point_back; | |
1042 | } | |
1043 | else { print CHAPTERS $buff; } | |
1044 | } | |
1045 | ||
1046 | $previous_chapter = $current_chapter; | |
1047 | ||
1048 | close(ONECHAPTER); | |
1049 | unlink($ONECHAPTER); | |
1050 | } | |
1051 | ||
1052 | ||
1053 | ||
1054 | ################################################## | |
1055 | # Main Program # | |
1056 | ################################################## | |
1057 | ||
1058 | # This is a two-pass algorithm. The first pass goes through and gets the | |
1059 | # variable names for cross references. The second pass does the real work, | |
1060 | # but we can't just read through doing the translation in one pass. We need | |
1061 | # to know the list of chapters in order to build a top-level menu, and for | |
1062 | # each chapter we need to know the sections in order to build a section | |
1063 | # menu. Consequently, make use of temporary files to buffer things. | |
1064 | ||
1065 | # This script is used for the filter document and the overview as well; | |
1066 | # flags tell it if it is doing one of them. | |
1067 | ||
1068 | $doing_filter = 0; | |
1069 | $skip_else = 0; | |
1070 | $in_itemize = 0; | |
1071 | $lastwasitem = 0; | |
1072 | ||
1073 | $chapter_number = 0; | |
1074 | $section_number = 0; | |
1075 | ||
1076 | if ($#ARGV >= 0 && $ARGV[0] eq "-filter") | |
1077 | { | |
1078 | $doing_filter = 1; | |
1079 | shift @ARGV; | |
1080 | } | |
1081 | ||
1082 | # First pass: Just fish out variable settings. Save the arguments so that | |
1083 | # they can be reinstated for a second pass. | |
1084 | ||
1085 | print STDERR "Scanning for references\n"; | |
1086 | @save_argv = @ARGV; | |
1087 | ||
1088 | # Pick up any .set directives right at the very start | |
1089 | ||
1090 | while (<>) | |
1091 | { | |
1092 | last if ! /^\.set\s+(\S+)\s+(.+)$/; | |
1093 | $name = $1; | |
1094 | $value = $2; | |
1095 | $value =~ s/^\"?(.*?)\"?\s*$/$1/; | |
1096 | $references{$name} = $value; | |
1097 | } | |
1098 | ||
1099 | # Now skip everything before the first .chapter except for | |
1100 | # .index lines that set up indirections. Save these so that | |
1101 | # the relevant index entries can be duplicated. | |
1102 | ||
1103 | while (<>) | |
1104 | { | |
1105 | if (/^\.chapter\s+(.+)$/) | |
1106 | { | |
1107 | $chapter_number++; | |
1108 | $section_number = 0; | |
1109 | $current_chapter = $1; | |
1110 | $current_chapter = $current_chapter; | |
1111 | $current_section = ""; | |
1112 | last; | |
1113 | } | |
1114 | ||
1115 | if (/^\.index\s+([^\$]+)\s+\$it\{see\s+([^}]+)\}\s*$/) | |
1116 | { | |
1117 | $indirections{"$2"} = $1; | |
1118 | } | |
1119 | } | |
1120 | ||
1121 | # Do the business | |
1122 | ||
1123 | while (<>) | |
1124 | { | |
1125 | if (/^\.chapter\s+(.+)$/) | |
1126 | { | |
1127 | $current_chapter = $1; | |
1128 | $current_chapter = &dequote($current_chapter); | |
1129 | $current_section = ""; | |
1130 | } | |
1131 | elsif (/^\.section\s+(.+)$/) | |
1132 | { | |
1133 | $current_section = $1; | |
1134 | $current_section = &dequote($current_section); | |
1135 | $current_section =~ s/://; | |
1136 | } | |
1137 | elsif (/^\.r?set\s+(\S+)\s+(.+)$/ && $1 ne "runningfoot") | |
1138 | { | |
1139 | $name = $1; | |
1140 | $value = $2; | |
1141 | ||
1142 | # Only set the first time. This handles a few special cases in part2 | |
1143 | # which is included in the filter text as well. | |
1144 | ||
1145 | if (!exists($references{$name})) | |
1146 | { | |
1147 | $value =~ s/^\"?(.*?)\"?\s*$/$1/; | |
1148 | $value =~ s/~~chapter\./~~chapter****/; | |
1149 | $value =~ s/~~chapter/$current_chapter/; | |
1150 | $value =~ s/~~section/$current_section/; | |
1151 | $references{$name} = $value; | |
1152 | } | |
1153 | } | |
1154 | } | |
1155 | ||
1156 | $final_chapter = defined($current_chapter)? $current_chapter : ""; | |
1157 | ||
1158 | # Reinstate ARGV with the list of files and proceed to the main pass | |
1159 | ||
1160 | @ARGV = @save_argv; | |
1161 | ||
1162 | # $asis is set true when processing .display asis blocks, to stop | |
1163 | # characters getting interpreted. | |
1164 | ||
1165 | $asis = 0; | |
1166 | ||
1167 | # $indisplay is set true while processing .display blocks, to stop | |
1168 | # .newlines being handled therein (adding @* wrecks alignment) | |
1169 | ||
1170 | $indisplay = 0; | |
1171 | ||
1172 | # $tab is set to the value of the tab stop - only one stop is ever used | |
1173 | # in the Exim source. | |
1174 | ||
1175 | $tab = 0; | |
1176 | ||
1177 | # Current driver name, for disambiguating nodes | |
1178 | ||
1179 | $driver_name = ""; | |
1180 | ||
1181 | # $section_pending is set if a new section is to be started on hitting | |
1182 | # any data lines. | |
1183 | ||
1184 | $section_pending = 0; | |
1185 | ||
1186 | # Open a file to buffer up the entire set of chapters | |
1187 | ||
1188 | $CHAPTERS = "/tmp/CHAPTERS.$$"; | |
1189 | open(CHAPTERS, ">$CHAPTERS") || die "Can't open $CHAPTERS for writing"; | |
1190 | ||
1191 | # Skip everything before the first .chapter | |
1192 | ||
1193 | while (<>) { last if /^\.chapter /; } | |
1194 | ||
1195 | # Loop, handling each chapter | |
1196 | ||
1197 | $current_up = ""; | |
1198 | $previous_chapter = "Top"; | |
1199 | $previous_node = "Top"; | |
1200 | ||
1201 | $chapter_number = 0; | |
1202 | $section_number = 0; | |
1203 | ||
1204 | while (defined ($_) && /^\.chapter /) | |
1205 | { | |
1206 | handle_chapter(); | |
1207 | } | |
1208 | ||
1209 | # Output the stuff at the start of the file | |
1210 | ||
1211 | print "\\input texinfo\n"; | |
1212 | ||
1213 | print "\@set{wmYear} 2003\n"; | |
1214 | print "\@set{wmAuthor} Philip Hazel\n"; | |
1215 | print "\@set{wmAuthor_email} <ph10\@\@cus.cam.ac.uk>\n"; | |
1216 | print "\@set{COPYRIGHT1} Copyright \@copyright{} \@value{wmYear} University of Cambridge\n"; | |
1217 | ||
1218 | print "\@c %**start of header\n"; | |
1219 | ||
1220 | if (!$doing_filter) | |
1221 | { | |
1222 | print "\@setfilename spec.info\n"; | |
1223 | print "\@settitle Exim Specification\n"; | |
1224 | } | |
1225 | else | |
1226 | { | |
1227 | print "\@setfilename filter.info\n"; | |
1228 | print "\@settitle Exim Filter Specification\n"; | |
1229 | } | |
1230 | ||
1231 | print "\@paragraphindent 0\n"; | |
1232 | print "\@c %**end of header\n\n"; | |
1233 | ||
1234 | ||
1235 | print "\@titlepage\n"; | |
1236 | print "\@title The Exim Mail Transfer Agent\n"; | |
1237 | print "\@author \@value{wmAuthor}\n"; | |
1238 | ||
1239 | print "\@page\n"; | |
1240 | print "\@vskip 0pt plus 1filll\n"; | |
1241 | ||
1242 | print "Permission is granted to make and distribute verbatim copies of this manual provided the\n"; | |
1243 | print "copyright notice and this permission notice are preserved on all copies.\n"; | |
1244 | ||
1245 | print "\@sp2\n"; | |
1246 | print "\@value{COPYRIGHT1}\@*\n"; | |
1247 | ||
1248 | print "\@end titlepage\n\n"; | |
1249 | ||
1250 | # Output the top-level node and its introductory blurb | |
1251 | ||
1252 | print "\@node Top, $chapter_list[0], (dir), (dir)\n"; | |
1253 | print "\@top\n"; | |
1254 | ||
1255 | if (!$doing_filter) | |
1256 | { | |
1257 | print <<End; | |
1258 | The Exim Mail Transfer Agent\@* | |
1259 | **************************** | |
1260 | ||
1261 | The specification of the Exim Mail Transfer Agent is converted mechanically | |
1262 | into Texinfo format from its original marked-up source. Some typographic | |
1263 | representations are changed, chapters and sections cannot be numbered, and | |
1264 | Texinfo lacks the ability to mark updated parts of the specification with | |
1265 | change bars. | |
1266 | ||
1267 | Because the chapters and sections are unnumbered, cross references are set to | |
1268 | their names. This makes the English a bit odd, with phrases like \`see chapter | |
1269 | \"Retry configuration\"\' but it seemed very cumbersome to change this to \`see | |
1270 | the chapter entitled \"Retry configuration\"\' each time. | |
1271 | ||
1272 | Each chapter, section, and configuration option has been placed in a separate | |
1273 | Texinfo node. Texinfo doesn\'t allow commas, colons, or apostrophes in node | |
1274 | names, which is a rather nasty restriction. I have arranged not to use colons | |
1275 | or apostrophes in section titles, but cannot bring myself to omit them from | |
1276 | titles such as \"The foo, bar and baz commands\". For the corresponding node | |
1277 | names I have just used multiple occurrences of \"and\", though it looks very | |
1278 | ugly. | |
1279 | ||
1280 | If a chapter or section continues after a list of configuration options that is | |
1281 | not in a new section, a new node is started, using the chapter\'s or section\'s | |
1282 | name plus \`(continued)\'. The \`Up\' operation from a section or configuration | |
1283 | option returns to the start of the current chapter; the \`Up\' operation at a | |
1284 | chapter start returns to the top of the document; the \`Up\' in a list of | |
1285 | configuration options within a section returns to the top of that section. | |
1286 | ||
1287 | A number of drivers have options with the same name, so they have been | |
1288 | disambiguated by adding the name of the driver to its option names in order to | |
1289 | create node names. Thus, for example, the specification of the \`command\' | |
1290 | options of the \`lmtp\' and \`pipe\' transports are in nodes called \`command | |
1291 | (lmtp)\' and \`command (pipe)\', respectively. | |
1292 | ||
1293 | End | |
1294 | } | |
1295 | ||
1296 | else | |
1297 | { | |
1298 | print <<End; | |
1299 | Filtering with the Exim Mail Transfer Agent\@* | |
1300 | ******************************************* | |
1301 | ||
1302 | The specifications of the Exim Mail Transfer Agent\'s filtering facility is | |
1303 | converted mechanically into Texinfo format from its original marked-up source. | |
1304 | Some typographic representations are changed, chapters and sections cannot be | |
1305 | numbered, and Texinfo lacks the ability to mark updated parts of the | |
1306 | specification with change bars. | |
1307 | ||
1308 | Because the chapters and sections are unnumbered, cross references are set to | |
1309 | their names. This makes the English a bit odd, with phrases like \`see section | |
1310 | \"Multiple personal mailboxes\"\' but it seemed very cumbersome to change this to | |
1311 | \`see the section entitled \"Multiple personal mailboxes\"\' each time. | |
1312 | ||
1313 | End | |
1314 | } | |
1315 | ||
1316 | # Output the top-level menu | |
1317 | ||
1318 | print "\@menu\n"; | |
1319 | ||
1320 | while (scalar(@chapter_list)) | |
1321 | { | |
1322 | $name = &decomma(shift(@chapter_list)); | |
1323 | print "* ${name}::\n"; | |
1324 | } | |
1325 | print "* Concept Index::\n" if (!$doing_filter); | |
1326 | print "\@end menu\n\n"; | |
1327 | ||
1328 | # Copy the chapters, then delete the temporary file | |
1329 | ||
1330 | close(CHAPTERS); | |
1331 | open(CHAPTERS, "$CHAPTERS") || die "Can't open $CHAPTERS for reading"; | |
1332 | print $buff while($buff = <CHAPTERS>); | |
1333 | close(CHAPTERS); | |
1334 | unlink($CHAPTERS); | |
1335 | ||
1336 | # Output the finishing off stuff | |
1337 | ||
1338 | if (!$doing_filter) | |
1339 | { | |
1340 | print "\@node Concept Index, , $final_chapter, Top\n"; | |
1341 | print "\@chapter Concept Index\n\@printindex cp\n"; | |
1342 | print "\@chapter Function Index\n\@printindex fn\n"; | |
1343 | } | |
1344 | print "\@contents\n"; | |
1345 | print "\@bye\n"; | |
1346 | ||
1347 | # End |