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