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