Fix the positioning of the page number in the table of contents
[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 ref_ps {
862     my($r) = @_;
863     $r =~ s/\./_/g;
864     return 'n'.$r;
865 }
866
867 sub ps_write_bookmarks {
868     my $para;
869     my %nchildren = ();
870     my %titles = ();
871     my @reflist = ();
872     my $ref, $pref, $i, $title;
873
874     for ($para = 0; $para <= $#pnames; $para++) {
875         my $pname = $pnames[$para];
876         my $pflags = $pflags[$para];
877         my $ptype = substr($pflags,0,4);
878         
879         if ($ptype eq "chap" || $ptype eq "appn") {
880             # Chapter/appendix heading. "Chapter N: Title" followed by a line of
881             # minus signs.
882             
883             $pflags =~ /(chap|appn) (.*) :(.*)/;
884             $ref = &ref_ps($2);
885             $title = '';
886             foreach $i (@$pname) {
887                 $title .= &word_ps_title($i);
888             }
889             $titles{$ref} = $title;
890             push @reflist, $ref;
891         } elsif ($ptype eq "head" || $ptype eq "subh") {
892             # Heading/subheading.  Just a number and some text.
893             $pflags =~ /.... (.*) :(.*)/;
894             $ref = &ref_ps($1);
895             $ref =~ /^(n[0-9A-Za-z_]+)\_[0-9A-Za-z]+$/;
896             $pref = $1;
897
898             $title = '';
899             foreach $i (@$pname) {
900                 $title .= &word_ps_title($i);
901             }
902             $titles{$ref} = $title;
903             push @reflist, $ref;
904             $nchildren{$pref}++;
905         }
906     }
907
908     # Now we should have enough data to generate the bookmarks
909     print "[/Title (Contents) /Dest /nContents /OUT pdfmark";
910     foreach $i ( @reflist ) {
911         print '[/Title (', $titles{$i}, ")\n";
912         print '/Count -', $nchildren{$i}, ' ' if ( $nchildren{$i} );
913         print "/Dest /$i /OUT pdfmark\n";
914     }
915     print "[/Title (Index) /Dest /nIndex /OUT pdfmark\n";
916 }
917
918 sub write_ps {
919   # This is called from the top level, so I won't bother using
920   # my or local.
921
922   # First, set up the font metric arrays.
923   &font_metrics;
924
925   # First stage: reprocess the source arrays into a list of
926   # lines, each of which is a list of word-strings, each of
927   # which has a single-letter font code followed by text.
928   # Each line also has an associated type, which will be
929   # used for final alignment and font selection and things.
930   #
931   # Font codes are:
932   #   n == Normal
933   #   e == Emphasised
934   #   c == Code
935   #  ' ' == space (no following text required)
936   #  '-' == dash (no following text required)
937   #
938   # Line types are:
939   #   chap == Chapter or appendix heading.
940   #   head == Major heading.
941   #   subh == Sub-heading.
942   #   Ccha == Contents entry for a chapter.
943   #   Chea == Contents entry for a heading.
944   #   Csub == Contents entry for a subheading.
945   #   cone == Code paragraph with just this one line on it.
946   #   cbeg == First line of multi-line code paragraph.
947   #   cbdy == Interior line of multi-line code paragraph.
948   #   cend == Final line of multi-line code paragraph.
949   #   none == Normal paragraph with just this one line on it.
950   #   nbeg == First line of multi-line normal paragraph.
951   #   nbdy == Interior line of multi-line normal paragraph.
952   #   nend == Final line of multi-line normal paragraph.
953   #   bone == Bulleted paragraph with just this one line on it.
954   #   bbeg == First line of multi-line bulleted paragraph.
955   #   bbdy == Interior line of multi-line bulleted paragraph.
956   #   bend == Final line of multi-line bulleted paragraph.
957   print "line-breaks...";
958   $lname = "psline000000";
959   $lnamei = "idx" . $lname;
960   @lnames = @ltypes = ();
961
962   $linewidth = 468;             # ADJUSTABLE: width of a normal text line
963   $bulletadj = 12;              # ADJUSTABLE: space for a bullet
964
965   for ($para = 0; $para <= $#pnames; $para++) {
966     $pname = $pnames[$para];
967     $pflags = $pflags[$para];
968     $ptype = substr($pflags,0,4);
969
970     # New paragraph _ergo_ new line.
971     @line = ();
972     @lindex = (); # list of index tags referenced to this line
973
974     if ($ptype eq "chap") {
975       # Chapter heading. "Chapter N: Title" followed by a line of
976       # minus signs.
977       $pflags =~ /chap (.*) :(.*)/;
978       push @line, "B".&ref_ps($1), "nChapter", " ", "n$1:", " ";
979       foreach $i (@$pname) {
980         $ww = &word_ps($i);
981         push @line, $ww unless $ww eq "x";
982       }
983       @$lname = @line; @$lnamei = @lindex;
984       push @lnames, $lname++;
985       $lnamei = "idx" . $lname;
986       push @ltypes, "chap";
987     } elsif ($ptype eq "appn") {
988       # Appendix heading. "Appendix N: Title" followed by a line of
989       # minus signs.
990       $pflags =~ /appn (.*) :(.*)/;
991       push @line, "B".&ref_ps($1), "nAppendix", " ", "n$1:", " ";
992       foreach $i (@$pname) {
993         $ww = &word_ps($i);
994         push @line, $ww unless $ww eq "x";
995       }
996       @$lname = @line; @$lnamei = @lindex;
997       push @lnames, $lname++;
998       $lnamei = "idx" . $lname;
999       push @ltypes, "chap";
1000     } elsif ($ptype eq "head") {
1001       # Heading. Just a number and some text.
1002       $pflags =~ /.... (.*) :(.*)/;
1003       push @line, "B".&ref_ps($1), "n$1";
1004       foreach $i (@$pname) {
1005         $ww = &word_ps($i);
1006         push @line, $ww unless $ww eq "x";
1007       }
1008       @$lname = @line; @$lnamei = @lindex;
1009       push @lnames, $lname++;
1010       $lnamei = "idx" . $lname;
1011       push @ltypes, $ptype;
1012     } elsif ($ptype eq "subh") {
1013       # Subheading. Just a number and some text.
1014       $pflags =~ /subh (.*) :(.*)/;
1015       push @line, "B".&ref_ps($1), "n$1";
1016       foreach $i (@$pname) {
1017         push @line, &word_ps($i);
1018       }
1019       @$lname = @line; @$lnamei = @lindex;
1020       push @lnames, $lname++;
1021       $lnamei = "idx" . $lname;
1022       push @ltypes, "subh";
1023     } elsif ($ptype eq "code") {
1024       # Code paragraph. Emit lines one at a time.
1025       $type = "cbeg";
1026       foreach $i (@$pname) {
1027         @$lname = ("c$i");
1028         push @lnames, $lname++;
1029         $lnamei = "idx" . $lname;
1030         push @ltypes, $type;
1031         $type = "cbdy";
1032       }
1033       $ltypes[$#ltypes] = ($ltypes[$#ltypes] eq "cbeg" ? "cone" : "cend");
1034     } elsif ($ptype eq "bull" || $ptype eq "norm") {
1035       # Ordinary paragraph, optionally bulleted. We wrap, with ragged
1036       # 75-char right margin and either 7 or 11 char left margin
1037       # depending on bullets.
1038       if ($ptype eq "bull") {
1039         $width = $linewidth - $bulletadj;
1040         $type = $begtype = "bbeg";
1041         $bodytype = "bbdy";
1042         $onetype = "bone";
1043         $endtype = "bend";
1044       } else {
1045         $width = $linewidth;
1046         $type = $begtype = "nbeg";
1047         $bodytype = "nbdy";
1048         $onetype = "none";
1049         $endtype = "nend";
1050       }
1051       @a = @$pname;
1052       @line = @wd = ();
1053       $linelen = 0;
1054       $wprev = undef;
1055       do {
1056         do { $w = &word_ps(shift @a) } while ($w eq "x");
1057         push @wd, $wprev if $wprev;
1058         if ($wprev =~ /^n.*-$/ || $w eq ' ' || $w eq '' || $w eq undef) {
1059           $wdlen = &len_ps(@wd);
1060           if ($linelen + $wdlen > $width) {
1061             pop @line while $line[$#line] eq ' '; # trim trailing spaces
1062             @$lname = @line; @$lnamei = @lindex;
1063             push @lnames, $lname++;
1064             $lnamei = "idx" . $lname;
1065             push @ltypes, $type;
1066             $type = $bodytype;
1067             @line = @lindex = ();
1068             $linelen = 0;
1069             shift @wd while $wd[0] eq ' '; # trim leading spaces
1070           }
1071           push @line, @wd;
1072           $linelen += $wdlen;
1073           @wd = ();
1074         }
1075         $wprev = $w;
1076       } while ($w ne '' && $w ne undef);
1077       if (@line) {
1078         pop @line while $line[$#line] eq ' '; # trim trailing spaces
1079         @$lname = @line; @$lnamei = @lindex;
1080         push @lnames, $lname++;
1081         $lnamei = "idx" . $lname;
1082         push @ltypes, $type;
1083         $type = $bodytype;
1084       }
1085       $ltypes[$#ltypes] =
1086         ($ltypes[$#ltypes] eq $begtype ? $onetype : $endtype);
1087     }
1088   }
1089
1090   # We've now processed the document source into lines. Before we
1091   # go on and do the page breaking, we'll fabricate a table of contents,
1092   # line by line, and then after doing page breaks we'll go back and
1093   # insert the page numbers into the contents entries.
1094   print "building contents...";
1095   @clnames = @cltypes = ();
1096   $clname = "pscont000000";
1097   @$clname = ("BnContents", "nContents"); # "chapter heading" for TOC
1098   push @clnames,$clname++;
1099   push @cltypes,"chap";
1100   for ($i=0; $i<=$#lnames; $i++) {
1101     $lname = $lnames[$i];
1102     if ($ltypes[$i] =~ /^(chap|head|subh)/) {
1103       @$clname = @$lname;
1104       splice @$clname,2,0," " if ($ltypes[$i] !~ /chap/);
1105       push @$clname,$i; # placeholder for page number
1106       push @clnames,$clname++;
1107       push @cltypes,"C" . substr($ltypes[$i],0,3);
1108     }
1109   }
1110   @$clname = ("BnIndex", "nIndex"); # contents entry for Index
1111   push @$clname,$i;      # placeholder for page number
1112   $idx_clname = $clname;
1113   push @clnames,$clname++;
1114   push @cltypes,"Ccha";
1115   $contlen = $#clnames + 1;
1116   unshift @lnames,@clnames;
1117   unshift @ltypes,@cltypes;
1118
1119   # Second stage: now we have a list of lines, break them into pages.
1120   # We do this by means of adding a third array in parallel with
1121   # @lnames and @ltypes, called @lpages, in which we store the page
1122   # number that each line resides on. We also add @ycoord which
1123   # stores the vertical position of each line on the page.
1124   #
1125   # Page breaks may not come after line-types:
1126   #   chap head subh cbeg nbeg bbeg
1127   # and may not come before line-types:
1128   #   cend nend bend
1129   # They are forced before line-types:
1130   #   chap
1131   print "page-breaks...";
1132   $pmax = 600; # ADJUSTABLE: maximum length of a page in points
1133   $textht = 11; # ADJUSTABLE: height of a normal line in points
1134   $spacing = 6; # ADJUSTABLE: space between paragraphs, in points
1135   $headht = 14; # ADJUSTABLE: height of a major heading in points
1136   $subht = 12; # ADJUSTABLE: height of a sub-heading in points
1137   $pstart = 0; # start line of current page
1138   $plen = 0; # current length of current page
1139   $pnum = 1; # number of current page
1140   $bpt = -1; # last feasible break point
1141   $i = 0; # line number
1142   while ($i <= $#lnames) {
1143     $lname = $lnames[$i];
1144     # Add the height of this line (computed the last time we went round
1145     # the loop, unless we're a chapter heading in which case we do it
1146     # now) to the length of the current page. Also, _put_ this line on
1147     # the current page, and allocate it a y-coordinate.
1148     if ($ltypes[$i] =~ /^chap$/) {
1149       $pnum += 1 - ($pnum & 1);  # advance to odd numbered page if necessary
1150       $plen = 100; # ADJUSTABLE: space taken up by a chapter heading
1151       $ycoord[$i] = 0; # chapter heading: y-coord doesn't matter
1152     } else {
1153       $ycoord[$i] = $plen + $space;
1154       $plen += $space + $ht;
1155     }
1156     # See if we can break after this line.
1157     $bpt = $i if $ltypes[$i] !~ /^chap|head|subh|cbeg|nbeg|bbeg$/ &&
1158                  $ltypes[$i+1] !~ /^cend|nend|bend$/;
1159     # Assume, to start with, that we don't break after this line.
1160     $break = 0;
1161     # See if a break is forced.
1162     $break = 1, $bpt = $i if $ltypes[$i+1] eq "chap" || !$ltypes[$i+1];
1163     # Otherwise, compute the height of the next line, and break if
1164     # it would make this page too long.
1165     $ht = $textht, $space = 0 if $ltypes[$i+1] =~ /^[nbc](bdy|end)$/;
1166     $ht = $textht, $space = $spacing if $ltypes[$i+1] =~ /^[nbc](one|beg)$/;
1167     $ht = $textht, $space = $spacing if $ltypes[$i+1] =~ /^C/;
1168     $ht = $subht, $space = $spacing if $ltypes[$i+1] eq "subh";
1169     $ht = $headht, $space = $spacing if $ltypes[$i+1] eq "head";
1170     $break = 1 if $plen + $space + $ht > $pmax;
1171     # Now, if we're breaking, assign page number $pnum to all lines up
1172     # to $bpt, set $i == $bpt+1, and zero $space since we are at the
1173     # start of a new page and don't want leading space.
1174     if ($break) {
1175       die "no feasible break point at all on page $pnum\n" if $bpt == -1;
1176       for ($j = $pstart; $j <= $bpt; $j++) {
1177         $lnamei = "idx" . $lnames[$j];
1178         foreach $k (@$lnamei) {
1179           ${$psidxpp{$k}}{$pnum} = 1;
1180         }
1181         $lpages[$j] = $pnum;
1182       }
1183       $pnum++;
1184       $i = $bpt;
1185       $bpt = -1;
1186       $pstart = $i+1;
1187       $plen = 0;
1188       $space = 0;
1189     }
1190     $i++;
1191   }
1192
1193   # Now fix up the TOC with page numbers.
1194   print "\n   fixing up contents...";
1195   for ($i=0; $i<=$#lnames; $i++) {
1196     $lname = $lnames[$i];
1197     if ($ltypes[$i] =~ /^C/) {
1198       $j = pop @$lname;
1199       push @$lname, "n" . $lpages[$j+$contlen];
1200     }
1201   }
1202
1203   # Having got page numbers for most stuff, generate an index.
1204   print "building index...";
1205   $iwid = 222;
1206   $sep = 12;
1207   $commaindent = 32;
1208   foreach $k (@itags) {
1209     @line = ();
1210     $cmd = "index";
1211     @idxentry = @{$idxmap{$k}};
1212     if ($commaafter{$k} and !$commanext{$k}) {
1213       # This line is a null line beginning a multiple entry. We must
1214       # output the prefix on a line by itself.
1215
1216       @idxhead = splice @idxentry,0,$commapos{$k};
1217       @line = ();
1218       foreach $i (@idxhead) {
1219         $ww = &word_ps($i);
1220         push @line, $ww unless $ww eq "x";
1221       }
1222       &ps_idxout("index",\@line,[]);
1223       $cmd = "iindex";
1224       @line = ();
1225     }
1226     $cmd = "iindex", splice @idxentry,0,$commapos{$k} if $commanext{$k};
1227     foreach $i (@idxentry) {
1228       $ww = &word_ps($i);
1229       push @line, $ww unless $ww eq "x";
1230     }
1231     $len = $iwid - $sep - &len_ps(@line);
1232     warn "text for index tag `%s' is longer than one index line!\n"
1233       if $len < -$sep;
1234     @pp = ();
1235     $inums = join(',',sort { $a <=> $b } keys %{$psidxpp{$k}});
1236     while (length $inums) {
1237       $inums =~ /^([^,]+)(,?)(.*)$/;
1238       $inums = $3, $inumc = $2; $inum = $1;
1239       @pnum = (" ", "Bp$inum", "n$inum", "E");
1240       push(@pnum, "n$inumc") if ( $inumc ne '' );
1241       $pnumlen = &len_ps(@pnum);
1242       if ($pnumlen > $len) {
1243         &ps_idxout($cmd,\@line,\@pp);
1244         @pp = ();
1245         @line = ();
1246         $cmd = "index";
1247         $len = $iwid - $sep;
1248       }
1249       push @pp, @pnum;
1250       $len -= $pnumlen;
1251     }
1252     &ps_idxout($cmd,\@line,\@pp) if (length @pp);
1253     $l1 = &len_ps(@line);
1254     $l2 = &len_ps($pp);
1255   }
1256   $$idx_clname[$#$idx_clname] = "n" . $pnum; # fix up TOC entry for index
1257
1258   print "writing file...";
1259   open PS,">nasmdoc.ps";
1260   select PS;
1261   $page = $lpages[0];
1262   &ps_header;
1263   &ps_write_bookmarks;
1264   for ($i=0; $i<=$#lnames; $i++) {
1265     &ps_throw_pg($page,$lpages[$i]) if $page != $lpages[$i];
1266     $page = $lpages[$i];
1267     &ps_out_line($ycoord[$i],$ltypes[$i],$lnames[$i]);
1268   }
1269   $i = 0;
1270   while ($i <= $#psindex) {
1271     &ps_throw_pg($page, $pnum) if $page != $pnum;
1272     $page = $pnum++;
1273     $ypos = 0;
1274     $ypos = 100, &ps_out_line(0, "chap", ["BnIndex", "nIndex"]) if !$i;
1275     $lines = ($pmax - $ypos) / $textht;
1276     my $col; # ps_out_line hits this variable
1277     PAGE:for ($col = 1; $col <= 2; $col++) {
1278       $y = $ypos; $l = $lines;
1279       COL: while ($l > 0) {
1280         $j = $i+1;
1281         $j++ while $psindex[$j] and ($psindex[$j][3] == 0); # find next break
1282         last COL if $j-$i > $l or $i > $#psindex;
1283         while ($i < $j) {
1284           &ps_out_line($y, $psindex[$i][0] eq "index" ? "idl$col" : "ldl$col",
1285                        $psindex[$i][1]);
1286           &ps_out_line($y,"idr$col",$psindex[$i][2]);
1287           $i++;
1288           $y += $textht;
1289           $l--;
1290         }
1291       }
1292       last PAGE if $i > $#psindex;
1293     }
1294   }
1295   &ps_trailer($page);
1296   close PS;
1297   select STDOUT;
1298 }
1299
1300 sub ps_idxout {
1301   my ($cmd, $left, $right) = @_;
1302   my $break = 1;
1303   $break = 0
1304       if ($#psindex >= 0) and ( ($#$left < 0) or ($cmd eq "iindex") );
1305   push @psindex,[$cmd,[@$left],[@$right],$break];
1306 }
1307
1308 sub ps_header {
1309     $pshdr = <<'EOF';
1310 /sp (n ) def
1311 /nf /Times-Roman findfont 11 scalefont def
1312 /ef /Times-Italic findfont 11 scalefont def
1313 /cf /Courier findfont 11 scalefont def
1314 /nc /Helvetica-Bold findfont 18 scalefont def
1315 /ec /Helvetica-Oblique findfont 18 scalefont def
1316 /cc /Courier-Bold findfont 18 scalefont def
1317 /nh /Helvetica-Bold findfont 14 scalefont def
1318 /eh /Helvetica-Oblique findfont 14 scalefont def
1319 /ch /Courier-Bold findfont 14 scalefont def
1320 /ns /Helvetica-Bold findfont 12 scalefont def
1321 /es /Helvetica-Oblique findfont 12 scalefont def
1322 /cs /Courier-Bold findfont 12 scalefont def
1323 /n 16#6E def /e 16#65 def /c 16#63 def
1324 /B 16#42 def /E 16#45 def /D 16#44 def
1325 /min { 2 copy gt { exch } if pop } def
1326 /max { 2 copy lt { exch } if pop } def
1327 /lkbegun 0 def
1328 /lkury 0 def
1329 /lkurx 0 def
1330 /lklly 0 def
1331 /lkllx 0 def
1332 /lktarget () def
1333 /linkbegin {
1334   /lkbegun 1 def
1335   /lktarget exch cvn def
1336 } def
1337 /linkshow {
1338   lkbegun 0 ne {
1339     gsave dup true charpath pathbbox grestore
1340     lkbegun 1 eq {
1341       /lkury exch def
1342       /lkurx exch def
1343       /lklly exch def
1344       /lkllx exch def
1345       /lkbegun 2 def
1346     } {
1347       lkury max /lkury exch def
1348       lkurx max /lkurx exch def
1349       lklly min /lklly exch def
1350       lkllx min /lkllx exch def
1351     } ifelse
1352   } if
1353   show
1354 } def
1355 /linkend {
1356   [/Rect [ lkllx lklly lkurx lkury ]
1357     /Color [ 1.0 0.0 0.0 ]
1358     /Border [0 0 0]
1359     /Dest lktarget
1360     /Subtype /Link
1361     /ANN pdfmark
1362   /lkbegun 0 def
1363 } def
1364 /linkdest {
1365   /lkdest exch cvn def
1366   [ /Dest lkdest
1367     /View [ /XYZ currentpoint 0 ]
1368     /DEST pdfmark
1369 } def
1370 /handlelink {
1371   dup 0 get
1372   dup B eq {
1373     pop dup length 1 sub 1 exch getinterval linkbegin
1374   } {
1375     E eq {
1376       pop linkend
1377     } {
1378       dup length 1 sub 1 exch getinterval linkdest
1379     } ifelse
1380   } ifelse
1381 } def
1382 /pageodd {
1383    550 50 moveto ns setfont dup stringwidth pop neg 0 rmoveto show
1384 } def
1385 /pageeven { 50 50 moveto ns setfont show } def
1386 /destmark {
1387   dup length 1 sub 1 exch getinterval linkdest
1388 } def
1389 /chapter {
1390   100 620 moveto
1391   dup 0 get destmark
1392   dup length 1 sub 1 exch getinterval
1393   {
1394     dup 0 get
1395     dup n eq {pop nc setfont} {
1396       e eq {ec setfont} {cc setfont} ifelse
1397     } ifelse
1398     dup length 1 sub 1 exch getinterval show
1399   } forall
1400   0 setlinecap 3 setlinewidth
1401   newpath 100 610 moveto 468 0 rlineto stroke
1402 } def
1403 /heading {
1404   686 exch sub /y exch def /a exch def
1405   90 y moveto
1406   a 0 get destmark
1407   a 1 get dup length 1 sub 1 exch getinterval
1408   nh setfont dup stringwidth pop neg 0 rmoveto show
1409   100 y moveto
1410   a dup length 2 sub 2 exch getinterval {
1411     /s exch def
1412     s 0 get
1413     dup n eq {pop nh setfont} {
1414       e eq {eh setfont} {ch setfont} ifelse
1415     } ifelse
1416     s s length 1 sub 1 exch getinterval show
1417   } forall
1418 } def
1419 /subhead {
1420   688 exch sub /y exch def /a exch def
1421   90 y moveto
1422   a 0 get destmark
1423   a 1 get dup length 1 sub 1 exch getinterval
1424   ns setfont dup stringwidth pop neg 0 rmoveto show
1425   100 y moveto
1426   a dup length 2 sub 2 exch getinterval {
1427     /s exch def
1428     s 0 get
1429     dup n eq {pop ns setfont} {
1430       e eq {es setfont} {cs setfont} ifelse
1431     } ifelse
1432     s s length 1 sub 1 exch getinterval show
1433   } forall
1434 } def
1435 /disp { /j exch def
1436   568 exch sub exch 689 exch sub moveto
1437   {
1438     /s exch def
1439     s 0 get
1440     dup E le {
1441       pop s handlelink
1442     } {
1443       dup n eq {pop nf setfont} {
1444         e eq {ef setfont} {cf setfont} ifelse
1445       } ifelse
1446       s s length 1 sub 1 exch getinterval linkshow
1447       s sp eq {j 0 rmoveto} if
1448     } ifelse
1449   } forall
1450 } def
1451 /contents { /w exch def /y exch def /a exch def
1452   /yy 689 y sub def
1453   a a length 1 sub get dup length 1 sub 1 exch getinterval
1454   /ss exch def
1455   nf setfont 568 ss stringwidth pop sub /ex exch def
1456   a 0 a length 1 sub getinterval y w 0 disp
1457   /sx currentpoint pop def nf setfont
1458   100 10 568 { /i exch def
1459     i 5 sub sx gt i 5 add ex lt and {
1460       i yy moveto (.) linkshow
1461     } if
1462   } for
1463   ex yy moveto ss linkshow
1464   linkend
1465 } def
1466 /just { /w exch def /y exch def /a exch def
1467   /jj w def /spaces 0 def
1468   a {
1469     /s exch def
1470     s 0 get
1471     dup n eq {pop nf setfont} {
1472       e eq {ef setfont} {cf setfont} ifelse
1473     } ifelse
1474     s s length 1 sub 1 exch getinterval stringwidth pop
1475     jj exch sub /jj exch def
1476     s sp eq {/spaces spaces 1 add def} if
1477   } forall
1478   a y w jj spaces spaces 0 eq {pop pop 0} {div} ifelse disp
1479 } def
1480 /idl { 468 exch sub 0 disp } def
1481 /ldl { 436 exch sub 0 disp } def
1482 /idr { 222 add 468 exch sub /x exch def /y exch def /a exch def
1483   a {
1484     /s exch def
1485     s 0 get
1486     dup E le {
1487       pop
1488     } {
1489       dup n eq {pop nf setfont} {
1490         e eq {ef setfont} {cf setfont} ifelse
1491       } ifelse
1492       s s length 1 sub 1 exch getinterval stringwidth pop
1493       x add /x exch def
1494     } ifelse
1495   } forall
1496   a y x 0 disp
1497 } def
1498 /left {0 disp} def
1499 /bullet {
1500   nf setfont dup 100 exch 689 exch sub moveto (\267) show
1501 } def
1502 [/PageMode /UseOutlines /DOCVIEW pdfmark
1503 EOF
1504   print "%!PS-Adobe-3.0\n";
1505   print "%%BoundingBox: 95 95 590 705\n";
1506   print "%%Creator: a nasty Perl script\n";
1507   print "%%DocumentData: Clean7Bit\n";
1508   print "%%Orientation: Portrait\n";
1509   print "%%Pages: $lpages[$#lpages]\n";
1510   print "%%DocumentNeededResources: font Times-Roman Times-Italic\n";
1511   print "%%+ font Helvetica-Bold Courier Courier-Bold\n";
1512   print "%%EndComments\n";
1513   print "%%BeginProlog\n";
1514   # This makes sure non-PDF PostScript interpreters don't choke on
1515   # pdfmarks in the output
1516   print "/pdfmark where\n";
1517   print "{pop} {userdict /pdfmark /cleartomark load put} ifelse\n";
1518   print "%%EndProlog\n";
1519   print "%%BeginSetup\n";
1520   print "save\n";
1521   $pshdr =~ s/\s+/ /g;
1522   while ($pshdr =~ /\S/) {
1523     last if length($pshdr) < 72 || $pshdr !~ /^(.{0,72}\S)\s(.*)$/;
1524     $pshdr = $2;
1525     print "$1\n";
1526   }
1527   print "$pshdr\n" if $pshdr =~ /\S/;
1528   print "%%EndSetup\n";
1529   &ps_initpg($lpages[0]);
1530 }
1531
1532 sub ps_trailer {
1533   my ($oldpg) = @_;
1534   &ps_donepg($oldpg);
1535   print "%%Trailer\nrestore\n%%EOF\n";
1536 }
1537
1538 sub ps_throw_pg {
1539   my ($oldpg, $newpg) = @_;
1540   while ($oldpg < $newpg) {
1541     &ps_donepg($oldpg);
1542     $oldpg++;
1543     &ps_initpg($oldpg);
1544   }
1545 }
1546
1547 sub ps_initpg {
1548   my ($pgnum) = @_;
1549   print "%%Page: $pgnum $pgnum\n";
1550   print "%%BeginPageSetup\nsave\n%%EndPageSetup\n";
1551   print "95 705 moveto (p$pgnum) linkdest\n";
1552 }
1553
1554 sub ps_donepg {
1555   my ($pgnum) = @_;
1556   if ($pgnum & 1) {
1557     print "%%PageTrailer\n($pgnum)pageodd restore showpage\n";
1558   } else {
1559     print "%%PageTrailer\n($pgnum)pageeven restore showpage\n";
1560   }
1561 }
1562
1563 sub ps_out_line {
1564   my ($ypos,$ltype,$lname) = @_;
1565   my $c,$d,$wid;
1566
1567   print "[";
1568   $col = 1;
1569   foreach $c (@$lname) {#
1570     $c= "n " if $c eq " ";
1571     $c = "n\261" if $c eq "-";
1572     $d = '';
1573     while (length $c) {
1574       $d .= $1, $c = $2 while $c =~ /^([ -\'\*-\[\]-~]+)(.*)$/;
1575       while (1) {
1576         $d .= "\\$1", $c = $2, next if $c =~ /^([\\\(\)])(.*)$/;
1577         ($d .= sprintf "\\%3o",unpack("C",$1)), $c = $2, next
1578           if $c =~ /^([^ -~])(.*)$/;
1579         last;
1580       }
1581     }
1582     $d = "($d)";
1583     $col = 0, print "\n" if $col>0 && $col+length $d > 77;
1584     print $d;
1585     $col += length $d;
1586   }
1587   print "\n" if $col > 60;
1588   print "]";
1589   if ($ltype =~ /^[nb](beg|bdy)$/) {
1590     printf "%d %s%d just\n",
1591       $ypos, ($ltype eq "bbeg" ? "bullet " : ""),
1592       ($ltype =~ /^b/ ? 456 : 468);
1593   } elsif ($ltype =~ /^[nb](one|end)$/) {
1594     printf "%d %s%d left\n",
1595       $ypos, ($ltype eq "bone" ? "bullet " : ""),
1596       ($ltype =~ /^b/ ? 456 : 468);
1597   } elsif ($ltype =~ /^c(one|beg|bdy|end)$/) {
1598     printf "$ypos 468 left\n";
1599   } elsif ($ltype =~ /^C/) {
1600     $wid = 468;
1601     $wid = 456 if $ltype eq "Chea";
1602     $wid = 444 if $ltype eq "Csub";
1603     printf "$ypos $wid contents\n";
1604   } elsif ($ltype eq "chap") {
1605     printf "chapter\n";
1606   } elsif ($ltype eq "head") {
1607     printf "$ypos heading\n";
1608   } elsif ($ltype eq "subh") {
1609     printf "$ypos subhead\n";
1610   } elsif ($ltype =~ /([il]d[lr])([12])/) {
1611     $left = ($2 eq "2" ? 468-222 : 0);
1612     printf "$ypos $left $1\n";
1613   }
1614 }
1615
1616 sub word_ps {
1617   my ($w) = @_;
1618   my $wtype, $wmajt;
1619
1620   return undef if $w eq '' || $w eq undef;
1621
1622   $wtype = substr($w,0,2);
1623   $wmajt = substr($wtype,0,1);
1624   $w = substr($w,2);
1625   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
1626   if ($wmajt eq "n" || $wtype eq "w ") {
1627     return "n$w";
1628   } elsif ($wtype eq "sp") {
1629     return ' ';
1630   } elsif ($wtype eq "da") {
1631     return '-';
1632   } elsif ($wmajt eq "c" || $wtype eq "wc") {
1633     return "c$w";
1634   } elsif ($wmajt eq "e") {
1635     return "e$w";
1636   } elsif ($wmajt eq "x") {
1637     return "x";
1638   } elsif ($wtype eq "i ") {
1639     push @lindex, $w;
1640     return "x";
1641   } else {
1642     die "panic in word_ps: $wtype$w\n";
1643   }
1644 }
1645
1646 sub word_ps_title {
1647   my ($w) = @_;
1648   my $wtype, $wmajt;
1649
1650   return undef if $w eq '' || $w eq undef;
1651
1652   $wtype = substr($w,0,2);
1653   $wmajt = substr($wtype,0,1);
1654   $w = substr($w,2);
1655   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
1656   if ($wmajt eq "n" || $wtype eq "w ") {
1657     return $w;
1658   } elsif ($wtype eq "sp") {
1659     return ' ';
1660   } elsif ($wtype eq "da") {
1661     return '-';
1662   } elsif ($wmajt eq "c" || $wtype eq "wc") {
1663     return $w;
1664   } elsif ($wmajt eq "e") {
1665     return $w;
1666   } elsif ($wmajt eq "x") {
1667     return '';
1668   } elsif ($wtype eq "i ") {
1669     return '';
1670   } else {
1671     die "panic in word_ps_title: $wtype$w\n";
1672   }
1673 }
1674
1675 sub len_ps {
1676   my (@line) = @_;
1677   my $l = 0;
1678   my $w, $size;
1679
1680   $size = 11/1000; # used only for length calculations
1681   while ($w = shift @line) {
1682     $w = "n " if $w eq " ";
1683     $w = "n\261" if $w eq "-";
1684     $f = substr($w,0,1);
1685     if ( $f !~ /^[BDE]$/ ) {
1686         $f = "timesr" if $f eq "n";
1687         $f = "timesi" if $f eq "e";
1688         $f = "courr" if $f eq "c";
1689         foreach $c (unpack 'C*',substr($w,1)) {
1690             $l += $size * $$f[$c];
1691         }
1692     }
1693   }
1694   return $l;
1695 }
1696
1697 sub write_texi {
1698   # This is called from the top level, so I won't bother using
1699   # my or local.
1700
1701   # Open file.
1702   print "writing file...";
1703   open TEXT,">nasmdoc.texi";
1704   select TEXT;
1705
1706   # Preamble.
1707   print "\\input texinfo   \@c -*-texinfo-*-\n";
1708   print "\@c \%**start of header\n";
1709   print "\@setfilename nasm.info\n";
1710   print "\@dircategory Programming\n";
1711   print "\@direntry\n";
1712   print "* NASM: (nasm).                The Netwide Assembler for x86.\n";
1713   print "\@end direntry\n";
1714   print "\@settitle NASM: The Netwide Assembler\n";
1715   print "\@setchapternewpage odd\n";
1716   print "\@c \%**end of header\n";
1717   print "\n";
1718   print "\@ifinfo\n";
1719   print "This file documents NASM, the Netwide Assembler: an assembler\n";
1720   print "targetting the Intel x86 series of processors, with portable source.\n";
1721   print "\n";
1722   print "Copyright 1997 Simon Tatham\n";
1723   print "\n";
1724   print "All rights reserved. This document is redistributable under the\n";
1725   print "licence given in the file \"Licence\" distributed in the NASM archive.\n";
1726   print "\@end ifinfo\n";
1727   print "\n";
1728   print "\@titlepage\n";
1729   print "\@title NASM: The Netwide Assembler\n";
1730   print "\@author Simon Tatham\n";
1731   print "\n";
1732   print "\@page\n";
1733   print "\@vskip 0pt plus 1filll\n";
1734   print "Copyright \@copyright{} 1997 Simon Tatham\n";
1735   print "\n";
1736   print "All rights reserved. This document is redistributable under the\n";
1737   print "licence given in the file \"Licence\" distributed in the NASM archive.\n";
1738   print "\@end titlepage\n";
1739   print "\n";
1740   print "\@node Top, $tstruct_next{'Top'}, (dir), (dir)\n";
1741   print "\@top Netwide Assembler\n";
1742   print "\n";
1743   print "\@ifinfo\n";
1744   print "This file documents NASM, the Netwide Assembler: an assembler\n";
1745   print "targetting the Intel x86 series of processors, with portable source.\n";
1746   print "\@end ifinfo\n";
1747
1748   $node = "Top";
1749
1750   $bulleting = 0;
1751   for ($para = 0; $para <= $#pnames; $para++) {
1752     $pname = $pnames[$para];
1753     $pflags = $pflags[$para];
1754     $ptype = substr($pflags,0,4);
1755
1756     $bulleting = 0, print "\@end itemize\n" if $bulleting && $ptype ne "bull";
1757     print "\n"; # always one of these before a new paragraph
1758
1759     if ($ptype eq "chap") {
1760       # Chapter heading. Begin a new node.
1761       &texi_menu($node)
1762         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1763       $pflags =~ /chap (.*) :(.*)/;
1764       $node = "Chapter $1";
1765       $title = "Chapter $1: ";
1766       foreach $i (@$pname) {
1767         $ww = &word_texi($i);
1768         $title .= $ww unless $ww eq "\001";
1769       }
1770       print "\@node $node, $tstruct_next{$node}, $tstruct_prev{$node},";
1771       print " $tstruct_up{$node}\n\@unnumbered $title\n";
1772     } elsif ($ptype eq "appn") {
1773       # Appendix heading. Begin a new node.
1774       &texi_menu($node)
1775         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1776       $pflags =~ /appn (.*) :(.*)/;
1777       $node = "Appendix $1";
1778       $title = "Appendix $1: ";
1779       foreach $i (@$pname) {
1780         $ww = &word_texi($i);
1781         $title .= $ww unless $ww eq "\001";
1782       }
1783       print "\@node $node, $tstruct_next{$node}, $tstruct_prev{$node},";
1784       print " $tstruct_up{$node}\n\@unnumbered $title\n";
1785     } elsif ($ptype eq "head" || $ptype eq "subh") {
1786       # Heading or subheading. Begin a new node.
1787       &texi_menu($node)
1788         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
1789       $pflags =~ /.... (.*) :(.*)/;
1790       $node = "Section $1";
1791       $title = "$1. ";
1792       foreach $i (@$pname) {
1793         $ww = &word_texi($i);
1794         $title .= $ww unless $ww eq "\001";
1795       }
1796       print "\@node $node, $tstruct_next{$node}, $tstruct_prev{$node},";
1797       print " $tstruct_up{$node}\n";
1798       $hdr = ($ptype eq "subh" ? "\@unnumberedsubsec" : "\@unnumberedsec");
1799       print "$hdr $title\n";
1800     } elsif ($ptype eq "code") {
1801       # Code paragraph. Surround with @example / @end example.
1802       print "\@example\n";
1803       foreach $i (@$pname) {
1804         warn "code line longer than 68 chars: $i\n" if length $i > 68;
1805         $i =~ s/\@/\@\@/g;
1806         $i =~ s/\{/\@\{/g;
1807         $i =~ s/\}/\@\}/g;
1808         print "$i\n";
1809       }
1810       print "\@end example\n";
1811     } elsif ($ptype eq "bull" || $ptype eq "norm") {
1812       # Ordinary paragraph, optionally bulleted. We wrap, FWIW.
1813       if ($ptype eq "bull") {
1814         $bulleting = 1, print "\@itemize \@bullet\n" if !$bulleting;
1815         print "\@item\n";
1816       }
1817       $line = '';
1818       @a = @$pname;
1819       $wd = $wprev = '';
1820       do {
1821         do { $w = &word_texi(shift @a); } while $w eq "\001"; # hack
1822         $wd .= $wprev;
1823         if ($wprev =~ /-$/ || $w eq ' ' || $w eq '' || $w eq undef) {
1824           if (length ($line . $wd) > 75) {
1825             $line =~ s/\s*$//; # trim trailing spaces
1826             print "$line\n";
1827             $line = '';
1828             $wd =~ s/^\s*//; # trim leading spaces
1829           }
1830           $line .= $wd;
1831           $wd = '';
1832         }
1833         $wprev = $w;
1834       } while ($w ne '' && $w ne undef);
1835       if ($line =~ /\S/) {
1836         $line =~ s/\s*$//; # trim trailing spaces
1837         print "$line\n";
1838       }
1839     }
1840   }
1841
1842   # Write index.
1843   &texi_index;
1844
1845   # Close file.
1846   print "\n\@contents\n\@bye\n";
1847   select STDOUT;
1848   close TEXT;
1849 }
1850
1851 # Side effect of this procedure: update global `texiwdlen' to be the length
1852 # in chars of the formatted version of the word.
1853 sub word_texi {
1854   my ($w) = @_;
1855   my $wtype, $wmajt;
1856
1857   return undef if $w eq '' || $w eq undef;
1858   $wtype = substr($w,0,2);
1859   $wmajt = substr($wtype,0,1);
1860   $w = substr($w,2);
1861   $wlen = length $w;
1862   $w =~ s/\@/\@\@/g;
1863   $w =~ s/\{/\@\{/g;
1864   $w =~ s/\}/\@\}/g;
1865   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
1866   substr($w,0,1) =~ tr/a-z/A-Z/, $capital = 0 if $capital;
1867   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
1868     $texiwdlen = $wlen;
1869     return $w;
1870   } elsif ($wtype eq "sp") {
1871     $texiwdlen = 1;
1872     return ' ';
1873   } elsif ($wtype eq "da") {
1874     $texiwdlen = 2;
1875     return '--';
1876   } elsif ($wmajt eq "c" || $wtype eq "wc") {
1877     $texiwdlen = 2 + $wlen;
1878     return "\@code\{$w\}";
1879   } elsif ($wtype eq "es") {
1880     $texiwdlen = 1 + $wlen;
1881     return "\@emph\{${w}";
1882   } elsif ($wtype eq "ee") {
1883     $texiwdlen = 1 + $wlen;
1884     return "${w}\}";
1885   } elsif ($wtype eq "eo") {
1886     $texiwdlen = 2 + $wlen;
1887     return "\@emph\{${w}\}";
1888   } elsif ($wtype eq "x ") {
1889     $texiwdlen = 0; # we don't need it in this case
1890     $capital = 1; # hack
1891     return "\@ref\{";
1892   } elsif ($wtype eq "xe") {
1893     $texiwdlen = 0; # we don't need it in this case
1894     return "\}";
1895   } elsif ($wmajt eq "i") {
1896     $texiwdlen = 0; # we don't need it in this case
1897     return "\001";
1898   } else {
1899     die "panic in word_texi: $wtype$w\n";
1900   }
1901 }
1902
1903 sub texi_menu {
1904   my ($topitem) = @_;
1905   my $item, $i, $mpname, $title, $wd;
1906
1907   $item = $tstruct_next{$topitem};
1908   print "\@menu\n";
1909   while ($item) {
1910     $title = "";
1911     $mpname = $tstruct_pname{$item};
1912     foreach $i (@$mpname) {
1913       $wd = &word_texi($i);
1914       $title .= $wd unless $wd eq "\001";
1915     }
1916     print "* ${item}:: $title\n";
1917     $item = $tstruct_mnext{$item};
1918   }
1919   print "* Index::\n" if $topitem eq "Top";
1920   print "\@end menu\n";
1921 }
1922
1923 sub texi_index {
1924   my $itag, $ientry, @a, $wd, $item, $len;
1925   my $subnums = "123456789ABCDEFGHIJKLMNOPQRSTU" .
1926                 "VWXYZabcdefghijklmnopqrstuvwxyz";
1927
1928   print "\@ifinfo\n\@node Index, , $FIXMElastnode, Top\n";
1929   print "\@unnumbered Index\n\n\@menu\n";
1930
1931   foreach $itag (@itags) {
1932     $ientry = $idxmap{$itag};
1933     @a = @$ientry;
1934     $item = '';
1935     $len = 0;
1936     foreach $i (@a) {
1937       $wd = &word_texi($i);
1938       $item .= $wd, $len += $texiwdlen unless $wd eq "\001";
1939     }
1940     $i = 0;
1941     foreach $node (@nodes) {
1942       next if !$idxnodes{$node,$itag};
1943       printf "* %s%s (%s): %s.\n",
1944           $item, " " x (40-$len), substr($subnums,$i++,1), $node;
1945     }
1946   }
1947   print "\@end menu\n\@end ifinfo\n";
1948 }
1949
1950 sub write_hlp {
1951   # This is called from the top level, so I won't bother using
1952   # my or local.
1953
1954   # Build the index-tag text forms.
1955   print "building index entries...";
1956   @hlp_index = map {
1957                  my $i,$ww;
1958                  my $ientry = $idxmap{$_};
1959                  my $title = "";
1960                  foreach $i (@$ientry) {
1961                    $ww = &word_hlp($i,0);
1962                    $title .= $ww unless $ww eq "\001";
1963                  }
1964                  $title;
1965                } @itags;
1966
1967   # Write the HPJ project-description file.
1968   print "writing .hpj file...";
1969   open HPJ,">nasmdoc.hpj";
1970   print HPJ "[OPTIONS]\ncompress=true\n";
1971   print HPJ "title=NASM: The Netwide Assembler\noldkeyphrase=no\n\n";
1972   print HPJ "[FILES]\nnasmdoc.rtf\n\n";
1973   print HPJ "[CONFIG]\n";
1974   print HPJ 'CreateButton("btn_up", "&Up",'.
1975             ' "JumpContents(`nasmdoc.hlp'."'".')")';
1976   print HPJ "\nBrowseButtons()\n";
1977   close HPJ;
1978
1979   # Open file.
1980   print "\n   writing .rtf file...";
1981   open TEXT,">nasmdoc.rtf";
1982   select TEXT;
1983
1984   # Preamble.
1985   print "{\\rtf1\\ansi{\\fonttbl\n";
1986   print "\\f0\\froman Times New Roman;\\f1\\fmodern Courier New;\n";
1987   print "\\f2\\fswiss Arial;\\f3\\ftech Wingdings}\\deff0\n";
1988   print "#{\\footnote Top}\n";
1989   print "\${\\footnote Contents}\n";
1990   print "+{\\footnote browse:00000}\n";
1991   print "!{\\footnote DisableButton(\"btn_up\")}\n";
1992   print "\\keepn\\f2\\b\\fs30\\sb0\n";
1993   print "NASM: The Netwide Assembler\n";
1994   print "\\par\\pard\\plain\\sb120\n";
1995   print "This file documents NASM, the Netwide Assembler: an assembler \n";
1996   print "targetting the Intel x86 series of processors, with portable source.\n";
1997
1998   $node = "Top";
1999   $browse = 0;
2000
2001   $newpar = "\\par\\sb120\n";
2002   for ($para = 0; $para <= $#pnames; $para++) {
2003     $pname = $pnames[$para];
2004     $pflags = $pflags[$para];
2005     $ptype = substr($pflags,0,4);
2006
2007     print $newpar;
2008     $newpar = "\\par\\sb120\n";
2009
2010     if ($ptype eq "chap") {
2011       # Chapter heading. Begin a new node.
2012       &hlp_menu($node)
2013         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
2014       $pflags =~ /chap (.*) :(.*)/;
2015       $node = "Chapter $1";
2016       $title = $footnotetitle = "Chapter $1: ";
2017       foreach $i (@$pname) {
2018         $ww = &word_hlp($i,1);
2019         $title .= $ww, $footnotetitle .= &word_hlp($i,0) unless $ww eq "\001";
2020       }
2021       print "\\page\n";
2022       printf "#{\\footnote %s}\n", &hlp_sectkw($node);
2023       print "\${\\footnote $footnotetitle}\n";
2024       printf "+{\\footnote browse:%05d}\n", ++$browse;
2025       printf "!{\\footnote ChangeButtonBinding(\"btn_up\"," .
2026              "\"JumpId(\`nasmdoc.hlp',\`%s')\");\n",
2027              &hlp_sectkw($tstruct_up{$node});
2028       print "EnableButton(\"btn_up\")}\n";
2029       &hlp_keywords($node);
2030       print "\\keepn\\f2\\b\\fs30\\sb60\\sa60\n";
2031       print "$title\n";
2032       $newpar = "\\par\\pard\\plain\\sb120\n";
2033     } elsif ($ptype eq "appn") {
2034       # Appendix heading. Begin a new node.
2035       &hlp_menu($node)
2036         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
2037       $pflags =~ /appn (.*) :(.*)/;
2038       $node = "Appendix $1";
2039       $title = $footnotetitle = "Appendix $1: ";
2040       foreach $i (@$pname) {
2041         $ww = &word_hlp($i,1);
2042         $title .= $ww, $footnotetitle .= &word_hlp($i,0) unless $ww eq "\001";
2043       }
2044       print "\\page\n";
2045       printf "#{\\footnote %s}\n", &hlp_sectkw($node);
2046       print "\${\\footnote $footnotetitle}\n";
2047       printf "+{\\footnote browse:%05d}\n", ++$browse;
2048       printf "!{\\footnote ChangeButtonBinding(\"btn_up\"," .
2049              "\"JumpId(\`nasmdoc.hlp',\`%s')\");\n",
2050              &hlp_sectkw($tstruct_up{$node});
2051       print "EnableButton(\"btn_up\")}\n";
2052       &hlp_keywords($node);
2053       print "\\keepn\\f2\\b\\fs30\\sb60\\sa60\n";
2054       print "$title\n";
2055       $newpar = "\\par\\pard\\plain\\sb120\n";
2056     } elsif ($ptype eq "head" || $ptype eq "subh") {
2057       # Heading or subheading. Begin a new node.
2058       &hlp_menu($node)
2059         if $tstruct_level{$tstruct_next{$node}} > $tstruct_level{$node};
2060       $pflags =~ /.... (.*) :(.*)/;
2061       $node = "Section $1";
2062       $title = $footnotetitle = "$1. ";
2063       foreach $i (@$pname) {
2064         $ww = &word_hlp($i,1);
2065         $title .= $ww, $footnotetitle .= &word_hlp($i,0) unless $ww eq "\001";
2066       }
2067       print "\\page\n";
2068       printf "#{\\footnote %s}\n", &hlp_sectkw($node);
2069       print "\${\\footnote $footnotetitle}\n";
2070       printf "+{\\footnote browse:%05d}\n", ++$browse;
2071       printf "!{\\footnote ChangeButtonBinding(\"btn_up\"," .
2072              "\"JumpId(\`nasmdoc.hlp',\`%s')\");\n",
2073              &hlp_sectkw($tstruct_up{$node});
2074       print "EnableButton(\"btn_up\")}\n";
2075       &hlp_keywords($node);
2076       print "\\keepn\\f2\\b\\fs30\\sb60\\sa60\n";
2077       print "$title\n";
2078       $newpar = "\\par\\pard\\plain\\sb120\n";
2079     } elsif ($ptype eq "code") {
2080       # Code paragraph.
2081       print "\\keep\\f1\\sb120\n";
2082       foreach $i (@$pname) {
2083         warn "code line longer than 68 chars: $i\n" if length $i > 68;
2084         $i =~ s/\\/\\\\/g;
2085         $i =~ s/\{/\\\{/g;
2086         $i =~ s/\}/\\\}/g;
2087         print "$i\\par\\sb0\n";
2088       }
2089       $newpar = "\\pard\\f0\\sb120\n";
2090     } elsif ($ptype eq "bull" || $ptype eq "norm") {
2091       # Ordinary paragraph, optionally bulleted. We wrap, FWIW.
2092       if ($ptype eq "bull") {
2093         print "\\tx360\\li360\\fi-360{\\f3\\'9F}\\tab\n";
2094         $newpar = "\\par\\pard\\sb120\n";
2095       } else {
2096         $newpar = "\\par\\sb120\n";
2097       }
2098       $line = '';
2099       @a = @$pname;
2100       $wd = $wprev = '';
2101       do {
2102         do { $w = &word_hlp((shift @a),1); } while $w eq "\001"; # hack
2103         $wd .= $wprev;
2104         if ($w eq ' ' || $w eq '' || $w eq undef) {
2105           if (length ($line . $wd) > 75) {
2106             $line =~ s/\s*$//; # trim trailing spaces
2107             print "$line \n"; # and put one back
2108             $line = '';
2109             $wd =~ s/^\s*//; # trim leading spaces
2110           }
2111           $line .= $wd;
2112           $wd = '';
2113         }
2114         $wprev = $w;
2115       } while ($w ne '' && $w ne undef);
2116       if ($line =~ /\S/) {
2117         $line =~ s/\s*$//; # trim trailing spaces
2118         print "$line\n";
2119       }
2120     }
2121   }
2122
2123   # Close file.
2124   print "\\page}\n";
2125   select STDOUT;
2126   close TEXT;
2127 }
2128
2129 sub word_hlp {
2130   my ($w, $docode) = @_;
2131   my $wtype, $wmajt;
2132
2133   return undef if $w eq '' || $w eq undef;
2134   $wtype = substr($w,0,2);
2135   $wmajt = substr($wtype,0,1);
2136   $w = substr($w,2);
2137   $w =~ s/\\/\\\\/g;
2138   $w =~ s/\{/\\\{/g;
2139   $w =~ s/\}/\\\}/g;
2140   $w =~ s/<.*>// if $wmajt eq "w"; # remove web links
2141   substr($w,0,length($w)-1) =~ s/-/\\\'AD/g if $wmajt ne "x"; #nonbreakhyphens
2142   if ($wmajt eq "n" || $wtype eq "e " || $wtype eq "w ") {
2143     return $w;
2144   } elsif ($wtype eq "sp") {
2145     return ' ';
2146   } elsif ($wtype eq "da") {
2147     return "\\'96";
2148   } elsif ($wmajt eq "c" || $wtype eq "wc") {
2149     $w =~ s/ /\\\'A0/g; # make spaces non-breaking
2150     return $docode ? "{\\f1 ${w}}" : $w;
2151   } elsif ($wtype eq "es") {
2152     return "{\\i ${w}";
2153   } elsif ($wtype eq "ee") {
2154     return "${w}}";
2155   } elsif ($wtype eq "eo") {
2156     return "{\\i ${w}}";
2157   } elsif ($wtype eq "x ") {
2158     return "{\\uldb ";
2159   } elsif ($wtype eq "xe") {
2160     $w = &hlp_sectkw($w);
2161     return "}{\\v ${w}}";
2162   } elsif ($wmajt eq "i") {
2163     return "\001";
2164   } else {
2165     die "panic in word_hlp: $wtype$w\n";
2166   }
2167 }
2168
2169 sub hlp_menu {
2170   my ($topitem) = @_;
2171   my $item, $kword, $i, $mpname, $title;
2172
2173   $item = $tstruct_next{$topitem};
2174   print "\\li360\\fi-360\n";
2175   while ($item) {
2176     $title = "";
2177     $mpname = $tstruct_pname{$item};
2178     foreach $i (@$mpname) {
2179       $ww = &word_hlp($i, 0);
2180       $title .= $ww unless $ww eq "\001";
2181     }
2182     $kword = &hlp_sectkw($item);
2183     print "{\\uldb ${item}: $title}{\\v $kword}\\par\\sb0\n";
2184     $item = $tstruct_mnext{$item};
2185   }
2186   print "\\pard\\sb120\n";
2187 }
2188
2189 sub hlp_sectkw {
2190   my ($node) = @_;
2191   $node =~ tr/A-Z/a-z/;
2192   $node =~ tr/- ./___/;
2193   $node;
2194 }
2195
2196 sub hlp_keywords {
2197   my ($node) = @_;
2198   my $pfx = "K{\\footnote ";
2199   my $done = 0;
2200   foreach $i (0..$#itags) {
2201     (print $pfx,$hlp_index[$i]), $pfx = ";\n", $done++
2202         if $idxnodes{$node,$itags[$i]};
2203   }
2204   print "}\n" if $done;
2205 }
2206
2207 # Make tree structures. $tstruct_* is top-level and global.
2208 sub add_item {
2209   my ($item, $level) = @_;
2210   my $i;
2211
2212   $tstruct_pname{$item} = $pname;
2213   $tstruct_next{$tstruct_previtem} = $item;
2214   $tstruct_prev{$item} = $tstruct_previtem;
2215   $tstruct_level{$item} = $level;
2216   $tstruct_up{$item} = $tstruct_last[$level-1];
2217   $tstruct_mnext{$tstruct_last[$level]} = $item;
2218   $tstruct_last[$level] = $item;
2219   for ($i=$level+1; $i<$MAXLEVEL; $i++) { $tstruct_last[$i] = undef; }
2220   $tstruct_previtem = $item;
2221   push @nodes, $item;
2222 }
2223
2224 # PostScript font metric data. Used for line breaking.
2225 sub font_metrics {
2226   @timesr = (
2227      250,   0,   0,   0,   0,   0,   0,   0,
2228        0,   0,   0,   0,   0,   0,   0,   0,
2229        0,   0,   0,   0,   0,   0,   0,   0,
2230        0,   0,   0,   0,   0,   0,   0,   0,
2231      250, 333, 408, 500, 500, 833, 778, 333,
2232      333, 333, 500, 564, 250, 333, 250, 278,
2233      500, 500, 500, 500, 500, 500, 500, 500,
2234      500, 500, 278, 278, 564, 564, 564, 444,
2235      921, 722, 667, 667, 722, 611, 556, 722,
2236      722, 333, 389, 722, 611, 889, 722, 722,
2237      556, 722, 667, 556, 611, 722, 722, 944,
2238      722, 722, 611, 333, 278, 333, 469, 500,
2239      333, 444, 500, 444, 500, 444, 333, 500,
2240      500, 278, 278, 500, 278, 778, 500, 500,
2241      500, 500, 333, 389, 278, 500, 500, 722,
2242      500, 500, 444, 480, 200, 480, 541,   0,
2243        0,   0,   0,   0,   0,   0,   0,   0,
2244        0,   0,   0,   0,   0,   0,   0,   0,
2245        0,   0,   0,   0,   0,   0,   0,   0,
2246        0,   0,   0,   0,   0,   0,   0,   0,
2247        0, 333, 500, 500, 167, 500, 500, 500,
2248      500, 180, 444, 500, 333, 333, 556, 556,
2249        0, 500, 500, 500, 250,   0, 453, 350,
2250      333, 444, 444, 500,1000,1000,   0, 444,
2251        0, 333, 333, 333, 333, 333, 333, 333,
2252      333,   0, 333, 333,   0, 333, 333, 333,
2253     1000,   0,   0,   0,   0,   0,   0,   0,
2254        0,   0,   0,   0,   0,   0,   0,   0,
2255        0, 889,   0, 276,   0,   0,   0,   0,
2256      611, 722, 889, 310,   0,   0,   0,   0,
2257        0, 667,   0,   0,   0, 278,   0,   0,
2258      278, 500, 722, 500,   0,   0,   0,   0
2259   );
2260   @timesi = (
2261      250,   0,   0,   0,   0,   0,   0,   0,
2262        0,   0,   0,   0,   0,   0,   0,   0,
2263        0,   0,   0,   0,   0,   0,   0,   0,
2264        0,   0,   0,   0,   0,   0,   0,   0,
2265      250, 333, 420, 500, 500, 833, 778, 333,
2266      333, 333, 500, 675, 250, 333, 250, 278,
2267      500, 500, 500, 500, 500, 500, 500, 500,
2268      500, 500, 333, 333, 675, 675, 675, 500,
2269      920, 611, 611, 667, 722, 611, 611, 722,
2270      722, 333, 444, 667, 556, 833, 667, 722,
2271      611, 722, 611, 500, 556, 722, 611, 833,
2272      611, 556, 556, 389, 278, 389, 422, 500,
2273      333, 500, 500, 444, 500, 444, 278, 500,
2274      500, 278, 278, 444, 278, 722, 500, 500,
2275      500, 500, 389, 389, 278, 500, 444, 667,
2276      444, 444, 389, 400, 275, 400, 541,   0,
2277        0,   0,   0,   0,   0,   0,   0,   0,
2278        0,   0,   0,   0,   0,   0,   0,   0,
2279        0,   0,   0,   0,   0,   0,   0,   0,
2280        0,   0,   0,   0,   0,   0,   0,   0,
2281        0, 389, 500, 500, 167, 500, 500, 500,
2282      500, 214, 556, 500, 333, 333, 500, 500,
2283        0, 500, 500, 500, 250,   0, 523, 350,
2284      333, 556, 556, 500, 889,1000,   0, 500,
2285        0, 333, 333, 333, 333, 333, 333, 333,
2286      333,   0, 333, 333,   0, 333, 333, 333,
2287      889,   0,   0,   0,   0,   0,   0,   0,
2288        0,   0,   0,   0,   0,   0,   0,   0,
2289        0, 889,   0, 276,   0,   0,   0,   0,
2290      556, 722, 944, 310,   0,   0,   0,   0,
2291        0, 667,   0,   0,   0, 278,   0,   0,
2292      278, 500, 667, 500,   0,   0,   0,   0
2293   );
2294   @courr = (
2295      600,   0,   0,   0,   0,   0,   0,   0,
2296        0,   0,   0,   0,   0,   0,   0,   0,
2297        0,   0,   0,   0,   0,   0,   0,   0,
2298        0,   0,   0,   0,   0,   0,   0,   0,
2299      600, 600, 600, 600, 600, 600, 600, 600,
2300      600, 600, 600, 600, 600, 600, 600, 600,
2301      600, 600, 600, 600, 600, 600, 600, 600,
2302      600, 600, 600, 600, 600, 600, 600, 600,
2303      600, 600, 600, 600, 600, 600, 600, 600,
2304      600, 600, 600, 600, 600, 600, 600, 600,
2305      600, 600, 600, 600, 600, 600, 600, 600,
2306      600, 600, 600, 600, 600, 600, 600, 600,
2307      600, 600, 600, 600, 600, 600, 600, 600,
2308      600, 600, 600, 600, 600, 600, 600, 600,
2309      600, 600, 600, 600, 600, 600, 600, 600,
2310      600, 600, 600, 600, 600, 600, 600,   0,
2311        0,   0,   0,   0,   0,   0,   0,   0,
2312        0,   0,   0,   0,   0,   0,   0,   0,
2313        0,   0,   0,   0,   0,   0,   0,   0,
2314        0,   0,   0,   0,   0,   0,   0,   0,
2315        0, 600, 600, 600, 600, 600, 600, 600,
2316      600, 600, 600, 600, 600, 600, 600, 600,
2317        0, 600, 600, 600, 600,   0, 600, 600,
2318      600, 600, 600, 600, 600, 600,   0, 600,
2319        0, 600, 600, 600, 600, 600, 600, 600,
2320      600,   0, 600, 600,   0, 600, 600, 600,
2321      600,   0,   0,   0,   0,   0,   0,   0,
2322        0,   0,   0,   0,   0,   0,   0,   0,
2323        0, 600,   0, 600,   0,   0,   0,   0,
2324      600, 600, 600, 600,   0,   0,   0,   0,
2325        0, 600,   0,   0,   0, 600,   0,   0,
2326      600, 600, 600, 600,   0,   0,   0,   0
2327   );
2328 }