NASM 0.98
[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 # PS output:
9 # - show page numbers in printed output
10 # - think about double-sided support (start all chapters on RHS,
11 #   ie odd-numbered, pages).
12 #
13 # Ellipsis support would be nice.
14
15 # Source-form features:
16 # ---------------------
17
18 # Bullet \b
19 #   Bullets the paragraph. Rest of paragraph is indented to cope. In
20 #   HTML, consecutive groups of bulleted paragraphs become unordered
21 #   lists.
22
23 # Emphasis \e{foobar}
24 #   produces `_foobar_' in text and italics in HTML, PS, RTF
25
26 # Inline code \c{foobar}
27 #   produces ``foobar'' in text, and fixed-pitch font in HTML, PS, RTF
28
29 # Display code
30 # \c  line one
31 # \c   line two
32 #   produces fixed-pitch font where appropriate, and doesn't break
33 #   pages except sufficiently far into the middle of a display.
34
35 # Chapter, header and subheader
36 # \C{intro} Introduction
37 # \H{whatsnasm} What is NASM?
38 # \S{free} NASM Is Free
39 #   dealt with as appropriate. Chapters begin on new sides, possibly
40 #   even new _pages_. (Sub)?headers are good places to begin new
41 #   pages. Just _after_ a (sub)?header isn't.
42 #   The keywords can be substituted with \K and \k.
43 #
44 # Keyword \K{cintro} \k{cintro}
45 #   Expands to `Chapter 1', `Section 1.1', `Section 1.1.1'. \K has an
46 #   initial capital whereas \k doesn't. In HTML, will produce
47 #   hyperlinks.
48
49 # Web link \W{http://foobar/}{text} or \W{mailto:me@here}\c{me@here}
50 #   the \W prefix is ignored except in HTML; in HTML the last part
51 #   becomes a hyperlink to the first part.
52
53 # Literals \{ \} \\
54 #   In case it's necessary, they expand to the real versions.
55
56 # Nonbreaking hyphen \-
57 #   Need more be said?
58
59 # Source comment \#
60 #   Causes everything after it on the line to be ignored by the
61 #   source-form processor.
62 #
63 # Indexable word \i{foobar} (or \i\e{foobar} or \i\c{foobar}, equally)
64 #   makes word appear in index, referenced to that point
65 #   \i\c comes up in code style even in the index; \i\e doesn't come
66 #   up in emphasised style.
67 #
68 # Indexable non-displayed word \I{foobar} or \I\c{foobar}
69 #   just as \i{foobar} except that nothing is displayed for it
70 #
71 # Index rewrite
72 # \IR{foobar} \c{foobar} operator, uses of
73 #   tidies up the appearance in the index of something the \i or \I
74 #   operator was applied to
75 #
76 # Index alias
77 # \IA{foobar}{bazquux}
78 #   aliases one index tag (as might be supplied to \i or \I) to
79 #   another, so that \I{foobar} has the effect of \I{bazquux}, and
80 #   \i{foobar} has the effect of \I{bazquux}foobar
81
82 $diag = 1, shift @ARGV if $ARGV[0] eq "-d";
83
84 $| = 1;
85
86 $tstruct_previtem = $node = "Top";
87 $nodes = ($node);
88 $tstruct_level{$tstruct_previtem} = 0;
89 $tstruct_last[$tstruct_level{$tstruct_previtem}] = $tstruct_previtem;
90 $MAXLEVEL = 10;  # really 3, but play safe ;-)
91
92 # Read the file; pass a paragraph at a time to the paragraph processor.
93 print "Reading input...";
94 $pname = "para000000";
95 @pnames = @pflags = ();
96 $para = undef;
97 while (<>) {
98   chomp;
99   if (!/\S/ || /^\\I[AR]/) { # special case: \I[AR] implies new-paragraph
100     &got_para($para);
101     $para = undef;
102   }
103   if (/\S/) {
104     s/\\#.*$//; # strip comments
105     $para .= " " . $_;
106   }
107 }
108 &got_para($para);
109 print "done.\n";
110
111 # Now we've read in the entire document and we know what all the
112 # heading keywords refer to. Go through and fix up the \k references.
113 print "Fixing up cross-references...";
114 &fixup_xrefs;
115 print "done.\n";
116
117 # Sort the index tags, according to the slightly odd order I've decided on.
118 print "Sorting index tags...";
119 &indexsort;
120 print "done.\n";
121
122 if ($diag) {
123   print "Writing index-diagnostic file...";
124   &indexdiag;
125   print "done.\n";
126 }
127
128 # OK. Write out the various output files.
129 print "Producing text output: ";
130 &write_txt;
131 print "done.\n";
132 print "Producing HTML output: ";
133 &write_html;
134 print "done.\n";
135 print "Producing PostScript output: ";
136 &write_ps;
137 print "done.\n";
138 print "Producing Texinfo output: ";
139 &write_texi;
140 print "done.\n";
141 print "Producing WinHelp output: ";
142 &write_hlp;
143 print "done.\n";
144
145 sub got_para {
146   local ($_) = @_;
147   my $pflags = "", $i, $w, $l, $t;
148   return if !/\S/;
149
150   @$pname = ();
151
152   # Strip off _leading_ spaces, then determine type of paragraph.
153   s/^\s*//;
154   $irewrite = undef;
155   if (/^\\c[^{]/) {
156     # A code paragraph. The paragraph-array will contain the simple
157     # strings which form each line of the paragraph.
158     $pflags = "code";
159     while (/^\\c (([^\\]|\\[^c])*)(.*)$/) {
160       $l = $1;
161       $_ = $3;
162       $l =~ s/\\{/{/g;
163       $l =~ s/\\}/}/g;
164       $l =~ s/\\\\/\\/g;
165       push @$pname, $l;
166     }
167     $_ = ''; # suppress word-by-word code
168   } elsif (/^\\C/) {
169     # A chapter heading. Define the keyword and allocate a chapter
170     # number.
171     $cnum++;
172     $hnum = 0;
173     $snum = 0;
174     $xref = "chapter-$cnum";
175     $pflags = "chap $cnum :$xref";
176     die "badly formatted chapter heading: $_\n" if !/^\\C{([^}]*)}\s*(.*)$/;
177     $refs{$1} = "chapter $cnum";
178     $node = "Chapter $cnum";
179     &add_item($node, 1);
180     $xrefnodes{$node} = $xref; $nodexrefs{$xref} = $node;
181     $xrefs{$1} = $xref;
182     $_ = $2;
183     # the standard word-by-word code will happen next
184   } elsif (/^\\A/) {
185     # An appendix heading. Define the keyword and allocate an appendix
186     # letter.
187     $cnum++;
188     $cnum = 'A' if $cnum =~ /[0-9]+/;
189     $hnum = 0;
190     $snum = 0;
191     $xref = "appendix-$cnum";
192     $pflags = "appn $cnum :$xref";
193     die "badly formatted appendix heading: $_\n" if !/^\\A{([^}]*)}\s*(.*)$/;
194     $refs{$1} = "appendix $cnum";
195     $node = "Appendix $cnum";
196     &add_item($node, 1);
197     $xrefnodes{$node} = $xref; $nodexrefs{$xref} = $node;
198     $xrefs{$1} = $xref;
199     $_ = $2;
200     # the standard word-by-word code will happen next
201   } elsif (/^\\H/) {
202     # A major heading. Define the keyword and allocate a section number.
203     $hnum++;
204     $snum = 0;
205     $xref = "section-$cnum.$hnum";
206     $pflags = "head $cnum.$hnum :$xref";
207     die "badly formatted heading: $_\n" if !/^\\[HP]{([^}]*)}\s*(.*)$/;
208     $refs{$1} = "section $cnum.$hnum";
209     $node = "Section $cnum.$hnum";
210     &add_item($node, 2);
211     $xrefnodes{$node} = $xref; $nodexrefs{$xref} = $node;
212     $xrefs{$1} = $xref;
213     $_ = $2;
214     # the standard word-by-word code will happen next
215   } elsif (/^\\S/) {
216     # A sub-heading. Define the keyword and allocate a section number.
217     $snum++;
218     $xref = "section-$cnum.$hnum.$snum";
219     $pflags = "subh $cnum.$hnum.$snum :$xref";
220     die "badly formatted subheading: $_\n" if !/^\\S{([^}]*)}\s*(.*)$/;
221     $refs{$1} = "section $cnum.$hnum.$snum";
222     $node = "Section $cnum.$hnum.$snum";
223     &add_item($node, 3);
224     $xrefnodes{$node} = $xref; $nodexrefs{$xref} = $node;
225     $xrefs{$1} = $xref;
226     $_ = $2;
227     # the standard word-by-word code will happen next
228   } elsif (/^\\IR/) {
229     # An index-rewrite.
230     die "badly formatted index rewrite: $_\n" if !/^\\IR{([^}]*)}\s*(.*)$/;
231     $irewrite = $1;
232     $_ = $2;
233     # the standard word-by-word code will happen next
234   } elsif (/^\\IA/) {
235     # An index-alias.
236     die "badly formatted index alias: $_\n" if !/^\\IA{([^}]*)}{([^}]*)}\s*$/;
237     $idxalias{$1} = $2;
238     return; # avoid word-by-word code
239   } elsif (/^\\b/) {
240     # A bulleted paragraph. Strip off the initial \b and let the
241     # word-by-word code take care of the rest.
242     $pflags = "bull";
243     s/^\\b\s*//;
244   } else {
245     # A normal paragraph. Just set $pflags: the word-by-word code does
246     # the rest.
247     $pflags = "norm";
248   }
249
250   # The word-by-word code: unless @$pname is already defined (which it
251   # will be in the case of a code paragraph), split the paragraph up
252   # into words and push each on @$pname.
253   #
254   # Each thing pushed on @$pname should have a two-character type
255   # code followed by the text.
256   #
257   # Type codes are:
258   # "n " for normal
259   # "da" for a dash
260   # "es" for first emphasised word in emphasised bit
261   # "e " for emphasised in mid-emphasised-bit
262   # "ee" for last emphasised word in emphasised bit
263   # "eo" for single (only) emphasised word
264   # "c " for code
265   # "k " for cross-ref
266   # "kK" for capitalised cross-ref
267   # "w " for Web link
268   # "wc" for code-type Web link
269   # "x " for beginning of resolved cross-ref; generates no visible output,
270   #      and the text is the cross-reference code
271   # "xe" for end of resolved cross-ref; text is same as for "x ".
272   # "i " for point to be indexed: the text is the internal index into the
273   #      index-items arrays
274   # "sp" for space
275   while (/\S/) {
276     s/^\s*//, push @$pname, "sp" if /^\s/;
277     $indexing = $qindex = 0;
278     if (/^(\\[iI])?\\c/) {
279       $qindex = 1 if $1 eq "\\I";
280       $indexing = 1, s/^\\[iI]// if $1;
281       s/^\\c//;
282       die "badly formatted \\c: \\c$_\n" if !/{(([^\\}]|\\.)*)}(.*)$/;
283       $w = $1;
284       $_ = $3;
285       $w =~ s/\\{/{/g;
286       $w =~ s/\\}/}/g;
287       $w =~ s/\\-/-/g;
288       $w =~ s/\\\\/\\/g;
289       (push @$pname,"i"),$lastp = $#$pname if $indexing;
290       push @$pname,"c $w" if !$qindex;
291       $$pname[$lastp] = &addidx($node, $w, "c $w") if $indexing;
292     } elsif (/^\\[iIe]/) {
293       /^(\\[iI])?(\\e)?/;
294       $emph = 0;
295       $qindex = 1 if $1 eq "\\I";
296       $indexing = 1, $type = "\\i" if $1;
297       $emph = 1, $type = "\\e" if $2;
298       s/^(\\[iI])?(\\e?)//;
299       die "badly formatted $type: $type$_\n" if !/{(([^\\}]|\\.)*)}(.*)$/;
300       $w = $1;
301       $_ = $3;
302       $w =~ s/\\{/{/g;
303       $w =~ s/\\}/}/g;
304       $w =~ s/\\-/-/g;
305       $w =~ s/\\\\/\\/g;
306       $t = $emph ? "es" : "n ";
307       @ientry = ();
308       (push @$pname,"i"),$lastp = $#$pname if $indexing;
309       foreach $i (split /\s+/,$w) {  # \e and \i can be multiple words
310         push @$pname,"$t$i","sp" if !$qindex;
311         ($ii=$i) =~ tr/A-Z/a-z/, push @ientry,"n $ii","sp" if $indexing;
312         $t = $emph ? "e " : "n ";
313       }
314       $w =~ tr/A-Z/a-z/, pop @ientry if $indexing;
315       $$pname[$lastp] = &addidx($node, $w, @ientry) if $indexing;
316       pop @$pname if !$qindex; # remove final space
317       if (substr($$pname[$#$pname],0,2) eq "es" && !$qindex) {
318         substr($$pname[$#$pname],0,2) = "eo";
319       } elsif ($emph && !$qindex) {
320         substr($$pname[$#$pname],0,2) = "ee";
321       }
322     } elsif (/^\\[kK]/) {
323       $t = "k ";
324       $t = "kK" if /^\\K/;
325       s/^\\[kK]//;
326       die "badly formatted \\k: \\c$_\n" if !/{([^}]*)}(.*)$/;
327       $_ = $2;
328       push @$pname,"$t$1";
329     } elsif (/^\\W/) {
330       s/^\\W//;
331       die "badly formatted \\W: \\W$_\n"
332           if !/{([^}]*)}(\\i)?(\\c)?{(([^\\}]|\\.)*)}(.*)$/;
333       $l = $1;
334       $w = $4;
335       $_ = $6;
336       $t = "w ";
337       $t = "wc" if $3 eq "\\c";
338       $indexing = 1 if $2;
339       $w =~ s/\\{/{/g;
340       $w =~ s/\\}/}/g;
341       $w =~ s/\\-/-/g;
342       $w =~ s/\\\\/\\/g;
343       (push @$pname,"i"),$lastp = $#$pname if $indexing;
344       push @$pname,"$t<$l>$w";
345       $$pname[$lastp] = &addidx($node, $w, "c $w") if $indexing;
346     } else {
347       die "what the hell? $_\n" if !/^(([^\s\\\-]|\\[\\{}\-])*-?)(.*)$/;
348       die "painful death! $_\n" if !length $1;
349       $w = $1;
350       $_ = $3;
351       $w =~ s/\\{/{/g;
352       $w =~ s/\\}/}/g;
353       $w =~ s/\\-/-/g;
354       $w =~ s/\\\\/\\/g;
355       if ($w eq "-") {
356         push @$pname,"da";
357       } else {
358         push @$pname,"n $w";
359       }
360     }
361   }
362   if ($irewrite ne undef) {
363     &addidx(undef, $irewrite, @$pname);
364     @$pname = ();
365   } else {
366     push @pnames, $pname;
367     push @pflags, $pflags;
368     $pname++;
369   }
370 }
371
372 sub addidx {
373   my ($node, $text, @ientry) = @_;
374   $text = $idxalias{$text} || $text;
375   if ($node eq undef || !$idxmap{$text}) {
376     @$ientry = @ientry;
377     $idxmap{$text} = $ientry;
378     $ientry++;
379   }
380   if ($node) {
381     $idxnodes{$node,$text} = 1;
382     return "i $text";
383   }
384 }
385
386 sub indexsort {
387   my $iitem, $ientry, $i, $piitem, $pcval, $cval, $clrcval;
388
389   @itags = map { # get back the original data as the 1st elt of each list
390              $_->[0]
391            } sort { # compare auxiliary (non-first) elements of lists
392              $a->[1] cmp $b->[1] ||
393              $a->[2] cmp $b->[2] ||
394              $a->[0] cmp $b->[0]
395            } map { # transform array into list of 3-element lists
396              my $ientry = $idxmap{$_};
397              my $a = substr($$ientry[0],2);
398              $a =~ tr/A-Za-z//cd;
399              [$_, uc($a), substr($$ientry[0],0,2)]
400            } keys %idxmap;
401
402   # Having done that, check for comma-hood.
403   $cval = 0;
404   foreach $iitem (@itags) {
405     $ientry = $idxmap{$iitem};
406     $clrcval = 1;
407     $pcval = $cval;
408     FL:for ($i=0; $i <= $#$ientry; $i++) {
409       if ($$ientry[$i] =~ /^(n .*,)(.*)/) {
410         $$ientry[$i] = $1;
411         splice @$ientry,$i+1,0,"n $2" if length $2;
412         $commapos{$iitem} = $i+1;
413         $cval = join("\002", @$ientry[0..$i]);
414         $clrcval = 0;
415         last FL;
416       }
417     }
418     $cval = undef if $clrcval;
419     $commanext{$iitem} = $commaafter{$piitem} = 1
420       if $cval and ($cval eq $pcval);
421     $piitem = $iitem;
422   }
423 }
424
425 sub indexdiag {
426   my $iitem,$ientry,$w,$ww,$foo,$node;
427   open INDEXDIAG,">index.diag";
428   foreach $iitem (@itags) {
429     $ientry = $idxmap{$iitem};
430     print INDEXDIAG "<$iitem> ";
431     foreach $w (@$ientry) {
432       $ww = &word_txt($w);
433       print INDEXDIAG $ww unless $ww eq "\001";
434     }
435     print INDEXDIAG ":";
436     $foo = " ";
437     foreach $node (@nodes) {
438       (print INDEXDIAG $foo,$node), $foo = ", " if $idxnodes{$node,$iitem};
439     }
440     print INDEXDIAG "\n";
441   }
442   close INDEXDIAG;
443 }
444
445 sub fixup_xrefs {
446   my $pname, $p, $i, $j, $k, $caps, @repl;
447
448   for ($p=0; $p<=$#pnames; $p++) {
449     next if $pflags[$p] eq "code";
450     $pname = $pnames[$p];
451     for ($i=$#$pname; $i >= 0; $i--) {
452       if ($$pname[$i] =~ /^k/) {
453         $k = $$pname[$i];
454         $caps = ($k =~ /^kK/);
455         $k = substr($k,2);      
456         $repl = $refs{$k};
457         die "undefined keyword `$k'\n" unless $repl;
458         substr($repl,0,1) =~ tr/a-z/A-Z/ if $caps;
459         @repl = ();
460         push @repl,"x $xrefs{$k}";
461         foreach $j (split /\s+/,$repl) {
462           push @repl,"n $j";
463           push @repl,"sp";
464         }
465         pop @repl; # remove final space
466         push @repl,"xe$xrefs{$k}";
467         splice @$pname,$i,1,@repl;
468       }
469     }
470   }
471 }
472
473 sub write_txt {
474   # This is called from the top level, so I won't bother using
475   # my or local.
476
477   # Open file.
478   print "writing file...";
479   open TEXT,">nasmdoc.txt";
480   select TEXT;
481
482   # Preamble.
483   $title = "The Netwide Assembler: NASM";
484   $spaces = ' ' x ((75-(length $title))/2);
485   ($underscore = $title) =~ s/./=/g;
486   print "$spaces$title\n$spaces$underscore\n";
487
488   for ($para = 0; $para <= $#pnames; $para++) {
489     $pname = $pnames[$para];
490     $pflags = $pflags[$para];
491     $ptype = substr($pflags,0,4);
492
493     print "\n"; # always one of these before a new paragraph
494
495     if ($ptype eq "chap") {
496       # Chapter heading. "Chapter N: Title" followed by a line of
497       # minus signs.
498       $pflags =~ /chap (.*) :(.*)/;
499       $title = "Chapter $1: ";
500       foreach $i (@$pname) {
501         $ww = &word_txt($i);
502         $title .= $ww unless $ww eq "\001";
503       }
504       print "$title\n";
505       $title =~ s/./-/g;
506       print "$title\n";
507     } elsif ($ptype eq "appn") {
508       # Appendix heading. "Appendix N: Title" followed by a line of
509       # minus signs.
510       $pflags =~ /appn (.*) :(.*)/;
511       $title = "Appendix $1: ";
512       foreach $i (@$pname) {
513         $ww = &word_txt($i);
514         $title .= $ww unless $ww eq "\001";
515       }
516       print "$title\n";
517       $title =~ s/./-/g;
518       print "$title\n";
519     } elsif ($ptype eq "head" || $ptype eq "subh") {
520       # Heading or subheading. Just a number and some text.
521       $pflags =~ /.... (.*) :(.*)/;
522       $title = sprintf "%6s ", $1;
523       foreach $i (@$pname) {
524         $ww = &word_txt($i);
525         $title .= $ww unless $ww eq "\001";
526       }
527       print "$title\n";
528     } elsif ($ptype eq "code") {
529       # Code paragraph. Emit each line with a seven character indent.
530       foreach $i (@$pname) {
531         warn "code line longer than 68 chars: $i\n" if length $i > 68;
532         print ' 'x7, $i, "\n";
533       }
534     } elsif ($ptype eq "bull" || $ptype eq "norm") {
535       # Ordinary paragraph, optionally bulleted. We wrap, with ragged
536       # 75-char right margin and either 7 or 11 char left margin
537       # depending on bullets.
538       if ($ptype eq "bull") {
539         $line = ' 'x7 . '(*) ';
540         $next = ' 'x11;
541       } else {
542         $line = $next = ' 'x7;
543       }
544       @a = @$pname;
545       $wd = $wprev = '';
546       do {
547         do { $w = &word_txt(shift @a) } while $w eq "\001"; # nasty hack
548         $wd .= $wprev;
549         if ($wprev =~ /-$/ || $w eq ' ' || $w eq '' || $w eq undef) {
550           if (length ($line . $wd) > 75) {
551             $line =~ s/\s*$//; # trim trailing spaces
552             print "$line\n";
553             $line = $next;
554             $wd =~ s/^\s*//; # trim leading spaces
555           }
556           $line .= $wd;
557           $wd = '';
558         }
559         $wprev = $w;
560       } while ($w ne '' && $w ne undef);
561       if ($line =~ /\S/) {
562         $line =~ s/\s*$//; # trim trailing spaces
563         print "$line\n";
564       }
565     }
566   }
567
568   # Close file.
569   select STDOUT;
570   close TEXT;
571 }
572
573 sub word_txt {
574   my ($w) = @_;
575   my $wtype, $wmajt;
576
577   return undef if $w eq '' || $w eq undef;
578   $wtype = substr($w,0,2);
579   $wmajt = substr($wtype,0,1);
580   $w = substr($w,2);
581   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
582   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
583     return $w;
584   } elsif ($wtype eq "sp") {
585     return ' ';
586   } elsif ($wtype eq "da") {
587     return '-';
588   } elsif ($wmajt eq "c" || $wtype eq "wc") {
589     return "`${w}'";
590   } elsif ($wtype eq "es") {
591     return "_${w}";
592   } elsif ($wtype eq "ee") {
593     return "${w}_";
594   } elsif ($wtype eq "eo") {
595     return "_${w}_";
596   } elsif ($wmajt eq "x" || $wmajt eq "i") {
597     return "\001";
598   } else {
599     die "panic in word_txt: $wtype$w\n";
600   }
601 }
602
603 sub write_html {
604   # This is called from the top level, so I won't bother using
605   # my or local.
606
607   # Write contents file. Just the preamble, then a menu of links to the
608   # separate chapter files and the nodes therein.
609   print "writing contents file...";
610   open TEXT,">nasmdoc0.html";
611   select TEXT;
612   &html_preamble(0);
613   print "<p>This manual documents NASM, the Netwide Assembler: an assembler\n";
614   print "targetting the Intel x86 series of processors, with portable source.\n";
615   print "<p>";
616   for ($node = $tstruct_next{'Top'}; $node; $node = $tstruct_next{$node}) {
617     if ($tstruct_level{$node} == 1) {
618       # Invent a file name.
619       ($number = lc($xrefnodes{$node})) =~ s/.*-//;
620       $fname="nasmdocx.html";
621       substr($fname,8 - length $number, length $number) = $number;
622       $html_fnames{$node} = $fname;
623       $link = $fname;
624       print "<p>";
625     } else {
626       # Use the preceding filename plus a marker point.
627       $link = $fname . "#$xrefnodes{$node}";
628     }
629     $title = "$node: ";
630     $pname = $tstruct_pname{$node};
631     foreach $i (@$pname) {
632       $ww = &word_html($i);
633       $title .= $ww unless $ww eq "\001";
634     }
635     print "<a href=\"$link\">$title</a><br>\n";
636   }
637   print "<p><a href=\"nasmdoci.html\">Index</a>\n";
638   print "</body></html>\n";
639   select STDOUT;
640   close TEXT;
641
642   # Open a null file, to ensure output (eg random &html_jumppoints calls)
643   # goes _somewhere_.
644   print "writing chapter files...";
645   open TEXT,">/dev/null";
646   select TEXT;
647   $html_lastf = '';
648
649   $in_list = 0;
650
651   for ($para = 0; $para <= $#pnames; $para++) {
652     $pname = $pnames[$para];
653     $pflags = $pflags[$para];
654     $ptype = substr($pflags,0,4);
655
656     $in_list = 0, print "</ul>\n" if $in_list && $ptype ne "bull";
657     if ($ptype eq "chap") {
658       # Chapter heading. Begin a new file.
659       $pflags =~ /chap (.*) :(.*)/;
660       $title = "Chapter $1: ";
661       $xref = $2;
662       &html_jumppoints; print "</body></html>\n"; select STDOUT; close TEXT;
663       $html_lastf = $html_fnames{$chapternode};
664       $chapternode = $nodexrefs{$xref};
665       $html_nextf = $html_fnames{$tstruct_mnext{$chapternode}};
666       open TEXT,">$html_fnames{$chapternode}"; select TEXT; &html_preamble(1);
667       foreach $i (@$pname) {
668         $ww = &word_html($i);
669         $title .= $ww unless $ww eq "\001";
670       }
671       $h = "<h2><a name=\"$xref\">$title</a></h2>\n";
672       print $h; print FULL $h;
673     } elsif ($ptype eq "appn") {
674       # Appendix heading. Begin a new file.
675       $pflags =~ /appn (.*) :(.*)/;
676       $title = "Appendix $1: ";
677       $xref = $2;
678       &html_jumppoints; print "</body></html>\n"; select STDOUT; close TEXT;
679       $html_lastf = $html_fnames{$chapternode};
680       $chapternode = $nodexrefs{$xref};
681       $html_nextf = $html_fnames{$tstruct_mnext{$chapternode}};
682       open TEXT,">$html_fnames{$chapternode}"; select TEXT; &html_preamble(1);
683       foreach $i (@$pname) {
684         $ww = &word_html($i);
685         $title .= $ww unless $ww eq "\001";
686       }
687       print "<h2><a name=\"$xref\">$title</a></h2>\n";
688     } elsif ($ptype eq "head" || $ptype eq "subh") {
689       # Heading or subheading.
690       $pflags =~ /.... (.*) :(.*)/;
691       $hdr = ($ptype eq "subh" ? "h4" : "h3");
692       $title = $1 . " ";
693       $xref = $2;
694       foreach $i (@$pname) {
695         $ww = &word_html($i);
696         $title .= $ww unless $ww eq "\001";
697       }
698       print "<$hdr><a name=\"$xref\">$title</a></$hdr>\n";
699     } elsif ($ptype eq "code") {
700       # Code paragraph.
701       print "<p><pre>\n";
702       foreach $i (@$pname) {
703         $w = $i;
704         $w =~ s/&/&amp;/g;
705         $w =~ s/</&lt;/g;
706         $w =~ s/>/&gt;/g;
707         print $w, "\n";
708       }
709       print "</pre>\n";
710     } elsif ($ptype eq "bull" || $ptype eq "norm") {
711       # Ordinary paragraph, optionally bulleted. We wrap, with ragged
712       # 75-char right margin and either 7 or 11 char left margin
713       # depending on bullets.
714       if ($ptype eq "bull") {
715         $in_list = 1, print "<ul>\n" unless $in_list;
716         $line = '<li>';
717       } else {
718         $line = '<p>';
719       }
720       @a = @$pname;
721       $wd = $wprev = '';
722       do {
723         do { $w = &word_html(shift @a) } while $w eq "\001"; # nasty hack
724         $wd .= $wprev;
725         if ($w eq ' ' || $w eq '' || $w eq undef) {
726           if (length ($line . $wd) > 75) {
727             $line =~ s/\s*$//; # trim trailing spaces
728             print "$line\n";
729             $line = '';
730             $wd =~ s/^\s*//; # trim leading spaces
731           }
732           $line .= $wd;
733           $wd = '';
734         }
735         $wprev = $w;
736       } while ($w ne '' && $w ne undef);
737       if ($line =~ /\S/) {
738         $line =~ s/\s*$//; # trim trailing spaces
739         print "$line\n";
740       }
741     }
742   }
743
744   # Close whichever file was open.
745   &html_jumppoints;
746   print "</body></html>\n";
747   select STDOUT;
748   close TEXT;
749
750   print "\n   writing index file...";
751   open TEXT,">nasmdoci.html";
752   select TEXT;
753   &html_preamble(0);
754   print "<p align=center><a href=\"nasmdoc0.html\">Contents</a>\n";
755   print "<p>";
756   &html_index;
757   print "<p align=center><a href=\"nasmdoc0.html\">Contents</a>\n";
758   print "</body></html>\n";
759   select STDOUT;
760   close TEXT;
761 }
762
763 sub html_preamble {
764   print "<html><head><title>NASM Manual</title></head>\n";
765   print "<body><h1 align=center>The Netwide Assembler: NASM</h1>\n\n";
766   &html_jumppoints if $_[0];
767 }
768
769 sub html_jumppoints {
770   print "<p align=center>";
771   print "<a href=\"$html_nextf\">Next Chapter</a> |\n" if $html_nextf;
772   print "<a href=\"$html_lastf\">Previous Chapter</a> |\n" if $html_lastf;
773   print "<a href=\"nasmdoc0.html\">Contents</a> |\n";
774   print "<a href=\"nasmdoci.html\">Index</a>\n";
775 }
776
777 sub html_index {
778   my $itag, $a, @ientry, $sep, $w, $wd, $wprev, $line;
779
780   $chapternode = '';
781   foreach $itag (@itags) {
782     $ientry = $idxmap{$itag};
783     @a = @$ientry;
784     push @a, "n :";
785     $sep = 0;
786     foreach $node (@nodes) {
787       next if !$idxnodes{$node,$itag};
788       push @a, "n ," if $sep;
789       push @a, "sp", "x $xrefnodes{$node}", "n $node", "xe$xrefnodes{$node}";
790       $sep = 1;
791     }
792     $line = '';
793     do {
794       do { $w = &word_html(shift @a) } while $w eq "\001"; # nasty hack
795       $wd .= $wprev;
796       if ($w eq ' ' || $w eq '' || $w eq undef) {
797         if (length ($line . $wd) > 75) {
798           $line =~ s/\s*$//; # trim trailing spaces
799           print "$line\n";
800           $line = '';
801           $wd =~ s/^\s*//; # trim leading spaces
802         }
803         $line .= $wd;
804         $wd = '';
805       }
806       $wprev = $w;
807     } while ($w ne '' && $w ne undef);
808     if ($line =~ /\S/) {
809       $line =~ s/\s*$//; # trim trailing spaces
810       print "$line\n";
811     }
812     print "<br>\n";
813   }
814 }
815
816 sub word_html {
817   my ($w) = @_;
818   my $wtype, $wmajt, $pfx, $sfx;
819
820   return undef if $w eq '' || $w eq undef;
821
822   $wtype = substr($w,0,2);
823   $wmajt = substr($wtype,0,1);
824   $w = substr($w,2);
825   $pfx = $sfx = '';
826   $pfx = "<a href=\"$1\">", $sfx = "</a>", $w = $2
827     if $wmajt eq "w" && $w =~ /^<(.*)>(.*)$/;
828   $w =~ s/&/&amp;/g;
829   $w =~ s/</&lt;/g;
830   $w =~ s/>/&gt;/g;
831   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
832     return $pfx . $w . $sfx;
833   } elsif ($wtype eq "sp") {
834     return ' ';
835   } elsif ($wtype eq "da") {
836     return '-'; # sadly, en-dashes are non-standard in HTML
837   } elsif ($wmajt eq "c" || $wtype eq "wc") {
838     return $pfx . "<code><nobr>${w}</nobr></code>" . $sfx;
839   } elsif ($wtype eq "es") {
840     return "<em>${w}";
841   } elsif ($wtype eq "ee") {
842     return "${w}</em>";
843   } elsif ($wtype eq "eo") {
844     return "<em>${w}</em>";
845   } elsif ($wtype eq "x ") {
846     # Magic: we must resolve the cross reference into file and marker
847     # parts, then dispose of the file part if it's us, and dispose of
848     # the marker part if the cross reference describes the top node of
849     # another file.
850     my $node = $nodexrefs{$w}; # find the node we're aiming at
851     my $level = $tstruct_level{$node}; # and its level
852     my $up = $node, $uplev = $level-1;
853     $up = $tstruct_up{$up} while $uplev--; # get top node of containing file
854     my $file = ($up ne $chapternode) ? $html_fnames{$up} : "";
855     my $marker = ($level == 1 and $file) ? "" : "#$w";
856     return "<a href=\"$file$marker\">";
857   } elsif ($wtype eq "xe") {
858     return "</a>";
859   } elsif ($wmajt eq "i") {
860     return "\001";
861   } else {
862     die "panic in word_html: $wtype$w\n";
863   }
864 }
865
866 sub write_ps {
867   # This is called from the top level, so I won't bother using
868   # my or local.
869
870   # First, set up the font metric arrays.
871   &font_metrics;
872
873   # First stage: reprocess the source arrays into a list of
874   # lines, each of which is a list of word-strings, each of
875   # which has a single-letter font code followed by text.
876   # Each line also has an associated type, which will be
877   # used for final alignment and font selection and things.
878   #
879   # Font codes are:
880   #   n == Normal
881   #   e == Emphasised
882   #   c == Code
883   #  ' ' == space (no following text required)
884   #  '-' == dash (no following text required)
885   #
886   # Line types are:
887   #   chap == Chapter or appendix heading.
888   #   head == Major heading.
889   #   subh == Sub-heading.
890   #   Ccha == Contents entry for a chapter.
891   #   Chea == Contents entry for a heading.
892   #   Csub == Contents entry for a subheading.
893   #   cone == Code paragraph with just this one line on it.
894   #   cbeg == First line of multi-line code paragraph.
895   #   cbdy == Interior line of multi-line code paragraph.
896   #   cend == Final line of multi-line code paragraph.
897   #   none == Normal paragraph with just this one line on it.
898   #   nbeg == First line of multi-line normal paragraph.
899   #   nbdy == Interior line of multi-line normal paragraph.
900   #   nend == Final line of multi-line normal paragraph.
901   #   bone == Bulleted paragraph with just this one line on it.
902   #   bbeg == First line of multi-line bulleted paragraph.
903   #   bbdy == Interior line of multi-line bulleted paragraph.
904   #   bend == Final line of multi-line bulleted paragraph.
905   print "line-breaks...";
906   $lname = "psline000000";
907   $lnamei = "idx" . $lname;
908   @lnames = @ltypes = ();
909
910   for ($para = 0; $para <= $#pnames; $para++) {
911     $pname = $pnames[$para];
912     $pflags = $pflags[$para];
913     $ptype = substr($pflags,0,4);
914
915     # New paragraph _ergo_ new line.
916     @line = ();
917     @lindex = (); # list of index tags referenced to this line
918
919     if ($ptype eq "chap") {
920       # Chapter heading. "Chapter N: Title" followed by a line of
921       # minus signs.
922       $pflags =~ /chap (.*) :(.*)/;
923       push @line, "nChapter", " ", "n$1:", " ";
924       foreach $i (@$pname) {
925         $ww = &word_ps($i);
926         push @line, $ww unless $ww eq "x";
927       }
928       @$lname = @line; @$lnamei = @lindex;
929       push @lnames, $lname++;
930       $lnamei = "idx" . $lname;
931       push @ltypes, "chap";
932     } elsif ($ptype eq "appn") {
933       # Appendix heading. "Appendix N: Title" followed by a line of
934       # minus signs.
935       $pflags =~ /appn (.*) :(.*)/;
936       push @line, "nAppendix", " ", "n$1:", " ";
937       foreach $i (@$pname) {
938         $ww = &word_ps($i);
939         push @line, $ww unless $ww eq "x";
940       }
941       @$lname = @line; @$lnamei = @lindex;
942       push @lnames, $lname++;
943       $lnamei = "idx" . $lname;
944       push @ltypes, "chap";
945     } elsif ($ptype eq "head") {
946       # Heading. Just a number and some text.
947       $pflags =~ /.... (.*) :(.*)/;
948       push @line, "n$1";
949       foreach $i (@$pname) {
950         $ww = &word_ps($i);
951         push @line, $ww unless $ww eq "x";
952       }
953       @$lname = @line; @$lnamei = @lindex;
954       push @lnames, $lname++;
955       $lnamei = "idx" . $lname;
956       push @ltypes, $ptype;
957     } elsif ($ptype eq "subh") {
958       # Subheading. Just a number and some text.
959       $pflags =~ /subh (.*) :(.*)/;
960       push @line, "n$1";
961       foreach $i (@$pname) {
962         push @line, &word_ps($i);
963       }
964       @$lname = @line; @$lnamei = @lindex;
965       push @lnames, $lname++;
966       $lnamei = "idx" . $lname;
967       push @ltypes, "subh";
968     } elsif ($ptype eq "code") {
969       # Code paragraph. Emit lines one at a time.
970       $type = "cbeg";
971       foreach $i (@$pname) {
972         @$lname = ("c$i");
973         push @lnames, $lname++;
974         $lnamei = "idx" . $lname;
975         push @ltypes, $type;
976         $type = "cbdy";
977       }
978       $ltypes[$#ltypes] = ($ltypes[$#ltypes] eq "cbeg" ? "cone" : "cend");
979     } elsif ($ptype eq "bull" || $ptype eq "norm") {
980       # Ordinary paragraph, optionally bulleted. We wrap, with ragged
981       # 75-char right margin and either 7 or 11 char left margin
982       # depending on bullets.
983       if ($ptype eq "bull") {
984         $width = 456; # leave 12-pt left indent for the bullet
985         $type = $begtype = "bbeg";
986         $bodytype = "bbdy";
987         $onetype = "bone";
988         $endtype = "bend";
989       } else {
990         $width = 468;
991         $type = $begtype = "nbeg";
992         $bodytype = "nbdy";
993         $onetype = "none";
994         $endtype = "nend";
995       }
996       @a = @$pname;
997       @line = @wd = ();
998       $linelen = 0;
999       $wprev = undef;
1000       do {
1001         do { $w = &word_ps(shift @a) } while ($w eq "x");
1002         push @wd, $wprev if $wprev;
1003         if ($wprev =~ /^n.*-$/ || $w eq ' ' || $w eq '' || $w eq undef) {
1004           $wdlen = &len_ps(@wd);
1005           if ($linelen + $wdlen > $width) {
1006             pop @line while $line[$#line] eq ' '; # trim trailing spaces
1007             @$lname = @line; @$lnamei = @lindex;
1008             push @lnames, $lname++;
1009             $lnamei = "idx" . $lname;
1010             push @ltypes, $type;
1011             $type = $bodytype;
1012             @line = @lindex = ();
1013             $linelen = 0;
1014             shift @wd while $wd[0] eq ' '; # trim leading spaces
1015           }
1016           push @line, @wd;
1017           $linelen += $wdlen;
1018           @wd = ();
1019         }
1020         $wprev = $w;
1021       } while ($w ne '' && $w ne undef);
1022       if (@line) {
1023         pop @line while $line[$#line] eq ' '; # trim trailing spaces
1024         @$lname = @line; @$lnamei = @lindex;
1025         push @lnames, $lname++;
1026         $lnamei = "idx" . $lname;
1027         push @ltypes, $type;
1028         $type = $bodytype;
1029       }
1030       $ltypes[$#ltypes] =
1031         ($ltypes[$#ltypes] eq $begtype ? $onetype : $endtype);
1032     }
1033   }
1034
1035   # We've now processed the document source into lines. Before we
1036   # go on and do the page breaking, we'll fabricate a table of contents,
1037   # line by line, and then after doing page breaks we'll go back and
1038   # insert the page numbers into the contents entries.
1039   print "building contents...";
1040   @clnames = @cltypes = ();
1041   $clname = "pscont000000";
1042   @$clname = ("nContents"); # "chapter heading" for TOC
1043   push @clnames,$clname++;
1044   push @cltypes,"chap";
1045   for ($i=0; $i<=$#lnames; $i++) {
1046     $lname = $lnames[$i];
1047     if ($ltypes[$i] =~ /^(chap|head|subh)/) {
1048       @$clname = @$lname;
1049       splice @$clname,1,0," " if ($ltypes[$i] !~ /chap/);
1050       push @$clname,$i; # placeholder for page number
1051       push @clnames,$clname++;
1052       push @cltypes,"C" . substr($ltypes[$i],0,3);
1053     }
1054   }
1055   @$clname = ("nIndex"); # contents entry for Index
1056   push @$clname,$i;      # placeholder for page number
1057   $idx_clname = $clname;
1058   push @clnames,$clname++;
1059   push @cltypes,"Ccha";
1060   $contlen = $#clnames + 1;
1061   unshift @lnames,@clnames;
1062   unshift @ltypes,@cltypes;
1063
1064   # Second stage: now we have a list of lines, break them into pages.
1065   # We do this by means of adding a third array in parallel with
1066   # @lnames and @ltypes, called @lpages, in which we store the page
1067   # number that each line resides on. We also add @ycoord which
1068   # stores the vertical position of each line on the page.
1069   #
1070   # Page breaks may not come after line-types:
1071   #   chap head subh cbeg nbeg bbeg
1072   # and may not come before line-types:
1073   #   cend nend bend
1074   # They are forced before line-types:
1075   #   chap
1076   print "page-breaks...";
1077   $pmax = 600; # ADJUSTABLE: maximum length of a page in points
1078   $textht = 11; # ADJUSTABLE: height of a normal line in points
1079   $spacing = 6; # ADJUSTABLE: space between paragraphs, in points
1080   $headht = 14; # ADJUSTABLE: height of a major heading in points
1081   $subht = 12; # ADJUSTABLE: height of a sub-heading in points
1082   $pstart = 0; # start line of current page
1083   $plen = 0; # current length of current page
1084   $pnum = 1; # number of current page
1085   $bpt = -1; # last feasible break point
1086   $i = 0; # line number
1087   while ($i <= $#lnames) {
1088     $lname = $lnames[$i];
1089     # Add the height of this line (computed the last time we went round
1090     # the loop, unless we're a chapter heading in which case we do it
1091     # now) to the length of the current page. Also, _put_ this line on
1092     # the current page, and allocate it a y-coordinate.
1093     if ($ltypes[$i] =~ /^chap$/) {
1094       $plen = 100; # ADJUSTABLE: space taken up by a chapter heading
1095       $ycoord[$i] = 0; # chapter heading: y-coord doesn't matter
1096     } else {
1097       $ycoord[$i] = $plen + $space;
1098       $plen += $space + $ht;
1099     }
1100     # See if we can break after this line.
1101     $bpt = $i if $ltypes[$i] !~ /^chap|head|subh|cbeg|nbeg|bbeg$/ &&
1102                  $ltypes[$i+1] !~ /^cend|nend|bend$/;
1103     # Assume, to start with, that we don't break after this line.
1104     $break = 0;
1105     # See if a break is forced.
1106     $break = 1, $bpt = $i if $ltypes[$i+1] eq "chap" || !$ltypes[$i+1];
1107     # Otherwise, compute the height of the next line, and break if
1108     # it would make this page too long.
1109     $ht = $textht, $space = 0 if $ltypes[$i+1] =~ /^[nbc](bdy|end)$/;
1110     $ht = $textht, $space = $spacing if $ltypes[$i+1] =~ /^[nbc](one|beg)$/;
1111     $ht = $textht, $space = $spacing if $ltypes[$i+1] =~ /^C/;
1112     $ht = $subht, $space = $spacing if $ltypes[$i+1] eq "subh";
1113     $ht = $headht, $space = $spacing if $ltypes[$i+1] eq "head";
1114     $break = 1 if $plen + $space + $ht > $pmax;
1115     # Now, if we're breaking, assign page number $pnum to all lines up
1116     # to $bpt, set $i == $bpt+1, and zero $space since we are at the
1117     # start of a new page and don't want leading space.
1118     if ($break) {
1119       die "no feasible break point at all on page $pnum\n" if $bpt == -1;
1120       for ($j = $pstart; $j <= $bpt; $j++) {
1121         $lnamei = "idx" . $lnames[$j];
1122         foreach $k (@$lnamei) {
1123           ${$psidxpp{$k}}{$pnum} = 1;
1124         }
1125         $lpages[$j] = $pnum;
1126       }
1127       $pnum++;
1128       $i = $bpt;
1129       $bpt = -1;
1130       $pstart = $i+1;
1131       $plen = 0;
1132       $space = 0;
1133     }
1134     $i++;
1135   }
1136
1137   # Now fix up the TOC with page numbers.
1138   print "\n   fixing up contents...";
1139   for ($i=0; $i<=$#lnames; $i++) {
1140     $lname = $lnames[$i];
1141     if ($ltypes[$i] =~ /^C/) {
1142       $j = pop @$lname;
1143       push @$lname, "n" . $lpages[$j+$contlen];
1144     }
1145   }
1146
1147   # Having got page numbers for most stuff, generate an index.
1148   print "building index...";
1149   $iwid = 222;
1150   $sep = 12;
1151   $commaindent = 32;
1152   foreach $k (@itags) {
1153     @line = ();
1154     $cmd = "index";
1155     @idxentry = @{$idxmap{$k}};
1156     if ($commaafter{$k} and !$commanext{$k}) {
1157       # This line is a null line beginning a multiple entry. We must
1158       # output the prefix on a line by itself.
1159
1160       @idxhead = splice @idxentry,0,$commapos{$k};
1161       @line = ();
1162       foreach $i (@idxhead) {
1163         $ww = &word_ps($i);
1164         push @line, $ww unless $ww eq "x";
1165       }
1166       &ps_idxout("index",\@line,[]);
1167       $cmd = "iindex";
1168       @line = ();
1169     }
1170     $cmd = "iindex", splice @idxentry,0,$commapos{$k} if $commanext{$k};
1171     foreach $i (@idxentry) {
1172       $ww = &word_ps($i);
1173       push @line, $ww unless $ww eq "x";
1174     }
1175     $len = $iwid - $sep - &len_ps(@line);
1176     warn "text for index tag `%s' is longer than one index line!\n"
1177       if $len < -$sep;
1178     @pp = ();
1179     $inums = join(',',sort { $a <=> $b } keys %{$psidxpp{$k}});
1180     while (length $inums) {
1181       $inums =~ /^([^,]+,?)(.*)$/;
1182       $inums = $2, $inum = $1;
1183       @pnum = (" ", "n$inum");
1184       $pnumlen = &len_ps(@pnum);
1185       if ($pnumlen > $len) {
1186         &ps_idxout($cmd,\@line,\@pp);
1187         @pp = ();
1188         @line = ();
1189         $cmd = "index";
1190         $len = $iwid - $sep;
1191       }
1192       push @pp, @pnum;
1193       $len -= $pnumlen;
1194     }
1195     &ps_idxout($cmd,\@line,\@pp) if (length @pp);
1196     $l1 = &len_ps(@line);
1197     $l2 = &len_ps($pp);
1198   }
1199   $$idx_clname[$#$idx_clname] = "n" . $pnum; # fix up TOC entry for index
1200
1201   print "writing file...";
1202   open PS,">nasmdoc.ps";
1203   select PS;
1204   $page = $lpages[0];
1205   &ps_header;
1206   for ($i=0; $i<=$#lnames; $i++) {
1207     &ps_throw_pg($page,$lpages[$i]) if $page != $lpages[$i];
1208     $page = $lpages[$i];
1209     &ps_out_line($ycoord[$i],$ltypes[$i],$lnames[$i]);
1210   }
1211   $i = 0;
1212   while ($i <= $#psindex) {
1213     &ps_throw_pg($page, $pnum) if $page != $pnum;
1214     $page = $pnum++;
1215     $ypos = 0;
1216     $ypos = 100, &ps_out_line(0, "chap", ["nIndex"]) if !$i;
1217     $lines = ($pmax - $ypos) / $textht;
1218     my $col; # ps_out_line hits this variable
1219     PAGE:for ($col = 1; $col <= 2; $col++) {
1220       $y = $ypos; $l = $lines;
1221       COL: while ($l > 0) {
1222         $j = $i+1;
1223         $j++ while $psindex[$j] and ($psindex[$j][3] == 0); # find next break
1224         last COL if $j-$i > $l or $i > $#psindex;
1225         while ($i < $j) {
1226           &ps_out_line($y, $psindex[$i][0] eq "index" ? "idl$col" : "ldl$col",
1227                        $psindex[$i][1]);
1228           &ps_out_line($y,"idr$col",$psindex[$i][2]);
1229           $i++;
1230           $y += $textht;
1231           $l--;
1232         }
1233       }
1234       last PAGE if $i > $#psindex;
1235     }
1236   }
1237   &ps_trailer;
1238   close PS;
1239   select STDOUT;
1240 }
1241
1242 sub ps_idxout {
1243   my ($cmd, $left, $right) = @_;
1244   my $break = 1;
1245   $break = 0
1246       if ($#psindex >= 0) and ( ($#$left < 0) or ($cmd eq "iindex") );
1247   push @psindex,[$cmd,[@$left],[@$right],$break];
1248 }
1249
1250 sub ps_header {
1251   @pshdr = (
1252     '/sp (n ) def', # here it's sure not to get wrapped inside ()
1253     '/nf /Times-Roman findfont 11 scalefont def',
1254     '/ef /Times-Italic findfont 11 scalefont def',
1255     '/cf /Courier findfont 11 scalefont def',
1256     '/nc /Helvetica-Bold findfont 18 scalefont def',
1257     '/ec /Helvetica-Oblique findfont 18 scalefont def',
1258     '/cc /Courier-Bold findfont 18 scalefont def',
1259     '/nh /Helvetica-Bold findfont 14 scalefont def',
1260     '/eh /Helvetica-Oblique findfont 14 scalefont def',
1261     '/ch /Courier-Bold findfont 14 scalefont def',
1262     '/ns /Helvetica-Bold findfont 12 scalefont def',
1263     '/es /Helvetica-Oblique findfont 12 scalefont def',
1264     '/cs /Courier-Bold findfont 12 scalefont def',
1265     '/n 16#6E def /e 16#65 def /c 16#63 def',
1266     '/chapter {',
1267     '  100 620 moveto',
1268     '  {',
1269     '    dup 0 get',
1270     '    dup n eq {pop nc setfont} {',
1271     '      e eq {ec setfont} {cc setfont} ifelse',
1272     '    } ifelse',
1273     '    dup length 1 sub 1 exch getinterval show',
1274     '  } forall',
1275     '  0 setlinecap 3 setlinewidth',
1276     '  newpath 100 610 moveto 468 0 rlineto stroke',
1277     '} def',
1278     '/heading {',
1279     '  686 exch sub /y exch def /a exch def',
1280     '  90 y moveto a 0 get dup length 1 sub 1 exch getinterval',
1281     '  nh setfont dup stringwidth pop neg 0 rmoveto show',
1282     '  100 y moveto',
1283     '  a dup length 1 sub 1 exch getinterval {',
1284     '    /s exch def',
1285     '    s 0 get',
1286     '    dup n eq {pop nh setfont} {',
1287     '      e eq {eh setfont} {ch setfont} ifelse',
1288     '    } ifelse',
1289     '    s s length 1 sub 1 exch getinterval show',
1290     '  } forall',
1291     '} def',
1292     '/subhead {',
1293     '  688 exch sub /y exch def /a exch def',
1294     '  90 y moveto a 0 get dup length 1 sub 1 exch getinterval',
1295     '  ns setfont dup stringwidth pop neg 0 rmoveto show',
1296     '  100 y moveto',
1297     '  a dup length 1 sub 1 exch getinterval {',
1298     '    /s exch def',
1299     '    s 0 get',
1300     '    dup n eq {pop ns setfont} {',
1301     '      e eq {es setfont} {cs setfont} ifelse',
1302     '    } ifelse',
1303     '    s s length 1 sub 1 exch getinterval show',
1304     '  } forall',
1305     '} def',
1306     '/disp { /j exch def',
1307     '  568 exch sub exch 689 exch sub moveto',
1308     '  {',
1309     '    /s exch def',
1310     '    s 0 get',
1311     '    dup n eq {pop nf setfont} {',
1312     '      e eq {ef setfont} {cf setfont} ifelse',
1313     '    } ifelse',
1314     '    s s length 1 sub 1 exch getinterval show',
1315     '    s sp eq {j 0 rmoveto} if',
1316     '  } forall',
1317     '} def',
1318     '/contents { /w exch def /y exch def /a exch def',
1319     '  /yy 689 y sub def',
1320     '  a a length 1 sub get dup length 1 sub 1 exch getinterval /s exch def',
1321     '  nf setfont 568 s stringwidth pop sub /ex exch def',
1322     '  ex yy moveto s show',
1323     '  a 0 a length 1 sub getinterval y w 0 disp',
1324     '  /sx currentpoint pop def nf setfont',
1325     '  100 10 568 { /i exch def',
1326     '    i 5 sub sx gt i 5 add ex lt and {',
1327     '      i yy moveto (.) show',
1328     '    } if',
1329     '  } for',
1330     '} def',
1331     '/just { /w exch def /y exch def /a exch def',
1332     '  /jj w def /spaces 0 def',
1333     '  a {',
1334     '    /s exch def',
1335     '    s 0 get',
1336     '    dup n eq {pop nf setfont} {',
1337     '      e eq {ef setfont} {cf setfont} ifelse',
1338     '    } ifelse',
1339     '    s s length 1 sub 1 exch getinterval stringwidth pop',
1340     '    jj exch sub /jj exch def',
1341     '    s sp eq {/spaces spaces 1 add def} if',
1342     '  } forall',
1343     '  a y w jj spaces spaces 0 eq {pop pop 0} {div} ifelse disp',
1344     '} def',
1345     '/idl { 468 exch sub 0 disp } def',
1346     '/ldl { 436 exch sub 0 disp } def',
1347     '/idr { 222 add 468 exch sub /x exch def /y exch def /a exch def',
1348     '  a {',
1349     '    /s exch def',
1350     '    s 0 get',
1351     '    dup n eq {pop nf setfont} {',
1352     '      e eq {ef setfont} {cf setfont} ifelse',
1353     '    } ifelse',
1354     '    s s length 1 sub 1 exch getinterval stringwidth pop',
1355     '    x add /x exch def',
1356     '  } forall',
1357     '  a y x 0 disp',
1358     '} def',
1359     '/left {0 disp} def',
1360     '/bullet {',
1361     '  nf setfont dup 100 exch 689 exch sub moveto (\267) show',
1362     '} def'
1363   );
1364   print "%!PS-Adobe-3.0\n";
1365   print "%%BoundingBox: 95 95 590 705\n";
1366   print "%%Creator: a nasty Perl script\n";
1367   print "%%DocumentData: Clean7Bit\n";
1368   print "%%Orientation: Portrait\n";
1369   print "%%Pages: $lpages[$#lpages]\n";
1370   print "%%DocumentNeededResources: font Times-Roman Times-Italic\n";
1371   print "%%+ font Helvetica-Bold Courier Courier-Bold\n";
1372   print "%%EndComments\n%%BeginProlog\n%%EndProlog\n%%BeginSetup\nsave\n";
1373   $pshdr = join(' ',@pshdr);
1374   $pshdr =~ s/\s+/ /g;
1375   while ($pshdr =~ /\S/) {
1376     last if length($pshdr) < 72 || $pshdr !~ /^(.{0,72}\S)\s(.*)$/;
1377     $pshdr = $2;
1378     print "$1\n";
1379   }
1380   print "$pshdr\n" if $pshdr =~ /\S/;
1381   print "%%EndSetup\n";
1382   &ps_initpg($lpages[0]);
1383 }
1384
1385 sub ps_trailer {
1386   &ps_donepg;
1387   print "%%Trailer\nrestore\n%%EOF\n";
1388 }
1389
1390 sub ps_throw_pg {
1391   my ($oldpg, $newpg) = @_;
1392   &ps_donepg;
1393   &ps_initpg($newpg);
1394 }
1395
1396 sub ps_initpg {
1397   my ($pgnum) = @_;
1398   print "%%Page: $pgnum $pgnum\n";
1399   print "%%BeginPageSetup\nsave\n%%EndPageSetup\n";
1400 }
1401
1402 sub ps_donepg {
1403   print "%%PageTrailer\nrestore showpage\n";
1404 }
1405
1406 sub ps_out_line {
1407   my ($ypos,$ltype,$lname) = @_;
1408   my $c,$d,$wid;
1409
1410   print "[";
1411   $col = 1;
1412   foreach $c (@$lname) {#
1413     $c= "n " if $c eq " ";
1414     $c = "n\261" if $c eq "-";
1415     $d = '';
1416     while (length $c) {
1417       $d .= $1, $c = $2 while $c =~ /^([ -'\*-\[\]-~]+)(.*)$/;
1418       while (1) {
1419         $d .= "\\$1", $c = $2, next if $c =~ /^([\\\(\)])(.*)$/;
1420         ($d .= sprintf "\\%3o",unpack("C",$1)), $c = $2, next
1421           if $c =~ /^([^ -~])(.*)$/;
1422         last;
1423       }
1424     }
1425     $d = "($d)";
1426     $col = 0, print "\n" if $col>0 && $col+length $d > 77;
1427     print $d;
1428     $col += length $d;
1429   }
1430   print "\n" if $col > 60;
1431   print "]";
1432   if ($ltype =~ /^[nb](beg|bdy)$/) {
1433     printf "%d %s%d just\n",
1434       $ypos, ($ltype eq "bbeg" ? "bullet " : ""),
1435       ($ltype =~ /^b/ ? 456 : 468);
1436   } elsif ($ltype =~ /^[nb](one|end)$/) {
1437     printf "%d %s%d left\n",
1438       $ypos, ($ltype eq "bone" ? "bullet " : ""),
1439       ($ltype =~ /^b/ ? 456 : 468);
1440   } elsif ($ltype =~ /^c(one|beg|bdy|end)$/) {
1441     printf "$ypos 468 left\n";
1442   } elsif ($ltype =~ /^C/) {
1443     $wid = 468;
1444     $wid = 456 if $ltype eq "Chea";
1445     $wid = 444 if $ltype eq "Csub";
1446     printf "$ypos $wid contents\n";
1447   } elsif ($ltype eq "chap") {
1448     printf "chapter\n";
1449   } elsif ($ltype eq "head") {
1450     printf "$ypos heading\n";
1451   } elsif ($ltype eq "subh") {
1452     printf "$ypos subhead\n";
1453   } elsif ($ltype =~ /([il]d[lr])([12])/) {
1454     $left = ($2 eq "2" ? 468-222 : 0);
1455     printf "$ypos $left $1\n";
1456   }
1457 }
1458
1459 sub word_ps {
1460   my ($w) = @_;
1461   my $wtype, $wmajt;
1462
1463   return undef if $w eq '' || $w eq undef;
1464
1465   $wtype = substr($w,0,2);
1466   $wmajt = substr($wtype,0,1);
1467   $w = substr($w,2);
1468   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
1469   if ($wmajt eq "n" || $wtype eq "w ") {
1470     return "n$w";
1471   } elsif ($wtype eq "sp") {
1472     return ' ';
1473   } elsif ($wtype eq "da") {
1474     return '-';
1475   } elsif ($wmajt eq "c" || $wtype eq "wc") {
1476     return "c$w";
1477   } elsif ($wmajt eq "e") {
1478     return "e$w";
1479   } elsif ($wmajt eq "x") {
1480     return "x";
1481   } elsif ($wtype eq "i ") {
1482     push @lindex, $w;
1483     return "x";
1484   } else {
1485     die "panic in word_ps: $wtype$w\n";
1486   }
1487 }
1488
1489 sub len_ps {
1490   my (@line) = @_;
1491   my $l = 0;
1492   my $w, $size;
1493
1494   $size = 11/1000; # used only for length calculations
1495   while ($w = shift @line) {
1496     $w = "n " if $w eq " ";
1497     $w = "n\261" if $w eq "-";
1498     $f = substr($w,0,1);
1499     $f = "timesr" if $f eq "n";
1500     $f = "timesi" if $f eq "e";
1501     $f = "courr" if $f eq "c";
1502     foreach $c (unpack 'C*',substr($w,1)) {
1503       $l += $size * $$f[$c];
1504     }
1505   }
1506   return $l;
1507 }
1508
1509 sub write_texi {
1510   # This is called from the top level, so I won't bother using
1511   # my or local.
1512
1513   # Open file.
1514   print "writing file...";
1515   open TEXT,">nasmdoc.texi";
1516   select TEXT;
1517
1518   # Preamble.
1519   print "\input texinfo   \@c -*-texinfo-*-\n";
1520   print "\@c \%**start of header\n";
1521   print "\@setfilename nasm.info\n";
1522   print "\@dircategory Programming\n";
1523   print "\@direntry\n";
1524   print "* NASM: (nasm).                The Netwide Assembler for x86.\n";
1525   print "\@end direntry\n";
1526   print "\@settitle NASM: The Netwide Assembler\n";
1527   print "\@setchapternewpage odd\n";
1528   print "\@c \%**end of header\n";
1529   print "\n";
1530   print "\@ifinfo\n";
1531   print "This file documents NASM, the Netwide Assembler: an assembler\n";
1532   print "targetting the Intel x86 series of processors, with portable source.\n";
1533   print "\n";
1534   print "Copyright 1997 Simon Tatham\n";
1535   print "\n";
1536   print "All rights reserved. This document is redistributable under the\n";
1537   print "licence given in the file \"Licence\" distributed in the NASM archive.\n";
1538   print "\@end ifinfo\n";
1539   print "\n";
1540   print "\@titlepage\n";
1541   print "\@title NASM: The Netwide Assembler\n";
1542   print "\@author Simon Tatham\n";
1543   print "\n";
1544   print "\@page\n";
1545   print "\@vskip 0pt plus 1filll\n";
1546   print "Copyright \@copyright{} 1997 Simon Tatham\n";
1547   print "\n";
1548   print "All rights reserved. This document is redistributable under the\n";
1549   print "licence given in the file \"Licence\" distributed in the NASM archive.\n";
1550   print "\@end titlepage\n";
1551   print "\n";
1552   print "\@node Top, $tstruct_next{'Top'}, (dir), (dir)\n";
1553   print "\@top\n";
1554   print "\n";
1555   print "\@ifinfo\n";
1556   print "This file documents NASM, the Netwide Assembler: an assembler\n";
1557   print "targetting the Intel x86 series of processors, with portable source.\n";
1558   print "\@end ifinfo\n";
1559
1560   $node = "Top";
1561
1562   $bulleting = 0;
1563   for ($para = 0; $para <= $#pnames; $para++) {
1564     $pname = $pnames[$para];
1565     $pflags = $pflags[$para];
1566     $ptype = substr($pflags,0,4);
1567
1568     $bulleting = 0, print "\@end itemize\n" if $bulleting && $ptype ne "bull";
1569     print "\n"; # always one of these before a new paragraph
1570
1571     if ($ptype eq "chap") {
1572       # Chapter heading. Begin a new node.
1573       &texi_menu($node)
1574         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1575       $pflags =~ /chap (.*) :(.*)/;
1576       $node = "Chapter $1";
1577       $title = "Chapter $1: ";
1578       foreach $i (@$pname) {
1579         $ww = &word_texi($i);
1580         $title .= $ww unless $ww eq "\001";
1581       }
1582       print "\@node $node, $tstruct_next{$node}, $tstruct_prev{$node},";
1583       print " $tstruct_up{$node}\n\@unnumbered $title\n";
1584     } elsif ($ptype eq "appn") {
1585       # Appendix heading. Begin a new node.
1586       &texi_menu($node)
1587         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1588       $pflags =~ /appn (.*) :(.*)/;
1589       $node = "Appendix $1";
1590       $title = "Appendix $1: ";
1591       foreach $i (@$pname) {
1592         $ww = &word_texi($i);
1593         $title .= $ww unless $ww eq "\001";
1594       }
1595       print "\@node $node, $tstruct_next{$node}, $tstruct_prev{$node},";
1596       print " $tstruct_up{$node}\n\@unnumbered $title\n";
1597     } elsif ($ptype eq "head" || $ptype eq "subh") {
1598       # Heading or subheading. Begin a new node.
1599       &texi_menu($node)
1600         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1601       $pflags =~ /.... (.*) :(.*)/;
1602       $node = "Section $1";
1603       $title = "$1. ";
1604       foreach $i (@$pname) {
1605         $ww = &word_texi($i);
1606         $title .= $ww unless $ww eq "\001";
1607       }
1608       print "\@node $node, $tstruct_next{$node}, $tstruct_prev{$node},";
1609       print " $tstruct_up{$node}\n\@unnumbered $title\n";
1610     } elsif ($ptype eq "code") {
1611       # Code paragraph. Surround with @example / @end example.
1612       print "\@example\n";
1613       foreach $i (@$pname) {
1614         warn "code line longer than 68 chars: $i\n" if length $i > 68;
1615         $i =~ s/\@/\@\@/g;
1616         $i =~ s/\{/\@\{/g;
1617         $i =~ s/\}/\@\}/g;
1618         print "$i\n";
1619       }
1620       print "\@end example\n";
1621     } elsif ($ptype eq "bull" || $ptype eq "norm") {
1622       # Ordinary paragraph, optionally bulleted. We wrap, FWIW.
1623       if ($ptype eq "bull") {
1624         $bulleting = 1, print "\@itemize \@bullet\n" if !$bulleting;
1625         print "\@item\n";
1626       }
1627       $line = '';
1628       @a = @$pname;
1629       $wd = $wprev = '';
1630       do {
1631         do { $w = &word_texi(shift @a); } while $w eq "\001"; # hack
1632         $wd .= $wprev;
1633         if ($wprev =~ /-$/ || $w eq ' ' || $w eq '' || $w eq undef) {
1634           if (length ($line . $wd) > 75) {
1635             $line =~ s/\s*$//; # trim trailing spaces
1636             print "$line\n";
1637             $line = '';
1638             $wd =~ s/^\s*//; # trim leading spaces
1639           }
1640           $line .= $wd;
1641           $wd = '';
1642         }
1643         $wprev = $w;
1644       } while ($w ne '' && $w ne undef);
1645       if ($line =~ /\S/) {
1646         $line =~ s/\s*$//; # trim trailing spaces
1647         print "$line\n";
1648       }
1649     }
1650   }
1651
1652   # Write index.
1653   &texi_index;
1654
1655   # Close file.
1656   print "\n\@contents\n\@bye\n";
1657   select STDOUT;
1658   close TEXT;
1659 }
1660
1661 # Side effect of this procedure: update global `texiwdlen' to be the length
1662 # in chars of the formatted version of the word.
1663 sub word_texi {
1664   my ($w) = @_;
1665   my $wtype, $wmajt;
1666
1667   return undef if $w eq '' || $w eq undef;
1668   $wtype = substr($w,0,2);
1669   $wmajt = substr($wtype,0,1);
1670   $w = substr($w,2);
1671   $wlen = length $w;
1672   $w =~ s/\@/\@\@/g;
1673   $w =~ s/\{/\@\{/g;
1674   $w =~ s/\}/\@\}/g;
1675   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
1676   substr($w,0,1) =~ tr/a-z/A-Z/, $capital = 0 if $capital;
1677   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
1678     $texiwdlen = $wlen;
1679     return $w;
1680   } elsif ($wtype eq "sp") {
1681     $texiwdlen = 1;
1682     return ' ';
1683   } elsif ($wtype eq "da") {
1684     $texiwdlen = 2;
1685     return '--';
1686   } elsif ($wmajt eq "c" || $wtype eq "wc") {
1687     $texiwdlen = 2 + $wlen;
1688     return "\@code\{$w\}";
1689   } elsif ($wtype eq "es") {
1690     $texiwdlen = 1 + $wlen;
1691     return "\@emph\{${w}";
1692   } elsif ($wtype eq "ee") {
1693     $texiwdlen = 1 + $wlen;
1694     return "${w}\}";
1695   } elsif ($wtype eq "eo") {
1696     $texiwdlen = 2 + $wlen;
1697     return "\@emph\{${w}\}";
1698   } elsif ($wtype eq "x ") {
1699     $texiwdlen = 0; # we don't need it in this case
1700     $capital = 1; # hack
1701     return "\@ref\{";
1702   } elsif ($wtype eq "xe") {
1703     $texiwdlen = 0; # we don't need it in this case
1704     return "\}";
1705   } elsif ($wmajt eq "i") {
1706     $texiwdlen = 0; # we don't need it in this case
1707     return "\001";
1708   } else {
1709     die "panic in word_texi: $wtype$w\n";
1710   }
1711 }
1712
1713 sub texi_menu {
1714   my ($topitem) = @_;
1715   my $item, $i, $mpname, $title, $wd;
1716
1717   $item = $tstruct_next{$topitem};
1718   print "\@menu\n";
1719   while ($item) {
1720     $title = "";
1721     $mpname = $tstruct_pname{$item};
1722     foreach $i (@$mpname) {
1723       $wd = &word_texi($i);
1724       $title .= $wd unless $wd eq "\001";
1725     }
1726     print "* ${item}:: $title\n";
1727     $item = $tstruct_mnext{$item};
1728   }
1729   print "* Index::\n" if $topitem eq "Top";
1730   print "\@end menu\n";
1731 }
1732
1733 sub texi_index {
1734   my $itag, $ientry, @a, $wd, $item, $len;
1735   my $subnums = "123456789ABCDEFGHIJKLMNOPQRSTU" .
1736                 "VWXYZabcdefghijklmnopqrstuvwxyz";
1737
1738   print "\@ifinfo\n\@node Index, , $FIXMElastnode, Top\n";
1739   print "\@unnumbered Index\n\n\@menu\n";
1740
1741   foreach $itag (@itags) {
1742     $ientry = $idxmap{$itag};
1743     @a = @$ientry;
1744     $item = '';
1745     $len = 0;
1746     foreach $i (@a) {
1747       $wd = &word_texi($i);
1748       $item .= $wd, $len += $texiwdlen unless $wd eq "\001";
1749     }
1750     $i = 0;
1751     foreach $node (@nodes) {
1752       next if !$idxnodes{$node,$itag};
1753       printf "* %s%s (%s): %s.\n",
1754           $item, " " x (40-$len), substr($subnums,$i++,1), $node;
1755     }
1756   }
1757   print "\@end menu\n\@end ifinfo\n";
1758 }
1759
1760 sub write_hlp {
1761   # This is called from the top level, so I won't bother using
1762   # my or local.
1763
1764   # Build the index-tag text forms.
1765   print "building index entries...";
1766   @hlp_index = map {
1767                  my $i,$ww;
1768                  my $ientry = $idxmap{$_};
1769                  my $title = "";
1770                  foreach $i (@$ientry) {
1771                    $ww = &word_hlp($i,0);
1772                    $title .= $ww unless $ww eq "\001";
1773                  }
1774                  $title;
1775                } @itags;
1776
1777   # Write the HPJ project-description file.
1778   print "writing .hpj file...";
1779   open HPJ,">nasmdoc.hpj";
1780   print HPJ "[OPTIONS]\ncompress=true\n";
1781   print HPJ "title=NASM: The Netwide Assembler\noldkeyphrase=no\n\n";
1782   print HPJ "[FILES]\nnasmdoc.rtf\n\n";
1783   print HPJ "[CONFIG]\n";
1784   print HPJ 'CreateButton("btn_up", "&Up",'.
1785             ' "JumpContents(`nasmdoc.hlp'."'".')")';
1786   print HPJ "\nBrowseButtons()\n";
1787   close HPJ;
1788
1789   # Open file.
1790   print "\n   writing .rtf file...";
1791   open TEXT,">nasmdoc.rtf";
1792   select TEXT;
1793
1794   # Preamble.
1795   print "{\\rtf1\\ansi{\\fonttbl\n";
1796   print "\\f0\\froman Times New Roman;\\f1\\fmodern Courier New;\n";
1797   print "\\f2\\fswiss Arial;\\f3\\ftech Wingdings}\\deff0\n";
1798   print "#{\\footnote Top}\n";
1799   print "\${\\footnote Contents}\n";
1800   print "+{\\footnote browse:00000}\n";
1801   print "!{\\footnote DisableButton(\"btn_up\")}\n";
1802   print "\\keepn\\f2\\b\\fs30\\sb0\n";
1803   print "NASM: The Netwide Assembler\n";
1804   print "\\par\\pard\\plain\\sb120\n";
1805   print "This file documents NASM, the Netwide Assembler: an assembler \n";
1806   print "targetting the Intel x86 series of processors, with portable source.\n";
1807
1808   $node = "Top";
1809   $browse = 0;
1810
1811   $newpar = "\\par\\sb120\n";
1812   for ($para = 0; $para <= $#pnames; $para++) {
1813     $pname = $pnames[$para];
1814     $pflags = $pflags[$para];
1815     $ptype = substr($pflags,0,4);
1816
1817     print $newpar;
1818     $newpar = "\\par\\sb120\n";
1819
1820     if ($ptype eq "chap") {
1821       # Chapter heading. Begin a new node.
1822       &hlp_menu($node)
1823         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1824       $pflags =~ /chap (.*) :(.*)/;
1825       $node = "Chapter $1";
1826       $title = $footnotetitle = "Chapter $1: ";
1827       foreach $i (@$pname) {
1828         $ww = &word_hlp($i,1);
1829         $title .= $ww, $footnotetitle .= &word_hlp($i,0) unless $ww eq "\001";
1830       }
1831       print "\\page\n";
1832       printf "#{\\footnote %s}\n", &hlp_sectkw($node);
1833       print "\${\\footnote $footnotetitle}\n";
1834       printf "+{\\footnote browse:%05d}\n", ++$browse;
1835       printf "!{\\footnote ChangeButtonBinding(\"btn_up\"," .
1836              "\"JumpId(\`nasmdoc.hlp',\`%s')\");\n",
1837              &hlp_sectkw($tstruct_up{$node});
1838       print "EnableButton(\"btn_up\")}\n";
1839       &hlp_keywords($node);
1840       print "\\keepn\\f2\\b\\fs30\\sb60\\sa60\n";
1841       print "$title\n";
1842       $newpar = "\\par\\pard\\plain\\sb120\n";
1843     } elsif ($ptype eq "appn") {
1844       # Appendix heading. Begin a new node.
1845       &hlp_menu($node)
1846         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1847       $pflags =~ /appn (.*) :(.*)/;
1848       $node = "Appendix $1";
1849       $title = $footnotetitle = "Appendix $1: ";
1850       foreach $i (@$pname) {
1851         $ww = &word_hlp($i,1);
1852         $title .= $ww, $footnotetitle .= &word_hlp($i,0) unless $ww eq "\001";
1853       }
1854       print "\\page\n";
1855       printf "#{\\footnote %s}\n", &hlp_sectkw($node);
1856       print "\${\\footnote $footnotetitle}\n";
1857       printf "+{\\footnote browse:%05d}\n", ++$browse;
1858       printf "!{\\footnote ChangeButtonBinding(\"btn_up\"," .
1859              "\"JumpId(\`nasmdoc.hlp',\`%s')\");\n",
1860              &hlp_sectkw($tstruct_up{$node});
1861       print "EnableButton(\"btn_up\")}\n";
1862       &hlp_keywords($node);
1863       print "\\keepn\\f2\\b\\fs30\\sb60\\sa60\n";
1864       print "$title\n";
1865       $newpar = "\\par\\pard\\plain\\sb120\n";
1866     } elsif ($ptype eq "head" || $ptype eq "subh") {
1867       # Heading or subheading. Begin a new node.
1868       &hlp_menu($node)
1869         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1870       $pflags =~ /.... (.*) :(.*)/;
1871       $node = "Section $1";
1872       $title = $footnotetitle = "$1. ";
1873       foreach $i (@$pname) {
1874         $ww = &word_hlp($i,1);
1875         $title .= $ww, $footnotetitle .= &word_hlp($i,0) unless $ww eq "\001";
1876       }
1877       print "\\page\n";
1878       printf "#{\\footnote %s}\n", &hlp_sectkw($node);
1879       print "\${\\footnote $footnotetitle}\n";
1880       printf "+{\\footnote browse:%05d}\n", ++$browse;
1881       printf "!{\\footnote ChangeButtonBinding(\"btn_up\"," .
1882              "\"JumpId(\`nasmdoc.hlp',\`%s')\");\n",
1883              &hlp_sectkw($tstruct_up{$node});
1884       print "EnableButton(\"btn_up\")}\n";
1885       &hlp_keywords($node);
1886       print "\\keepn\\f2\\b\\fs30\\sb60\\sa60\n";
1887       print "$title\n";
1888       $newpar = "\\par\\pard\\plain\\sb120\n";
1889     } elsif ($ptype eq "code") {
1890       # Code paragraph.
1891       print "\\keep\\f1\\sb120\n";
1892       foreach $i (@$pname) {
1893         warn "code line longer than 68 chars: $i\n" if length $i > 68;
1894         $i =~ s/\\/\\\\/g;
1895         $i =~ s/\{/\\\{/g;
1896         $i =~ s/\}/\\\}/g;
1897         print "$i\\par\\sb0\n";
1898       }
1899       $newpar = "\\pard\\f0\\sb120\n";
1900     } elsif ($ptype eq "bull" || $ptype eq "norm") {
1901       # Ordinary paragraph, optionally bulleted. We wrap, FWIW.
1902       if ($ptype eq "bull") {
1903         print "\\tx360\\li360\\fi-360{\\f3\\'9F}\\tab\n";
1904         $newpar = "\\par\\pard\\sb120\n";
1905       } else {
1906         $newpar = "\\par\\sb120\n";
1907       }
1908       $line = '';
1909       @a = @$pname;
1910       $wd = $wprev = '';
1911       do {
1912         do { $w = &word_hlp((shift @a),1); } while $w eq "\001"; # hack
1913         $wd .= $wprev;
1914         if ($w eq ' ' || $w eq '' || $w eq undef) {
1915           if (length ($line . $wd) > 75) {
1916             $line =~ s/\s*$//; # trim trailing spaces
1917             print "$line \n"; # and put one back
1918             $line = '';
1919             $wd =~ s/^\s*//; # trim leading spaces
1920           }
1921           $line .= $wd;
1922           $wd = '';
1923         }
1924         $wprev = $w;
1925       } while ($w ne '' && $w ne undef);
1926       if ($line =~ /\S/) {
1927         $line =~ s/\s*$//; # trim trailing spaces
1928         print "$line\n";
1929       }
1930     }
1931   }
1932
1933   # Close file.
1934   print "\\page}\n";
1935   select STDOUT;
1936   close TEXT;
1937 }
1938
1939 sub word_hlp {
1940   my ($w, $docode) = @_;
1941   my $wtype, $wmajt;
1942
1943   return undef if $w eq '' || $w eq undef;
1944   $wtype = substr($w,0,2);
1945   $wmajt = substr($wtype,0,1);
1946   $w = substr($w,2);
1947   $w =~ s/\\/\\\\/g;
1948   $w =~ s/\{/\\\{/g;
1949   $w =~ s/\}/\\\}/g;
1950   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
1951   substr($w,0,length($w)-1) =~ s/-/\\'AD/g if $wmajt ne "x"; #nonbreakhyphens
1952   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
1953     return $w;
1954   } elsif ($wtype eq "sp") {
1955     return ' ';
1956   } elsif ($wtype eq "da") {
1957     return "\\'96";
1958   } elsif ($wmajt eq "c" || $wtype eq "wc") {
1959     $w =~ s/ /\\'A0/g; # make spaces non-breaking
1960     return $docode ? "{\\f1 ${w}}" : $w;
1961   } elsif ($wtype eq "es") {
1962     return "{\\i ${w}";
1963   } elsif ($wtype eq "ee") {
1964     return "${w}}";
1965   } elsif ($wtype eq "eo") {
1966     return "{\\i ${w}}";
1967   } elsif ($wtype eq "x ") {
1968     return "{\\uldb ";
1969   } elsif ($wtype eq "xe") {
1970     $w = &hlp_sectkw($w);
1971     return "}{\\v ${w}}";
1972   } elsif ($wmajt eq "i") {
1973     return "\001";
1974   } else {
1975     die "panic in word_hlp: $wtype$w\n";
1976   }
1977 }
1978
1979 sub hlp_menu {
1980   my ($topitem) = @_;
1981   my $item, $kword, $i, $mpname, $title;
1982
1983   $item = $tstruct_next{$topitem};
1984   print "\\li360\\fi-360\n";
1985   while ($item) {
1986     $title = "";
1987     $mpname = $tstruct_pname{$item};
1988     foreach $i (@$mpname) {
1989       $ww = &word_hlp($i, 0);
1990       $title .= $ww unless $ww eq "\001";
1991     }
1992     $kword = &hlp_sectkw($item);
1993     print "{\\uldb ${item}: $title}{\\v $kword}\\par\\sb0\n";
1994     $item = $tstruct_mnext{$item};
1995   }
1996   print "\\pard\\sb120\n";
1997 }
1998
1999 sub hlp_sectkw {
2000   my ($node) = @_;
2001   $node =~ tr/A-Z/a-z/;
2002   $node =~ tr/- ./___/;
2003   $node;
2004 }
2005
2006 sub hlp_keywords {
2007   my ($node) = @_;
2008   my $pfx = "K{\\footnote ";
2009   my $done = 0;
2010   foreach $i (0..$#itags) {
2011     (print $pfx,$hlp_index[$i]), $pfx = ";\n", $done++
2012         if $idxnodes{$node,$itags[$i]};
2013   }
2014   print "}\n" if $done;
2015 }
2016
2017 # Make tree structures. $tstruct_* is top-level and global.
2018 sub add_item {
2019   my ($item, $level) = @_;
2020   my $i;
2021
2022   $tstruct_pname{$item} = $pname;
2023   $tstruct_next{$tstruct_previtem} = $item;
2024   $tstruct_prev{$item} = $tstruct_previtem;
2025   $tstruct_level{$item} = $level;
2026   $tstruct_up{$item} = $tstruct_last[$level-1];
2027   $tstruct_mnext{$tstruct_last[$level]} = $item;
2028   $tstruct_last[$level] = $item;
2029   for ($i=$level+1; $i<$MAXLEVEL; $i++) { $tstruct_last[$i] = undef; }
2030   $tstruct_previtem = $item;
2031   push @nodes, $item;
2032 }
2033
2034 # PostScript font metric data. Used for line breaking.
2035 sub font_metrics {
2036   @timesr = (
2037      250,   0,   0,   0,   0,   0,   0,   0,
2038        0,   0,   0,   0,   0,   0,   0,   0,
2039        0,   0,   0,   0,   0,   0,   0,   0,
2040        0,   0,   0,   0,   0,   0,   0,   0,
2041      250, 333, 408, 500, 500, 833, 778, 333,
2042      333, 333, 500, 564, 250, 333, 250, 278,
2043      500, 500, 500, 500, 500, 500, 500, 500,
2044      500, 500, 278, 278, 564, 564, 564, 444,
2045      921, 722, 667, 667, 722, 611, 556, 722,
2046      722, 333, 389, 722, 611, 889, 722, 722,
2047      556, 722, 667, 556, 611, 722, 722, 944,
2048      722, 722, 611, 333, 278, 333, 469, 500,
2049      333, 444, 500, 444, 500, 444, 333, 500,
2050      500, 278, 278, 500, 278, 778, 500, 500,
2051      500, 500, 333, 389, 278, 500, 500, 722,
2052      500, 500, 444, 480, 200, 480, 541,   0,
2053        0,   0,   0,   0,   0,   0,   0,   0,
2054        0,   0,   0,   0,   0,   0,   0,   0,
2055        0,   0,   0,   0,   0,   0,   0,   0,
2056        0,   0,   0,   0,   0,   0,   0,   0,
2057        0, 333, 500, 500, 167, 500, 500, 500,
2058      500, 180, 444, 500, 333, 333, 556, 556,
2059        0, 500, 500, 500, 250,   0, 453, 350,
2060      333, 444, 444, 500,1000,1000,   0, 444,
2061        0, 333, 333, 333, 333, 333, 333, 333,
2062      333,   0, 333, 333,   0, 333, 333, 333,
2063     1000,   0,   0,   0,   0,   0,   0,   0,
2064        0,   0,   0,   0,   0,   0,   0,   0,
2065        0, 889,   0, 276,   0,   0,   0,   0,
2066      611, 722, 889, 310,   0,   0,   0,   0,
2067        0, 667,   0,   0,   0, 278,   0,   0,
2068      278, 500, 722, 500,   0,   0,   0,   0
2069   );
2070   @timesi = (
2071      250,   0,   0,   0,   0,   0,   0,   0,
2072        0,   0,   0,   0,   0,   0,   0,   0,
2073        0,   0,   0,   0,   0,   0,   0,   0,
2074        0,   0,   0,   0,   0,   0,   0,   0,
2075      250, 333, 420, 500, 500, 833, 778, 333,
2076      333, 333, 500, 675, 250, 333, 250, 278,
2077      500, 500, 500, 500, 500, 500, 500, 500,
2078      500, 500, 333, 333, 675, 675, 675, 500,
2079      920, 611, 611, 667, 722, 611, 611, 722,
2080      722, 333, 444, 667, 556, 833, 667, 722,
2081      611, 722, 611, 500, 556, 722, 611, 833,
2082      611, 556, 556, 389, 278, 389, 422, 500,
2083      333, 500, 500, 444, 500, 444, 278, 500,
2084      500, 278, 278, 444, 278, 722, 500, 500,
2085      500, 500, 389, 389, 278, 500, 444, 667,
2086      444, 444, 389, 400, 275, 400, 541,   0,
2087        0,   0,   0,   0,   0,   0,   0,   0,
2088        0,   0,   0,   0,   0,   0,   0,   0,
2089        0,   0,   0,   0,   0,   0,   0,   0,
2090        0,   0,   0,   0,   0,   0,   0,   0,
2091        0, 389, 500, 500, 167, 500, 500, 500,
2092      500, 214, 556, 500, 333, 333, 500, 500,
2093        0, 500, 500, 500, 250,   0, 523, 350,
2094      333, 556, 556, 500, 889,1000,   0, 500,
2095        0, 333, 333, 333, 333, 333, 333, 333,
2096      333,   0, 333, 333,   0, 333, 333, 333,
2097      889,   0,   0,   0,   0,   0,   0,   0,
2098        0,   0,   0,   0,   0,   0,   0,   0,
2099        0, 889,   0, 276,   0,   0,   0,   0,
2100      556, 722, 944, 310,   0,   0,   0,   0,
2101        0, 667,   0,   0,   0, 278,   0,   0,
2102      278, 500, 667, 500,   0,   0,   0,   0
2103   );
2104   @courr = (
2105      600,   0,   0,   0,   0,   0,   0,   0,
2106        0,   0,   0,   0,   0,   0,   0,   0,
2107        0,   0,   0,   0,   0,   0,   0,   0,
2108        0,   0,   0,   0,   0,   0,   0,   0,
2109      600, 600, 600, 600, 600, 600, 600, 600,
2110      600, 600, 600, 600, 600, 600, 600, 600,
2111      600, 600, 600, 600, 600, 600, 600, 600,
2112      600, 600, 600, 600, 600, 600, 600, 600,
2113      600, 600, 600, 600, 600, 600, 600, 600,
2114      600, 600, 600, 600, 600, 600, 600, 600,
2115      600, 600, 600, 600, 600, 600, 600, 600,
2116      600, 600, 600, 600, 600, 600, 600, 600,
2117      600, 600, 600, 600, 600, 600, 600, 600,
2118      600, 600, 600, 600, 600, 600, 600, 600,
2119      600, 600, 600, 600, 600, 600, 600, 600,
2120      600, 600, 600, 600, 600, 600, 600,   0,
2121        0,   0,   0,   0,   0,   0,   0,   0,
2122        0,   0,   0,   0,   0,   0,   0,   0,
2123        0,   0,   0,   0,   0,   0,   0,   0,
2124        0,   0,   0,   0,   0,   0,   0,   0,
2125        0, 600, 600, 600, 600, 600, 600, 600,
2126      600, 600, 600, 600, 600, 600, 600, 600,
2127        0, 600, 600, 600, 600,   0, 600, 600,
2128      600, 600, 600, 600, 600, 600,   0, 600,
2129        0, 600, 600, 600, 600, 600, 600, 600,
2130      600,   0, 600, 600,   0, 600, 600, 600,
2131      600,   0,   0,   0,   0,   0,   0,   0,
2132        0,   0,   0,   0,   0,   0,   0,   0,
2133        0, 600,   0, 600,   0,   0,   0,   0,
2134      600, 600, 600, 600,   0,   0,   0,   0,
2135        0, 600,   0,   0,   0, 600,   0,   0,
2136      600, 600, 600, 600,   0,   0,   0,   0
2137   );
2138 }