rdsrc.pl
上传用户:yuppie_zhu
上传日期:2007-01-08
资源大小:535k
文件大小:67k
源码类别:

编译器/解释器

开发平台:

C/C++

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