Fix regular expression at doc/rdsrc.pl
[platform/upstream/nasm.git] / doc / rdsrc.pl
1 #!/usr/bin/perl
2 ## --------------------------------------------------------------------------
3 ##   
4 ##   Copyright 1996-2009 The NASM Authors - All Rights Reserved
5 ##   See the file AUTHORS included with the NASM distribution for
6 ##   the specific copyright holders.
7 ##
8 ##   Redistribution and use in source and binary forms, with or without
9 ##   modification, are permitted provided that the following
10 ##   conditions are met:
11 ##
12 ##   * Redistributions of source code must retain the above copyright
13 ##     notice, this list of conditions and the following disclaimer.
14 ##   * Redistributions in binary form must reproduce the above
15 ##     copyright notice, this list of conditions and the following
16 ##     disclaimer in the documentation and/or other materials provided
17 ##     with the distribution.
18 ##     
19 ##     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
20 ##     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
21 ##     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 ##     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 ##     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 ##     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 ##     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ##     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 ##     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 ##     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 ##     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 ##     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31 ##     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 ##
33 ## --------------------------------------------------------------------------
34
35
36 # Read the source-form of the NASM manual and generate the various
37 # output forms.
38
39 # TODO:
40 #
41 # Ellipsis support would be nice.
42
43 # Source-form features:
44 # ---------------------
45 #
46 # Bullet \b
47 #   Bullets the paragraph. Rest of paragraph is indented to cope. In
48 #   HTML, consecutive groups of bulleted paragraphs become unordered
49 #   lists.
50 #
51 # Emphasis \e{foobar}
52 #   produces `_foobar_' in text and italics in HTML, PS, RTF
53 #
54 # Inline code \c{foobar}
55 #   produces ``foobar'' in text, and fixed-pitch font in HTML, PS, RTF
56 #
57 # Display code
58 # \c  line one
59 # \c   line two
60 #   produces fixed-pitch font where appropriate, and doesn't break
61 #   pages except sufficiently far into the middle of a display.
62 #
63 # Chapter, header and subheader
64 # \C{intro} Introduction
65 # \H{whatsnasm} What is NASM?
66 # \S{free} NASM Is Free
67 #   dealt with as appropriate. Chapters begin on new sides, possibly
68 #   even new _pages_. (Sub)?headers are good places to begin new
69 #   pages. Just _after_ a (sub)?header isn't.
70 #   The keywords can be substituted with \K and \k.
71 #
72 # Keyword \K{cintro} \k{cintro}
73 #   Expands to `Chapter 1', `Section 1.1', `Section 1.1.1'. \K has an
74 #   initial capital whereas \k doesn't. In HTML, will produce
75 #   hyperlinks.
76 #
77 # Web link \W{http://foobar/}{text} or \W{mailto:me@here}\c{me@here}
78 #   the \W prefix is ignored except in HTML; in HTML the last part
79 #   becomes a hyperlink to the first part.
80 #
81 # Literals \{ \} \\
82 #   In case it's necessary, they expand to the real versions.
83 #
84 # Nonbreaking hyphen \-
85 #   Need more be said?
86 #
87 # Source comment \#
88 #   Causes everything after it on the line to be ignored by the
89 #   source-form processor.
90 #
91 # Indexable word \i{foobar} (or \i\e{foobar} or \i\c{foobar}, equally)
92 #   makes word appear in index, referenced to that point
93 #   \i\c comes up in code style even in the index; \i\e doesn't come
94 #   up in emphasised style.
95 #
96 # Indexable non-displayed word \I{foobar} or \I\c{foobar}
97 #   just as \i{foobar} except that nothing is displayed for it
98 #
99 # Index rewrite
100 # \IR{foobar} \c{foobar} operator, uses of
101 #   tidies up the appearance in the index of something the \i or \I
102 #   operator was applied to
103 #
104 # Index alias
105 # \IA{foobar}{bazquux}
106 #   aliases one index tag (as might be supplied to \i or \I) to
107 #   another, so that \I{foobar} has the effect of \I{bazquux}, and
108 #   \i{foobar} has the effect of \I{bazquux}foobar
109 #
110 # Metadata
111 # \M{key}{something}
112 #   defines document metadata, such as authorship, title and copyright;
113 #   different output formats use this differently.
114 #
115 # Include subfile
116 # \&{filename}
117 #  Includes filename. Recursion is allowed.
118 #
119  
120 use IO::File;
121
122 $diag = 1, shift @ARGV if $ARGV[0] eq "-d";
123
124 ($out_format) = @ARGV;
125
126 $| = 1;
127
128 $tstruct_previtem = $node = "Top";
129 $nodes = ($node);
130 $tstruct_level{$tstruct_previtem} = 0;
131 $tstruct_last[$tstruct_level{$tstruct_previtem}] = $tstruct_previtem;
132 $MAXLEVEL = 10;  # really 3, but play safe ;-)
133
134 # Read the file; pass a paragraph at a time to the paragraph processor.
135 print "Reading input...";
136 $pname = "para000000";
137 @pnames = @pflags = ();
138 $para = undef;
139 while (defined($_ = <STDIN>)) {
140   $_ = &untabify($_);
141   &check_include($_);
142 }
143 &got_para($para);
144 print "done.\n";
145
146 # Now we've read in the entire document and we know what all the
147 # heading keywords refer to. Go through and fix up the \k references.
148 print "Fixing up cross-references...";
149 &fixup_xrefs;
150 print "done.\n";
151
152 # Sort the index tags, according to the slightly odd order I've decided on.
153 print "Sorting index tags...";
154 &indexsort;
155 print "done.\n";
156
157 if ($diag) {
158   print "Writing index-diagnostic file...";
159   &indexdiag;
160   print "done.\n";
161 }
162
163 # OK. Write out the various output files.
164 if ($out_format eq 'txt') {
165     print "Producing text output: ";
166     &write_txt;
167     print "done.\n";
168 } elsif ($out_format eq 'html') {
169     print "Producing HTML output: ";
170     &write_html;
171     print "done.\n";
172 } elsif ($out_format eq 'texi') {
173     print "Producing Texinfo output: ";
174     &write_texi;
175     print "done.\n";
176 } elsif ($out_format eq 'hlp') {
177     print "Producing WinHelp output: ";
178     &write_hlp;
179     print "done.\n";
180 } elsif ($out_format eq 'dip') {
181     print "Producing Documentation Intermediate Paragraphs: ";
182     &write_dip;
183     print "done.\n";
184 } else {
185     die "$0: unknown output format: $out_format\n";
186 }
187
188 sub untabify($) {
189   my($s) = @_;
190   my $o = '';
191   my($c, $i, $p);
192
193   $p = 0;
194   for ($i = 0; $i < length($s); $i++) {
195     $c = substr($s, $i, 1);
196     if ($c eq "\t") {
197       do {
198         $o .= ' ';
199         $p++;
200       } while ($p & 7);
201     } else {
202       $o .= $c;
203       $p++;
204     }
205   }
206   return $o;
207 }
208 sub check_include {
209   local $_ = shift;
210   if (/\\& (\S+)/) {
211      &include($1);
212   } else {
213      &get_para($_);
214   }
215 }
216 sub get_para($_) {
217   chomp;
218   if (!/\S/ || /^\\(IA|IR|M)/) { # special case: \IA \IR \M imply new-paragraph
219     &got_para($para);
220     $para = undef;
221   }
222   if (/\S/) {
223     s/\\#.*$//; # strip comments
224     $para .= " " . $_;
225   }
226 }
227 sub include {
228   my $name = shift;
229   my $F = IO::File->new($name)
230      or die "Cannot open $name: $!";
231   while (<$F>) {
232      &check_include($_);
233   }
234 }
235 sub got_para {
236   local ($_) = @_;
237   my $pflags = "", $i, $w, $l, $t;
238   return if !/\S/;
239
240   @$pname = ();
241
242   # Strip off _leading_ spaces, then determine type of paragraph.
243   s/^\s*//;
244   $irewrite = undef;
245   if (/^\\c[^{]/) {
246     # A code paragraph. The paragraph-array will contain the simple
247     # strings which form each line of the paragraph.
248     $pflags = "code";
249     while (/^\\c (([^\\]|\\[^c])*)(.*)$/) {
250       $l = $1;
251       $_ = $3;
252       $l =~ s/\\\{/{/g;
253       $l =~ s/\\\}/}/g;
254       $l =~ s/\\\\/\\/g;
255       push @$pname, $l;
256     }
257     $_ = ''; # suppress word-by-word code
258   } elsif (/^\\C/) {
259     # A chapter heading. Define the keyword and allocate a chapter
260     # number.
261     $cnum++;
262     $hnum = 0;
263     $snum = 0;
264     $xref = "chapter-$cnum";
265     $pflags = "chap $cnum :$xref";
266     die "badly formatted chapter heading: $_\n" if !/^\\C\{([^}]*)\}\s*(.*)$/;
267     $refs{$1} = "chapter $cnum";
268     $node = "Chapter $cnum";
269     &add_item($node, 1);
270     $xrefnodes{$node} = $xref; $nodexrefs{$xref} = $node;
271     $xrefs{$1} = $xref;
272     $_ = $2;
273     # the standard word-by-word code will happen next
274   } elsif (/^\\A/) {
275     # An appendix heading. Define the keyword and allocate an appendix
276     # letter.
277     $cnum++;
278     $cnum = 'A' if $cnum =~ /[0-9]+/;
279     $hnum = 0;
280     $snum = 0;
281     $xref = "appendix-$cnum";
282     $pflags = "appn $cnum :$xref";
283     die "badly formatted appendix heading: $_\n" if !/^\\A\{([^}]*)\}\s*(.*)$/;
284     $refs{$1} = "appendix $cnum";
285     $node = "Appendix $cnum";
286     &add_item($node, 1);
287     $xrefnodes{$node} = $xref; $nodexrefs{$xref} = $node;
288     $xrefs{$1} = $xref;
289     $_ = $2;
290     # the standard word-by-word code will happen next
291   } elsif (/^\\H/) {
292     # A major heading. Define the keyword and allocate a section number.
293     $hnum++;
294     $snum = 0;
295     $xref = "section-$cnum.$hnum";
296     $pflags = "head $cnum.$hnum :$xref";
297     die "badly formatted heading: $_\n" if !/^\\[HP]\{([^\}]*)\}\s*(.*)$/;
298     $refs{$1} = "section $cnum.$hnum";
299     $node = "Section $cnum.$hnum";
300     &add_item($node, 2);
301     $xrefnodes{$node} = $xref; $nodexrefs{$xref} = $node;
302     $xrefs{$1} = $xref;
303     $_ = $2;
304     # the standard word-by-word code will happen next
305   } elsif (/^\\S/) {
306     # A sub-heading. Define the keyword and allocate a section number.
307     $snum++;
308     $xref = "section-$cnum.$hnum.$snum";
309     $pflags = "subh $cnum.$hnum.$snum :$xref";
310     die "badly formatted subheading: $_\n" if !/^\\S\{([^}]*)\}\s*(.*)$/;
311     $refs{$1} = "section $cnum.$hnum.$snum";
312     $node = "Section $cnum.$hnum.$snum";
313     &add_item($node, 3);
314     $xrefnodes{$node} = $xref; $nodexrefs{$xref} = $node;
315     $xrefs{$1} = $xref;
316     $_ = $2;
317     # the standard word-by-word code will happen next
318   } elsif (/^\\IR/) {
319     # An index-rewrite.
320     die "badly formatted index rewrite: $_\n" if !/^\\IR\{([^\}]*)\}\s*(.*)$/;
321     $irewrite = $1;
322     $_ = $2;
323     # the standard word-by-word code will happen next
324   } elsif (/^\\IA/) {
325     # An index-alias.
326     die "badly formatted index alias: $_\n" if !/^\\IA\{([^}]*)\}\{([^\}]*)\}\s*$/;
327     $idxalias{$1} = $2;
328     return; # avoid word-by-word code
329   } elsif (/^\\M/) {
330     # Metadata
331     die "badly formed metadata: $_\n" if !/^\\M\{([^\}]*)\}\{([^\}]*)\}\s*$/;
332     $metadata{$1} = $2;
333     return; # avoid word-by-word code
334   } elsif (/^\\b/) {
335     # A bulleted paragraph. Strip off the initial \b and let the
336     # word-by-word code take care of the rest.
337     $pflags = "bull";
338     s/^\\b\s*//;
339   } else {
340     # A normal paragraph. Just set $pflags: the word-by-word code does
341     # the rest.
342     $pflags = "norm";
343   }
344
345   # The word-by-word code: unless @$pname is already defined (which it
346   # will be in the case of a code paragraph), split the paragraph up
347   # into words and push each on @$pname.
348   #
349   # Each thing pushed on @$pname should have a two-character type
350   # code followed by the text.
351   #
352   # Type codes are:
353   # "n " for normal
354   # "da" for a dash
355   # "es" for first emphasised word in emphasised bit
356   # "e " for emphasised in mid-emphasised-bit
357   # "ee" for last emphasised word in emphasised bit
358   # "eo" for single (only) emphasised word
359   # "c " for code
360   # "k " for cross-ref
361   # "kK" for capitalised cross-ref
362   # "w " for Web link
363   # "wc" for code-type Web link
364   # "x " for beginning of resolved cross-ref; generates no visible output,
365   #      and the text is the cross-reference code
366   # "xe" for end of resolved cross-ref; text is same as for "x ".
367   # "i " for point to be indexed: the text is the internal index into the
368   #      index-items arrays
369   # "sp" for space
370   while (/\S/) {
371     s/^\s*//, push @$pname, "sp" if /^\s/;
372     $indexing = $qindex = 0;
373     if (/^(\\[iI])?\\c/) {
374       $qindex = 1 if $1 eq "\\I";
375       $indexing = 1, s/^\\[iI]// if $1;
376       s/^\\c//;
377       die "badly formatted \\c: \\c$_\n" if !/{(([^\\}]|\\.)*)}(.*)$/;
378       $w = $1;
379       $_ = $3;
380       $w =~ s/\\\{/{/g;
381       $w =~ s/\\\}/}/g;
382       $w =~ s/\\-/-/g;
383       $w =~ s/\\\\/\\/g;
384       (push @$pname,"i"),$lastp = $#$pname if $indexing;
385       push @$pname,"c $w" if !$qindex;
386       $$pname[$lastp] = &addidx($node, $w, "c $w") if $indexing;
387     } elsif (/^\\[iIe]/) {
388       /^(\\[iI])?(\\e)?/;
389       $emph = 0;
390       $qindex = 1 if $1 eq "\\I";
391       $indexing = 1, $type = "\\i" if $1;
392       $emph = 1, $type = "\\e" if $2;
393       s/^(\\[iI])?(\\e?)//;
394       die "badly formatted $type: $type$_\n" if !/{(([^\\}]|\\.)*)}(.*)$/;
395       $w = $1;
396       $_ = $3;
397       $w =~ s/\\\{/{/g;
398       $w =~ s/\\\}/}/g;
399       $w =~ s/\\-/-/g;
400       $w =~ s/\\\\/\\/g;
401       $t = $emph ? "es" : "n ";
402       @ientry = ();
403       (push @$pname,"i"),$lastp = $#$pname if $indexing;
404       foreach $i (split /\s+/,$w) {  # \e and \i can be multiple words
405         push @$pname,"$t$i","sp" if !$qindex;
406         ($ii=$i) =~ tr/A-Z/a-z/, push @ientry,"n $ii","sp" if $indexing;
407         $t = $emph ? "e " : "n ";
408       }
409       $w =~ tr/A-Z/a-z/, pop @ientry if $indexing;
410       $$pname[$lastp] = &addidx($node, $w, @ientry) if $indexing;
411       pop @$pname if !$qindex; # remove final space
412       if (substr($$pname[$#$pname],0,2) eq "es" && !$qindex) {
413         substr($$pname[$#$pname],0,2) = "eo";
414       } elsif ($emph && !$qindex) {
415         substr($$pname[$#$pname],0,2) = "ee";
416       }
417     } elsif (/^\\[kK]/) {
418       $t = "k ";
419       $t = "kK" if /^\\K/;
420       s/^\\[kK]//;
421       die "badly formatted \\k: \\c$_\n" if !/{([^}]*)}(.*)$/;
422       $_ = $2;
423       push @$pname,"$t$1";
424     } elsif (/^\\W/) {
425       s/^\\W//;
426       die "badly formatted \\W: \\W$_\n"
427           if !/{([^}]*)}(\\i)?(\\c)?{(([^\\}]|\\.)*)}(.*)$/;
428       $l = $1;
429       $w = $4;
430       $_ = $6;
431       $t = "w ";
432       $t = "wc" if $3 eq "\\c";
433       $indexing = 1 if $2;
434       $w =~ s/\\\{/{/g;
435       $w =~ s/\\\}/}/g;
436       $w =~ s/\\-/-/g;
437       $w =~ s/\\\\/\\/g;
438       (push @$pname,"i"),$lastp = $#$pname if $indexing;
439       push @$pname,"$t<$l>$w";
440       $$pname[$lastp] = &addidx($node, $w, "c $w") if $indexing;
441     } else {
442       die "what the hell? $_\n" if !/^(([^\s\\\-]|\\[\\{}\-])*-?)(.*)$/;
443       die "painful death! $_\n" if !length $1;
444       $w = $1;
445       $_ = $3;
446       $w =~ s/\\\{/{/g;
447       $w =~ s/\\\}/}/g;
448       $w =~ s/\\-/-/g;
449       $w =~ s/\\\\/\\/g;
450       if ($w eq "-") {
451         push @$pname,"da";
452       } else {
453         push @$pname,"n $w";
454       }
455     }
456   }
457   if ($irewrite ne undef) {
458     &addidx(undef, $irewrite, @$pname);
459     @$pname = ();
460   } else {
461     push @pnames, $pname;
462     push @pflags, $pflags;
463     $pname++;
464   }
465 }
466
467 sub addidx {
468   my ($node, $text, @ientry) = @_;
469   $text = $idxalias{$text} || $text;
470   if ($node eq undef || !$idxmap{$text}) {
471     @$ientry = @ientry;
472     $idxmap{$text} = $ientry;
473     $ientry++;
474   }
475   if ($node) {
476     $idxnodes{$node,$text} = 1;
477     return "i $text";
478   }
479 }
480
481 sub indexsort {
482   my $iitem, $ientry, $i, $piitem, $pcval, $cval, $clrcval;
483
484   @itags = map { # get back the original data as the 1st elt of each list
485              $_->[0]
486            } sort { # compare auxiliary (non-first) elements of lists
487              $a->[1] cmp $b->[1] ||
488              $a->[2] cmp $b->[2] ||
489              $a->[0] cmp $b->[0]
490            } map { # transform array into list of 3-element lists
491              my $ientry = $idxmap{$_};
492              my $a = substr($$ientry[0],2);
493              $a =~ tr/A-Za-z0-9//cd;
494              [$_, uc($a), substr($$ientry[0],0,2)]
495            } keys %idxmap;
496
497   # Having done that, check for comma-hood.
498   $cval = 0;
499   foreach $iitem (@itags) {
500     $ientry = $idxmap{$iitem};
501     $clrcval = 1;
502     $pcval = $cval;
503     FL:for ($i=0; $i <= $#$ientry; $i++) {
504       if ($$ientry[$i] =~ /^(n .*,)(.*)/) {
505         $$ientry[$i] = $1;
506         splice @$ientry,$i+1,0,"n $2" if length $2;
507         $commapos{$iitem} = $i+1;
508         $cval = join("\002", @$ientry[0..$i]);
509         $clrcval = 0;
510         last FL;
511       }
512     }
513     $cval = undef if $clrcval;
514     $commanext{$iitem} = $commaafter{$piitem} = 1
515       if $cval and ($cval eq $pcval);
516     $piitem = $iitem;
517   }
518 }
519
520 sub indexdiag {
521   my $iitem,$ientry,$w,$ww,$foo,$node;
522   open INDEXDIAG,">index.diag";
523   foreach $iitem (@itags) {
524     $ientry = $idxmap{$iitem};
525     print INDEXDIAG "<$iitem> ";
526     foreach $w (@$ientry) {
527       $ww = &word_txt($w);
528       print INDEXDIAG $ww unless $ww eq "\001";
529     }
530     print INDEXDIAG ":";
531     $foo = " ";
532     foreach $node (@nodes) {
533       (print INDEXDIAG $foo,$node), $foo = ", " if $idxnodes{$node,$iitem};
534     }
535     print INDEXDIAG "\n";
536   }
537   close INDEXDIAG;
538 }
539
540 sub fixup_xrefs {
541   my $pname, $p, $i, $j, $k, $caps, @repl;
542
543   for ($p=0; $p<=$#pnames; $p++) {
544     next if $pflags[$p] eq "code";
545     $pname = $pnames[$p];
546     for ($i=$#$pname; $i >= 0; $i--) {
547       if ($$pname[$i] =~ /^k/) {
548         $k = $$pname[$i];
549         $caps = ($k =~ /^kK/);
550         $k = substr($k,2);
551         $repl = $refs{$k};
552         die "undefined keyword `$k'\n" unless $repl;
553         substr($repl,0,1) =~ tr/a-z/A-Z/ if $caps;
554         @repl = ();
555         push @repl,"x $xrefs{$k}";
556         foreach $j (split /\s+/,$repl) {
557           push @repl,"n $j";
558           push @repl,"sp";
559         }
560         pop @repl; # remove final space
561         push @repl,"xe$xrefs{$k}";
562         splice @$pname,$i,1,@repl;
563       }
564     }
565   }
566 }
567
568 sub write_txt {
569   # This is called from the top level, so I won't bother using
570   # my or local.
571
572   # Open file.
573   print "writing file...";
574   open TEXT,">nasmdoc.txt";
575   select TEXT;
576
577   # Preamble.
578   $title = "The Netwide Assembler: NASM";
579   $spaces = ' ' x ((75-(length $title))/2);
580   ($underscore = $title) =~ s/./=/g;
581   print "$spaces$title\n$spaces$underscore\n";
582
583   for ($para = 0; $para <= $#pnames; $para++) {
584     $pname = $pnames[$para];
585     $pflags = $pflags[$para];
586     $ptype = substr($pflags,0,4);
587
588     print "\n"; # always one of these before a new paragraph
589
590     if ($ptype eq "chap") {
591       # Chapter heading. "Chapter N: Title" followed by a line of
592       # minus signs.
593       $pflags =~ /chap (.*) :(.*)/;
594       $title = "Chapter $1: ";
595       foreach $i (@$pname) {
596         $ww = &word_txt($i);
597         $title .= $ww unless $ww eq "\001";
598       }
599       print "$title\n";
600       $title =~ s/./-/g;
601       print "$title\n";
602     } elsif ($ptype eq "appn") {
603       # Appendix heading. "Appendix N: Title" followed by a line of
604       # minus signs.
605       $pflags =~ /appn (.*) :(.*)/;
606       $title = "Appendix $1: ";
607       foreach $i (@$pname) {
608         $ww = &word_txt($i);
609         $title .= $ww unless $ww eq "\001";
610       }
611       print "$title\n";
612       $title =~ s/./-/g;
613       print "$title\n";
614     } elsif ($ptype eq "head" || $ptype eq "subh") {
615       # Heading or subheading. Just a number and some text.
616       $pflags =~ /.... (.*) :(.*)/;
617       $title = sprintf "%6s ", $1;
618       foreach $i (@$pname) {
619         $ww = &word_txt($i);
620         $title .= $ww unless $ww eq "\001";
621       }
622       print "$title\n";
623     } elsif ($ptype eq "code") {
624       # Code paragraph. Emit each line with a seven character indent.
625       foreach $i (@$pname) {
626         warn "code line longer than 68 chars: $i\n" if length $i > 68;
627         print ' 'x7, $i, "\n";
628       }
629     } elsif ($ptype eq "bull" || $ptype eq "norm") {
630       # Ordinary paragraph, optionally bulleted. We wrap, with ragged
631       # 75-char right margin and either 7 or 11 char left margin
632       # depending on bullets.
633       if ($ptype eq "bull") {
634         $line = ' 'x7 . '(*) ';
635         $next = ' 'x11;
636       } else {
637         $line = $next = ' 'x7;
638       }
639       @a = @$pname;
640       $wd = $wprev = '';
641       do {
642         do { $w = &word_txt(shift @a) } while $w eq "\001"; # nasty hack
643         $wd .= $wprev;
644         if ($wprev =~ /-$/ || $w eq ' ' || $w eq '' || $w eq undef) {
645           if (length ($line . $wd) > 75) {
646             $line =~ s/\s*$//; # trim trailing spaces
647             print "$line\n";
648             $line = $next;
649             $wd =~ s/^\s*//; # trim leading spaces
650           }
651           $line .= $wd;
652           $wd = '';
653         }
654         $wprev = $w;
655       } while ($w ne '' && $w ne undef);
656       if ($line =~ /\S/) {
657         $line =~ s/\s*$//; # trim trailing spaces
658         print "$line\n";
659       }
660     }
661   }
662
663   # Close file.
664   select STDOUT;
665   close TEXT;
666 }
667
668 sub word_txt {
669   my ($w) = @_;
670   my $wtype, $wmajt;
671
672   return undef if $w eq '' || $w eq undef;
673   $wtype = substr($w,0,2);
674   $wmajt = substr($wtype,0,1);
675   $w = substr($w,2);
676   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
677   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
678     return $w;
679   } elsif ($wtype eq "sp") {
680     return ' ';
681   } elsif ($wtype eq "da") {
682     return '-';
683   } elsif ($wmajt eq "c" || $wtype eq "wc") {
684     return "`${w}'";
685   } elsif ($wtype eq "es") {
686     return "_${w}";
687   } elsif ($wtype eq "ee") {
688     return "${w}_";
689   } elsif ($wtype eq "eo") {
690     return "_${w}_";
691   } elsif ($wmajt eq "x" || $wmajt eq "i") {
692     return "\001";
693   } else {
694     die "panic in word_txt: $wtype$w\n";
695   }
696 }
697
698 sub write_html {
699   # This is called from the top level, so I won't bother using
700   # my or local.
701
702   # Write contents file. Just the preamble, then a menu of links to the
703   # separate chapter files and the nodes therein.
704   print "writing contents file...";
705   open TEXT,">nasmdoc0.html";
706   select TEXT;
707   &html_preamble(0);
708   print "<p>This manual documents NASM, the Netwide Assembler: an assembler\n";
709   print "targetting the Intel x86 series of processors, with portable source.\n";
710   print "<p>";
711   for ($node = $tstruct_next{'Top'}; $node; $node = $tstruct_next{$node}) {
712     if ($tstruct_level{$node} == 1) {
713       # Invent a file name.
714       ($number = lc($xrefnodes{$node})) =~ s/.*-//;
715       $fname="nasmdocx.html";
716       substr($fname,8 - length $number, length $number) = $number;
717       $html_fnames{$node} = $fname;
718       $link = $fname;
719       print "<p>";
720     } else {
721       # Use the preceding filename plus a marker point.
722       $link = $fname . "#$xrefnodes{$node}";
723     }
724     $title = "$node: ";
725     $pname = $tstruct_pname{$node};
726     foreach $i (@$pname) {
727       $ww = &word_html($i);
728       $title .= $ww unless $ww eq "\001";
729     }
730     print "<a href=\"$link\">$title</a><br>\n";
731   }
732   print "<p><a href=\"nasmdoci.html\">Index</a>\n";
733   print "</body></html>\n";
734   select STDOUT;
735   close TEXT;
736
737   # Open a null file, to ensure output (eg random &html_jumppoints calls)
738   # goes _somewhere_.
739   print "writing chapter files...";
740   open TEXT,">/dev/null";
741   select TEXT;
742   $html_lastf = '';
743
744   $in_list = 0;
745
746   for ($para = 0; $para <= $#pnames; $para++) {
747     $pname = $pnames[$para];
748     $pflags = $pflags[$para];
749     $ptype = substr($pflags,0,4);
750
751     $in_list = 0, print "</ul>\n" if $in_list && $ptype ne "bull";
752     if ($ptype eq "chap") {
753       # Chapter heading. Begin a new file.
754       $pflags =~ /chap (.*) :(.*)/;
755       $title = "Chapter $1: ";
756       $xref = $2;
757       &html_jumppoints; print "</body></html>\n"; select STDOUT; close TEXT;
758       $html_lastf = $html_fnames{$chapternode};
759       $chapternode = $nodexrefs{$xref};
760       $html_nextf = $html_fnames{$tstruct_mnext{$chapternode}};
761       open TEXT,">$html_fnames{$chapternode}"; select TEXT; &html_preamble(1);
762       foreach $i (@$pname) {
763         $ww = &word_html($i);
764         $title .= $ww unless $ww eq "\001";
765       }
766       $h = "<h2><a name=\"$xref\">$title</a></h2>\n";
767       print $h; print FULL $h;
768     } elsif ($ptype eq "appn") {
769       # Appendix heading. Begin a new file.
770       $pflags =~ /appn (.*) :(.*)/;
771       $title = "Appendix $1: ";
772       $xref = $2;
773       &html_jumppoints; print "</body></html>\n"; select STDOUT; close TEXT;
774       $html_lastf = $html_fnames{$chapternode};
775       $chapternode = $nodexrefs{$xref};
776       $html_nextf = $html_fnames{$tstruct_mnext{$chapternode}};
777       open TEXT,">$html_fnames{$chapternode}"; select TEXT; &html_preamble(1);
778       foreach $i (@$pname) {
779         $ww = &word_html($i);
780         $title .= $ww unless $ww eq "\001";
781       }
782       print "<h2><a name=\"$xref\">$title</a></h2>\n";
783     } elsif ($ptype eq "head" || $ptype eq "subh") {
784       # Heading or subheading.
785       $pflags =~ /.... (.*) :(.*)/;
786       $hdr = ($ptype eq "subh" ? "h4" : "h3");
787       $title = $1 . " ";
788       $xref = $2;
789       foreach $i (@$pname) {
790         $ww = &word_html($i);
791         $title .= $ww unless $ww eq "\001";
792       }
793       print "<$hdr><a name=\"$xref\">$title</a></$hdr>\n";
794     } elsif ($ptype eq "code") {
795       # Code paragraph.
796       print "<p><pre>\n";
797       foreach $i (@$pname) {
798         $w = $i;
799         $w =~ s/&/&amp;/g;
800         $w =~ s/</&lt;/g;
801         $w =~ s/>/&gt;/g;
802         print $w, "\n";
803       }
804       print "</pre>\n";
805     } elsif ($ptype eq "bull" || $ptype eq "norm") {
806       # Ordinary paragraph, optionally bulleted. We wrap, with ragged
807       # 75-char right margin and either 7 or 11 char left margin
808       # depending on bullets.
809       if ($ptype eq "bull") {
810         $in_list = 1, print "<ul>\n" unless $in_list;
811         $line = '<li>';
812       } else {
813         $line = '<p>';
814       }
815       @a = @$pname;
816       $wd = $wprev = '';
817       do {
818         do { $w = &word_html(shift @a) } while $w eq "\001"; # nasty hack
819         $wd .= $wprev;
820         if ($w eq ' ' || $w eq '' || $w eq undef) {
821           if (length ($line . $wd) > 75) {
822             $line =~ s/\s*$//; # trim trailing spaces
823             print "$line\n";
824             $line = '';
825             $wd =~ s/^\s*//; # trim leading spaces
826           }
827           $line .= $wd;
828           $wd = '';
829         }
830         $wprev = $w;
831       } while ($w ne '' && $w ne undef);
832       if ($line =~ /\S/) {
833         $line =~ s/\s*$//; # trim trailing spaces
834         print "$line\n";
835       }
836     }
837   }
838
839   # Close whichever file was open.
840   &html_jumppoints;
841   print "</body></html>\n";
842   select STDOUT;
843   close TEXT;
844
845   print "\n   writing index file...";
846   open TEXT,">nasmdoci.html";
847   select TEXT;
848   &html_preamble(0);
849   print "<p align=center><a href=\"nasmdoc0.html\">Contents</a>\n";
850   print "<p>";
851   &html_index;
852   print "<p align=center><a href=\"nasmdoc0.html\">Contents</a>\n";
853   print "</body></html>\n";
854   select STDOUT;
855   close TEXT;
856 }
857
858 sub html_preamble {
859   print "<html><head><title>NASM Manual</title></head>\n";
860   print "<body><h1 align=center>The Netwide Assembler: NASM</h1>\n\n";
861   &html_jumppoints if $_[0];
862 }
863
864 sub html_jumppoints {
865   print "<p align=center>";
866   print "<a href=\"$html_nextf\">Next Chapter</a> |\n" if $html_nextf;
867   print "<a href=\"$html_lastf\">Previous Chapter</a> |\n" if $html_lastf;
868   print "<a href=\"nasmdoc0.html\">Contents</a> |\n";
869   print "<a href=\"nasmdoci.html\">Index</a>\n";
870 }
871
872 sub html_index {
873   my $itag, $a, @ientry, $sep, $w, $wd, $wprev, $line;
874
875   $chapternode = '';
876   foreach $itag (@itags) {
877     $ientry = $idxmap{$itag};
878     @a = @$ientry;
879     push @a, "n :";
880     $sep = 0;
881     foreach $node (@nodes) {
882       next if !$idxnodes{$node,$itag};
883       push @a, "n ," if $sep;
884       push @a, "sp", "x $xrefnodes{$node}", "n $node", "xe$xrefnodes{$node}";
885       $sep = 1;
886     }
887     $line = '';
888     do {
889       do { $w = &word_html(shift @a) } while $w eq "\001"; # nasty hack
890       $wd .= $wprev;
891       if ($w eq ' ' || $w eq '' || $w eq undef) {
892         if (length ($line . $wd) > 75) {
893           $line =~ s/\s*$//; # trim trailing spaces
894           print "$line\n";
895           $line = '';
896           $wd =~ s/^\s*//; # trim leading spaces
897         }
898         $line .= $wd;
899         $wd = '';
900       }
901       $wprev = $w;
902     } while ($w ne '' && $w ne undef);
903     if ($line =~ /\S/) {
904       $line =~ s/\s*$//; # trim trailing spaces
905       print "$line\n";
906     }
907     print "<br>\n";
908   }
909 }
910
911 sub word_html {
912   my ($w) = @_;
913   my $wtype, $wmajt, $pfx, $sfx;
914
915   return undef if $w eq '' || $w eq undef;
916
917   $wtype = substr($w,0,2);
918   $wmajt = substr($wtype,0,1);
919   $w = substr($w,2);
920   $pfx = $sfx = '';
921   $pfx = "<a href=\"$1\">", $sfx = "</a>", $w = $2
922     if $wmajt eq "w" && $w =~ /^<(.*)>(.*)$/;
923   $w =~ s/&/&amp;/g;
924   $w =~ s/</&lt;/g;
925   $w =~ s/>/&gt;/g;
926   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
927     return $pfx . $w . $sfx;
928   } elsif ($wtype eq "sp") {
929     return ' ';
930   } elsif ($wtype eq "da") {
931     return '-'; # sadly, en-dashes are non-standard in HTML
932   } elsif ($wmajt eq "c" || $wtype eq "wc") {
933     return $pfx . "<code><nobr>${w}</nobr></code>" . $sfx;
934   } elsif ($wtype eq "es") {
935     return "<em>${w}";
936   } elsif ($wtype eq "ee") {
937     return "${w}</em>";
938   } elsif ($wtype eq "eo") {
939     return "<em>${w}</em>";
940   } elsif ($wtype eq "x ") {
941     # Magic: we must resolve the cross reference into file and marker
942     # parts, then dispose of the file part if it's us, and dispose of
943     # the marker part if the cross reference describes the top node of
944     # another file.
945     my $node = $nodexrefs{$w}; # find the node we're aiming at
946     my $level = $tstruct_level{$node}; # and its level
947     my $up = $node, $uplev = $level-1;
948     $up = $tstruct_up{$up} while $uplev--; # get top node of containing file
949     my $file = ($up ne $chapternode) ? $html_fnames{$up} : "";
950     my $marker = ($level == 1 and $file) ? "" : "#$w";
951     return "<a href=\"$file$marker\">";
952   } elsif ($wtype eq "xe") {
953     return "</a>";
954   } elsif ($wmajt eq "i") {
955     return "\001";
956   } else {
957     die "panic in word_html: $wtype$w\n";
958   }
959 }
960
961 sub write_texi {
962   # This is called from the top level, so I won't bother using
963   # my or local.
964
965   # Open file.
966   print "writing file...";
967   open TEXT,">nasmdoc.texi";
968   select TEXT;
969
970   # Preamble.
971   print "\\input texinfo   \@c -*-texinfo-*-\n";
972   print "\@c \%**start of header\n";
973   print "\@setfilename ",$metadata{'infofile'},".info\n";
974   print "\@dircategory ",$metadata{'category'},"\n";
975   print "\@direntry\n";
976   printf "* %-28s %s.\n",
977   sprintf('%s: (%s).', $metadata{'infoname'}, $metadata{'infofile'}),
978   $metadata{'infotitle'};
979   print "\@end direntry\n";
980   print "\@settitle ", $metadata{'title'},"\n";
981   print "\@setchapternewpage odd\n";
982   print "\@c \%**end of header\n";
983   print "\n";
984   print "\@ifinfo\n";
985   print $metadata{'summary'}, "\n";
986   print "\n";
987   print "Copyright ",$metadata{'year'}," ",$metadata{'author'},"\n";
988   print "\n";
989   print $metadata{'license'}, "\n";
990   print "\@end ifinfo\n";
991   print "\n";
992   print "\@titlepage\n";
993   $title = $metadata{'title'};
994   $title =~ s/ - / --- /g;
995   print "\@title ${title}\n";
996   print "\@author ",$metadata{'author'},"\n";
997   print "\n";
998   print "\@page\n";
999   print "\@vskip 0pt plus 1filll\n";
1000   print "Copyright \@copyright{} ",$metadata{'year'},' ',$metadata{'author'},"\n";
1001   print "\n";
1002   print $metadata{'license'}, "\n";
1003   print "\@end titlepage\n";
1004   print "\n";
1005   print "\@node Top, $tstruct_next{'Top'}, (dir), (dir)\n";
1006   print "\@top ",$metadata{'infotitle'},"\n";
1007   print "\n";
1008   print "\@ifinfo\n";
1009   print $metadata{'summary'}, "\n";
1010   print "\@end ifinfo\n";
1011
1012   $node = "Top";
1013
1014   $bulleting = 0;
1015   for ($para = 0; $para <= $#pnames; $para++) {
1016     $pname = $pnames[$para];
1017     $pflags = $pflags[$para];
1018     $ptype = substr($pflags,0,4);
1019
1020     $bulleting = 0, print "\@end itemize\n" if $bulleting && $ptype ne "bull";
1021     print "\n"; # always one of these before a new paragraph
1022
1023     if ($ptype eq "chap") {
1024       # Chapter heading. Begin a new node.
1025       &texi_menu($node)
1026         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1027       $pflags =~ /chap (.*) :(.*)/;
1028       $node = "Chapter $1";
1029       $title = "Chapter $1: ";
1030       foreach $i (@$pname) {
1031         $ww = &word_texi($i);
1032         $title .= $ww unless $ww eq "\001";
1033       }
1034       print "\@node $node, $tstruct_next{$node}, $tstruct_prev{$node},";
1035       print " $tstruct_up{$node}\n\@unnumbered $title\n";
1036     } elsif ($ptype eq "appn") {
1037       # Appendix heading. Begin a new node.
1038       &texi_menu($node)
1039         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1040       $pflags =~ /appn (.*) :(.*)/;
1041       $node = "Appendix $1";
1042       $title = "Appendix $1: ";
1043       foreach $i (@$pname) {
1044         $ww = &word_texi($i);
1045         $title .= $ww unless $ww eq "\001";
1046       }
1047       print "\@node $node, $tstruct_next{$node}, $tstruct_prev{$node},";
1048       print " $tstruct_up{$node}\n\@unnumbered $title\n";
1049     } elsif ($ptype eq "head" || $ptype eq "subh") {
1050       # Heading or subheading. Begin a new node.
1051       &texi_menu($node)
1052         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1053       $pflags =~ /.... (.*) :(.*)/;
1054       $node = "Section $1";
1055       $title = "$1. ";
1056       foreach $i (@$pname) {
1057         $ww = &word_texi($i);
1058         $title .= $ww unless $ww eq "\001";
1059       }
1060       print "\@node $node, $tstruct_next{$node}, $tstruct_prev{$node},";
1061       print " $tstruct_up{$node}\n";
1062       $hdr = ($ptype eq "subh" ? "\@unnumberedsubsec" : "\@unnumberedsec");
1063       print "$hdr $title\n";
1064     } elsif ($ptype eq "code") {
1065       # Code paragraph. Surround with @example / @end example.
1066       print "\@example\n";
1067       foreach $i (@$pname) {
1068         warn "code line longer than 68 chars: $i\n" if length $i > 68;
1069         $i =~ s/\@/\@\@/g;
1070         $i =~ s/\{/\@\{/g;
1071         $i =~ s/\}/\@\}/g;
1072         print "$i\n";
1073       }
1074       print "\@end example\n";
1075     } elsif ($ptype eq "bull" || $ptype eq "norm") {
1076       # Ordinary paragraph, optionally bulleted. We wrap, FWIW.
1077       if ($ptype eq "bull") {
1078         $bulleting = 1, print "\@itemize \@bullet\n" if !$bulleting;
1079         print "\@item\n";
1080       }
1081       $line = '';
1082       @a = @$pname;
1083       $wd = $wprev = '';
1084       do {
1085         do { $w = &word_texi(shift @a); } while $w eq "\001"; # hack
1086         $wd .= $wprev;
1087         if ($wprev =~ /-$/ || $w eq ' ' || $w eq '' || $w eq undef) {
1088           if (length ($line . $wd) > 75) {
1089             $line =~ s/\s*$//; # trim trailing spaces
1090             print "$line\n";
1091             $line = '';
1092             $wd =~ s/^\s*//; # trim leading spaces
1093           }
1094           $line .= $wd;
1095           $wd = '';
1096         }
1097         $wprev = $w;
1098       } while ($w ne '' && $w ne undef);
1099       if ($line =~ /\S/) {
1100         $line =~ s/\s*$//; # trim trailing spaces
1101         print "$line\n";
1102       }
1103     }
1104   }
1105
1106   # Write index.
1107   &texi_index;
1108
1109   # Close file.
1110   print "\n\@contents\n\@bye\n";
1111   select STDOUT;
1112   close TEXT;
1113 }
1114
1115 # Side effect of this procedure: update global `texiwdlen' to be the length
1116 # in chars of the formatted version of the word.
1117 sub word_texi {
1118   my ($w) = @_;
1119   my $wtype, $wmajt;
1120
1121   return undef if $w eq '' || $w eq undef;
1122   $wtype = substr($w,0,2);
1123   $wmajt = substr($wtype,0,1);
1124   $w = substr($w,2);
1125   $wlen = length $w;
1126   $w =~ s/\@/\@\@/g;
1127   $w =~ s/\{/\@\{/g;
1128   $w =~ s/\}/\@\}/g;
1129   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
1130   substr($w,0,1) =~ tr/a-z/A-Z/, $capital = 0 if $capital;
1131   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
1132     $texiwdlen = $wlen;
1133     return $w;
1134   } elsif ($wtype eq "sp") {
1135     $texiwdlen = 1;
1136     return ' ';
1137   } elsif ($wtype eq "da") {
1138     $texiwdlen = 2;
1139     return '--';
1140   } elsif ($wmajt eq "c" || $wtype eq "wc") {
1141     $texiwdlen = 2 + $wlen;
1142     return "\@code\{$w\}";
1143   } elsif ($wtype eq "es") {
1144     $texiwdlen = 1 + $wlen;
1145     return "\@emph\{${w}";
1146   } elsif ($wtype eq "ee") {
1147     $texiwdlen = 1 + $wlen;
1148     return "${w}\}";
1149   } elsif ($wtype eq "eo") {
1150     $texiwdlen = 2 + $wlen;
1151     return "\@emph\{${w}\}";
1152   } elsif ($wtype eq "x ") {
1153     $texiwdlen = 0; # we don't need it in this case
1154     $capital = 1; # hack
1155     return "\@ref\{";
1156   } elsif ($wtype eq "xe") {
1157     $texiwdlen = 0; # we don't need it in this case
1158     return "\}";
1159   } elsif ($wmajt eq "i") {
1160     $texiwdlen = 0; # we don't need it in this case
1161     return "\001";
1162   } else {
1163     die "panic in word_texi: $wtype$w\n";
1164   }
1165 }
1166
1167 sub texi_menu {
1168   my ($topitem) = @_;
1169   my $item, $i, $mpname, $title, $wd;
1170
1171   $item = $tstruct_next{$topitem};
1172   print "\@menu\n";
1173   while ($item) {
1174     $title = "";
1175     $mpname = $tstruct_pname{$item};
1176     foreach $i (@$mpname) {
1177       $wd = &word_texi($i);
1178       $title .= $wd unless $wd eq "\001";
1179     }
1180     print "* ${item}:: $title\n";
1181     $item = $tstruct_mnext{$item};
1182   }
1183   print "* Index::\n" if $topitem eq "Top";
1184   print "\@end menu\n";
1185 }
1186
1187 sub texi_index {
1188   my $itag, $ientry, @a, $wd, $item, $len;
1189   my $subnums = "123456789ABCDEFGHIJKLMNOPQRSTU" .
1190                 "VWXYZabcdefghijklmnopqrstuvwxyz";
1191
1192   print "\@ifinfo\n\@node Index, , $FIXMElastnode, Top\n";
1193   print "\@unnumbered Index\n\n\@menu\n";
1194
1195   foreach $itag (@itags) {
1196     $ientry = $idxmap{$itag};
1197     @a = @$ientry;
1198     $item = '';
1199     $len = 0;
1200     foreach $i (@a) {
1201       $wd = &word_texi($i);
1202       $item .= $wd, $len += $texiwdlen unless $wd eq "\001";
1203     }
1204     $i = 0;
1205     foreach $node (@nodes) {
1206       next if !$idxnodes{$node,$itag};
1207       printf "* %s%s (%s): %s.\n",
1208           $item, " " x (40-$len), substr($subnums,$i++,1), $node;
1209     }
1210   }
1211   print "\@end menu\n\@end ifinfo\n";
1212 }
1213
1214 sub write_hlp {
1215   # This is called from the top level, so I won't bother using
1216   # my or local.
1217
1218   # Build the index-tag text forms.
1219   print "building index entries...";
1220   @hlp_index = map {
1221                  my $i,$ww;
1222                  my $ientry = $idxmap{$_};
1223                  my $title = "";
1224                  foreach $i (@$ientry) {
1225                    $ww = &word_hlp($i,0);
1226                    $title .= $ww unless $ww eq "\001";
1227                  }
1228                  $title;
1229                } @itags;
1230
1231   # Write the HPJ project-description file.
1232   print "writing .hpj file...";
1233   open HPJ,">nasmdoc.hpj";
1234   print HPJ "[OPTIONS]\ncompress=true\n";
1235   print HPJ "title=NASM: The Netwide Assembler\noldkeyphrase=no\n\n";
1236   print HPJ "[FILES]\nnasmdoc.rtf\n\n";
1237   print HPJ "[CONFIG]\n";
1238   print HPJ 'CreateButton("btn_up", "&Up",'.
1239             ' "JumpContents(`nasmdoc.hlp'."'".')")';
1240   print HPJ "\nBrowseButtons()\n";
1241   close HPJ;
1242
1243   # Open file.
1244   print "\n   writing .rtf file...";
1245   open TEXT,">nasmdoc.rtf";
1246   select TEXT;
1247
1248   # Preamble.
1249   print "{\\rtf1\\ansi{\\fonttbl\n";
1250   print "\\f0\\froman Times New Roman;\\f1\\fmodern Courier New;\n";
1251   print "\\f2\\fswiss Arial;\\f3\\ftech Wingdings}\\deff0\n";
1252   print "#{\\footnote Top}\n";
1253   print "\${\\footnote Contents}\n";
1254   print "+{\\footnote browse:00000}\n";
1255   print "!{\\footnote DisableButton(\"btn_up\")}\n";
1256   print "\\keepn\\f2\\b\\fs30\\sb0\n";
1257   print "NASM: The Netwide Assembler\n";
1258   print "\\par\\pard\\plain\\sb120\n";
1259   print "This file documents NASM, the Netwide Assembler: an assembler \n";
1260   print "targetting the Intel x86 series of processors, with portable source.\n";
1261
1262   $node = "Top";
1263   $browse = 0;
1264
1265   $newpar = "\\par\\sb120\n";
1266   for ($para = 0; $para <= $#pnames; $para++) {
1267     $pname = $pnames[$para];
1268     $pflags = $pflags[$para];
1269     $ptype = substr($pflags,0,4);
1270
1271     print $newpar;
1272     $newpar = "\\par\\sb120\n";
1273
1274     if ($ptype eq "chap") {
1275       # Chapter heading. Begin a new node.
1276       &hlp_menu($node)
1277         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1278       $pflags =~ /chap (.*) :(.*)/;
1279       $node = "Chapter $1";
1280       $title = $footnotetitle = "Chapter $1: ";
1281       foreach $i (@$pname) {
1282         $ww = &word_hlp($i,1);
1283         $title .= $ww, $footnotetitle .= &word_hlp($i,0) unless $ww eq "\001";
1284       }
1285       print "\\page\n";
1286       printf "#{\\footnote %s}\n", &hlp_sectkw($node);
1287       print "\${\\footnote $footnotetitle}\n";
1288       printf "+{\\footnote browse:%05d}\n", ++$browse;
1289       printf "!{\\footnote ChangeButtonBinding(\"btn_up\"," .
1290              "\"JumpId(\`nasmdoc.hlp',\`%s')\");\n",
1291              &hlp_sectkw($tstruct_up{$node});
1292       print "EnableButton(\"btn_up\")}\n";
1293       &hlp_keywords($node);
1294       print "\\keepn\\f2\\b\\fs30\\sb60\\sa60\n";
1295       print "$title\n";
1296       $newpar = "\\par\\pard\\plain\\sb120\n";
1297     } elsif ($ptype eq "appn") {
1298       # Appendix heading. Begin a new node.
1299       &hlp_menu($node)
1300         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1301       $pflags =~ /appn (.*) :(.*)/;
1302       $node = "Appendix $1";
1303       $title = $footnotetitle = "Appendix $1: ";
1304       foreach $i (@$pname) {
1305         $ww = &word_hlp($i,1);
1306         $title .= $ww, $footnotetitle .= &word_hlp($i,0) unless $ww eq "\001";
1307       }
1308       print "\\page\n";
1309       printf "#{\\footnote %s}\n", &hlp_sectkw($node);
1310       print "\${\\footnote $footnotetitle}\n";
1311       printf "+{\\footnote browse:%05d}\n", ++$browse;
1312       printf "!{\\footnote ChangeButtonBinding(\"btn_up\"," .
1313              "\"JumpId(\`nasmdoc.hlp',\`%s')\");\n",
1314              &hlp_sectkw($tstruct_up{$node});
1315       print "EnableButton(\"btn_up\")}\n";
1316       &hlp_keywords($node);
1317       print "\\keepn\\f2\\b\\fs30\\sb60\\sa60\n";
1318       print "$title\n";
1319       $newpar = "\\par\\pard\\plain\\sb120\n";
1320     } elsif ($ptype eq "head" || $ptype eq "subh") {
1321       # Heading or subheading. Begin a new node.
1322       &hlp_menu($node)
1323         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1324       $pflags =~ /.... (.*) :(.*)/;
1325       $node = "Section $1";
1326       $title = $footnotetitle = "$1. ";
1327       foreach $i (@$pname) {
1328         $ww = &word_hlp($i,1);
1329         $title .= $ww, $footnotetitle .= &word_hlp($i,0) unless $ww eq "\001";
1330       }
1331       print "\\page\n";
1332       printf "#{\\footnote %s}\n", &hlp_sectkw($node);
1333       print "\${\\footnote $footnotetitle}\n";
1334       printf "+{\\footnote browse:%05d}\n", ++$browse;
1335       printf "!{\\footnote ChangeButtonBinding(\"btn_up\"," .
1336              "\"JumpId(\`nasmdoc.hlp',\`%s')\");\n",
1337              &hlp_sectkw($tstruct_up{$node});
1338       print "EnableButton(\"btn_up\")}\n";
1339       &hlp_keywords($node);
1340       print "\\keepn\\f2\\b\\fs30\\sb60\\sa60\n";
1341       print "$title\n";
1342       $newpar = "\\par\\pard\\plain\\sb120\n";
1343     } elsif ($ptype eq "code") {
1344       # Code paragraph.
1345       print "\\keep\\f1\\sb120\n";
1346       foreach $i (@$pname) {
1347         my $x = $i;
1348         warn "code line longer than 68 chars: $i\n" if length $i > 68;
1349         $x =~ s/\\/\\\\/g;
1350         $x =~ s/\{/\\\{/g;
1351         $x =~ s/\}/\\\}/g;
1352         print "$x\\par\\sb0\n";
1353       }
1354       $newpar = "\\pard\\f0\\sb120\n";
1355     } elsif ($ptype eq "bull" || $ptype eq "norm") {
1356       # Ordinary paragraph, optionally bulleted. We wrap, FWIW.
1357       if ($ptype eq "bull") {
1358         print "\\tx360\\li360\\fi-360{\\f3\\'9F}\\tab\n";
1359         $newpar = "\\par\\pard\\sb120\n";
1360       } else {
1361         $newpar = "\\par\\sb120\n";
1362       }
1363       $line = '';
1364       @a = @$pname;
1365       $wd = $wprev = '';
1366       do {
1367         do { $w = &word_hlp((shift @a),1); } while $w eq "\001"; # hack
1368         $wd .= $wprev;
1369         if ($w eq ' ' || $w eq '' || $w eq undef) {
1370           if (length ($line . $wd) > 75) {
1371             $line =~ s/\s*$//; # trim trailing spaces
1372             print "$line \n"; # and put one back
1373             $line = '';
1374             $wd =~ s/^\s*//; # trim leading spaces
1375           }
1376           $line .= $wd;
1377           $wd = '';
1378         }
1379         $wprev = $w;
1380       } while ($w ne '' && $w ne undef);
1381       if ($line =~ /\S/) {
1382         $line =~ s/\s*$//; # trim trailing spaces
1383         print "$line\n";
1384       }
1385     }
1386   }
1387
1388   # Close file.
1389   print "\\page}\n";
1390   select STDOUT;
1391   close TEXT;
1392 }
1393
1394 sub word_hlp {
1395   my ($w, $docode) = @_;
1396   my $wtype, $wmajt;
1397
1398   return undef if $w eq '' || $w eq undef;
1399   $wtype = substr($w,0,2);
1400   $wmajt = substr($wtype,0,1);
1401   $w = substr($w,2);
1402   $w =~ s/\\/\\\\/g;
1403   $w =~ s/\{/\\\{/g;
1404   $w =~ s/\}/\\\}/g;
1405   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
1406   substr($w,0,length($w)-1) =~ s/-/\\\'AD/g if $wmajt ne "x"; #nonbreakhyphens
1407   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
1408     return $w;
1409   } elsif ($wtype eq "sp") {
1410     return ' ';
1411   } elsif ($wtype eq "da") {
1412     return "\\'96";
1413   } elsif ($wmajt eq "c" || $wtype eq "wc") {
1414     $w =~ s/ /\\\'A0/g; # make spaces non-breaking
1415     return $docode ? "{\\f1 ${w}}" : $w;
1416   } elsif ($wtype eq "es") {
1417     return "{\\i ${w}";
1418   } elsif ($wtype eq "ee") {
1419     return "${w}}";
1420   } elsif ($wtype eq "eo") {
1421     return "{\\i ${w}}";
1422   } elsif ($wtype eq "x ") {
1423     return "{\\uldb ";
1424   } elsif ($wtype eq "xe") {
1425     $w = &hlp_sectkw($w);
1426     return "}{\\v ${w}}";
1427   } elsif ($wmajt eq "i") {
1428     return "\001";
1429   } else {
1430     die "panic in word_hlp: $wtype$w\n";
1431   }
1432 }
1433
1434 sub hlp_menu {
1435   my ($topitem) = @_;
1436   my $item, $kword, $i, $mpname, $title;
1437
1438   $item = $tstruct_next{$topitem};
1439   print "\\li360\\fi-360\n";
1440   while ($item) {
1441     $title = "";
1442     $mpname = $tstruct_pname{$item};
1443     foreach $i (@$mpname) {
1444       $ww = &word_hlp($i, 0);
1445       $title .= $ww unless $ww eq "\001";
1446     }
1447     $kword = &hlp_sectkw($item);
1448     print "{\\uldb ${item}: $title}{\\v $kword}\\par\\sb0\n";
1449     $item = $tstruct_mnext{$item};
1450   }
1451   print "\\pard\\sb120\n";
1452 }
1453
1454 sub hlp_sectkw {
1455   my ($node) = @_;
1456   $node =~ tr/A-Z/a-z/;
1457   $node =~ tr/- ./___/;
1458   $node;
1459 }
1460
1461 sub hlp_keywords {
1462   my ($node) = @_;
1463   my $pfx = "K{\\footnote ";
1464   my $done = 0;
1465   foreach $i (0..$#itags) {
1466     (print $pfx,$hlp_index[$i]), $pfx = ";\n", $done++
1467         if $idxnodes{$node,$itags[$i]};
1468   }
1469   print "}\n" if $done;
1470 }
1471
1472 # Make tree structures. $tstruct_* is top-level and global.
1473 sub add_item {
1474   my ($item, $level) = @_;
1475   my $i;
1476
1477   $tstruct_pname{$item} = $pname;
1478   $tstruct_next{$tstruct_previtem} = $item;
1479   $tstruct_prev{$item} = $tstruct_previtem;
1480   $tstruct_level{$item} = $level;
1481   $tstruct_up{$item} = $tstruct_last[$level-1];
1482   $tstruct_mnext{$tstruct_last[$level]} = $item;
1483   $tstruct_last[$level] = $item;
1484   for ($i=$level+1; $i<$MAXLEVEL; $i++) { $tstruct_last[$i] = undef; }
1485   $tstruct_previtem = $item;
1486   push @nodes, $item;
1487 }
1488
1489 #
1490 # This produces documentation intermediate paragraph format; this is
1491 # basically the digested output of the front end.  Intended for use
1492 # by future backends, instead of putting it all in the same script.
1493 #
1494 sub write_dip {
1495   open(PARAS, "> nasmdoc.dip");
1496   foreach $k (keys(%metadata)) {
1497       print PARAS 'meta :', $k, "\n";
1498       print PARAS $metadata{$k},"\n";
1499   }
1500   for ($para = 0; $para <= $#pnames; $para++) {
1501       print PARAS $pflags[$para], "\n";
1502       print PARAS join("\037", @{$pnames[$para]}, "\n");
1503   }
1504   foreach $k (@itags) {
1505       print PARAS 'indx :', $k, "\n";
1506       print PARAS join("\037", @{$idxmap{$k}}), "\n";
1507   }
1508   close(PARAS);
1509 }