home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / nasm097d.zip / RDSRC.PL < prev    next >
Perl Script  |  1997-11-02  |  68KB  |  2,135 lines

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