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