#!/usr/bin/perl
#
#  Copyright (C) 2006
#  ASTRON (Netherlands Foundation for Research in Astronomy)
#  P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, diepen@astron.nl
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#  $Id$


#  Convert multiple C++ "slash-slash" style comment lines
#  into one spanning C-style comment (/* ... */), ignoring
#  lines that start with "slash-slash-hash style comments.
#  Lines starting with //# are blanked except for the ones at the beginning
#  showing the copyright.
#
#  The cxx2html tags are converted to doxygen or html tags.
#  Code from cxx2html (Html.pm) has been copied for this purpose.
#  When converting, the line numbers should stay the same, so doxygen
#  errors/warnings refer to the correct line. Also the line numbers and
#  source file shown by doxygen will be correct.


$NON_COMMENT_BLOCK = 0;
$COMMENT_BLOCK     = 1;
$FIRST_SUMMARY     = 1;

# If the caller supplied precisely one command line argument, then this script
# can proceed.
###open OUT, ">> doc/doxygen.done";

$arguments = @ARGV;
if ($arguments == 1)
{
  # The argument represents the name of the file to process.
  $filename = $ARGV[0];

  # Get current directory without trailing slash.
  $cwd = `pwd`;
  chop($cwd);
  $cwd =~ s|/$||;
  # Append file name to it (which may contain a path as well).
  $fullnm = $filename;
  if ($filename !~ m|^/|) {
    $fullnm = $cwd . "/" . $filename;
  }
  $filnm = $filename;
  $filnm =~ s|$cwd||;     # remove current directory (thus keep past casacore)
  $filnm =~ s|^/||;       # remove leading slash
  # Get the name of the header.
  $hdrnm = $filnm;
  $hdrnm =~ s|.*/||;      # remove directory (until last slash)
  $hdrnm =~ s|\..*||;     # remove extension
  # Get the name of the package and module.
  $isModHdr = 0;
  $modnm = "";
  $pkgnm = $filnm;
  $pkgnm =~ s|/.*||;      # remove first slash and beyond giving package name
  $modna = $filnm;
  $modna =~ s|$pkgnm/||;  # remove package name
  # The old situation has an extra layer like casa/casa/OS or casa/implement/OS.  # Remove that too.
  $modna =~ s%$pkgnm/|implement/%%;  # remove package name
  $modnm = $modna;
  $modnm =~ s|/.*||;      # again remove first slash and beyond giving module
  if ($modnm eq $modna) {
    # Nothing removed, so we have a .h file outside a module.
    $modnm =~ s|\..*||;   # remove extension
    # It is a module header if a directory with the same name exists.
    $tmpnm = $fullnm;
    $tmpnm =~ s|\.h$||;
    if (-d $tmpnm) {
      $isModHdr = 1;
    }
  }
  # On OS-X package tables and module Tables get the same (casa-insensitive)
  # file name which is solved by appending '_module' to a module name.
  $modnm .= "_module";

###  print OUT "$fullnm $filnm $hdrnm $modnm $pkgnm $isModHdr\n";

  # Open the file.
  $status = open(FILEHANDLE, $filename);
###  open DOXOUT, ">> doc/" . $hdrnm . $isModHdr . ".dout";

  # If the file could be opened then read the contents.
  if ($status)
  {
    # Read the contents of the file.
    @text = <FILEHANDLE>;

    # Close the file.
    close(FILEHANDLE);

    # Process the contents of the file.
    change_comment_style(@text);
  }

  # Otherwise report an error condition.
  else
  {
    print "ERROR: file \"$filename\" could not be opened.\n";
  }
}

# Otherwise, display the proper invocation for this script.
else
{
  print "usage: $0 filename\n";
}


# Handle changing a linkto tag.
# Replace linkto by the class linked to; remove possible ':description'
# Similarly replace module link; link ':classes' to the same.
# The closing </linkto> has already been removed.
sub handleLinkto
{
  my $linkstr = shift(@_);
  # Remove :description or :classes.
  $linkstr =~ s%:(description|classes)(['"]?\s*>)%$2%i;
  my $endlink = "";
  if ($linkstr =~ m|<linkto\s+class\s*=\s*([^:#]*?)\s*>|i) {
    # A link to a class.
    $linkstr =~ s|<linkto\s+class\s*=\s*['"]?(.*?)['"]?\s*>| \\link casacore::$1 |i;
    $endlink = " \\endlink";
  } elsif ($linkstr =~ m%<linkto\s+module\s*=\s*([^:#]*?)\s*>%i) {
    # A link to a module.
    $linkstr =~ s%<linkto\s+module\s*=\s*['"]?(.*?)['"]?\s*>% \\ref $1_module_anchor "%i;
    $endlink = "\"";
  } else {
    # A link to an anchor in a group or file.
    if ($linkstr =~ m%<linkto\s+(group|file)\s*=\s*(.*?)[:#](.*?)\s*>%i) {
      my $grpnm = $2;
      my $ref = $3;
      $grpnm =~ s|\..*||;
      $grpnm =~ s|\s*['".]\s*||g;
      $grpnm =~ s|\s+|_|g;
      $ref =~ s|\s*['".]\s*||g;
      $ref =~ s|\s+|_|g;
      $ref =~ s|:description||;
      $ref =~ s|[:]|_|g;
      $linkstr =~ s%<linkto\s+(group|file)\s*=\s*(.*?)[:#](.*?)\s*>% \\ref ${grpnm}_$ref "%i;
      $endlink = '"';
    } elsif ($linkstr =~ m%<linkto\s+class\s*=\s*['"]?(.*?)[:#](.*?)\((.*?)\)\s*(const)?\s*['"]?\s*>%i) {
      # A link to a class member function. Remove the signature.
      my $classnm = $1;
      my $funcnm = $2;
      $linkstr =~ s%<linkto(.*?)>% \\link ${classnm}::$funcnm() %i;
      $endlink = ' \\endlink';
    } elsif ($linkstr =~ m%<linkto\s+(class|module)\s*=\s*(.*?)[:#](.*?)\s*>%) {
      # A link to a class or module anchor.
      my $grpnm = $2;
      my $ref = $3;
      $grpnm =~ s|\s*['".]\s*||g;
      $grpnm =~ s|\s+|_|g;
      $ref =~ s|\s*['".]\s*||g;
      $ref =~ s|\s+|_|g;
      $ref =~ s|:description||;
      $ref =~ s|[:]|_|g;
      $linkstr =~ s%<linkto\s+(class|module)\s*=\s*(.*?)[:#](.*?)\s*>%\\ref ${grpnm}_$ref "%i;
      $endlink = '"';
    } else {
      # For the time being cannot really handle other linkto commands.
      $linkstr =~ s|<linkto\s+(.*?)>(.*?)|(see $2 ($1))|i;
    }
  }
  $linkstr .= $endlink;
  return $linkstr;
}

# Handle changing a reviewed tag.
sub handleReviewed
{
  # Get argument and remove tags delimiters.
  my $attr = shift(@_);
  $attr =~ s|^\s*<\s*reviewed\s*||;
  $attr =~ s|\s*>\s*$||;
  # Organize review info nicely.
  @attr = split(/(?:=["'][^'"]*['"]|=[^\s>]*)\s*/,$attr);
  $attr =~ s/.*?=//;
  my @val = split(/\s*\w+=/,$attr);
  my %attr = ();
  my $attrcnt = 0;
  while ( ($_ = shift(@attr)) && ($val = shift(@val)) ) {
    $val =~ s/^["']\s*//;
    $val =~ s/\s*["']$//;
    $val =~ s/@/\\@/g;       # doxygen treats @ as special, so escape
    $val =~ s/\.cc//g;       # doxygen cannot deal with . in <dd>
    if ( $val ) {
      $attr{$_} = $val;
      $attrcnt++;
    }
  }
  my $str = "";
  if ( $attrcnt ) {
    $str = "<h3>Review Status</h3><dl>";
    $str .= "<dt>Reviewed By:<dd>" . $attr{'reviewer'}
      if defined $attr{'reviewer'};
    $str .= "<dt>Date Reviewed:<dd>" . $attr{'date'}
      if defined $attr{'date'};
    $str .= "<dt>Test programs:<dd>" . $attr{'tests'}
      if defined $attr{'tests'};
    $str .= "<dt>Demo programs:<dd>" . $attr{'demos'}
      if defined $attr{'demos'};
    $str .= "</dl>";
  }
  return $str;
}


# Loop through all lines and change comments as needed.
sub change_comment_style
{
  $keephash = 1;
  $state = $NON_COMMENT_BLOCK;
  $newstate = $NON_COMMENT_BLOCK;
  @spanning_comment = ();
  $indent = "";
  $comment = "";
  $extraline = "";
  $grouplevel = 0;
  $namedgrouplevel = 0;
  $code = 0;           # no <src> block found
  $link = 0;           # no <linkto> block found
  $linkfr = 0;         # no <linkfrom> found
  $linkstr = "";
  $todo = 0;           # no <todo> found
  $prereq = 0;         # no <prerequisites> found
  $thrown = 0;         # no <thrown> found
  $emnote = 0;         # no <note> found
  $review = 0;         # no <reviewed> block found
  $reviewstr = "";
  $nadded = 0;         # nr of added lines for which blank lines are removed
  $summinx = -1;       # index of summary line in spanning_comment

  foreach $line (@_)
  {
    $skip = 0;
    $newstate = $NON_COMMENT_BLOCK;
    # slash-slash-hash is blanked if not at beginning of file.
    if ($keephash == 0  &&  $line =~ m|^\s*//\#|)
    {
      $line = "\n";
    }
    $comment = $line;
    # If this line contains only a slash-slash comment, then
    # the comment block might need to be converted.
    if ($line =~ m|(^\s*)(//)(\s*)(.*)|)
    {
      # Remove indentation, because doxygen treats a line with 4 leading spaces
      # (after an empty line) as an example code block.
      $indent = $1;
      $indent2 = $3;
      $comment = $4;
      if ($indent2 =~ m|\s.*|) {
        $comment = " $comment";
      }
      # slash-slash-slash/hash are kept as such (is doxygen already).
      # Four slashes is comment in code examples, thus is normal comment.
      if ($comment !~ m|^[/#][^/]|  &&  $comment !~ m|^[/#]$|)
      {
        $keephash = 0;
	$newstate = $COMMENT_BLOCK;

	# Doxygen treats #name as a request to link to name.
	# So escape the #. Don't do that for #include and if.
	$comment =~ s|([ (])#([a-z]+)|$1\\#$2|ig;
	$comment =~ s| \\#include| #include|g;
   	$comment =~ s| \\#if| #if|g;
	$comment =~ s| \\#endif| #endif|g;

	# Remove possible prototype text.
	$comment =~ s|You should have at least a preliminary understanding of these classes:||;
	$comment =~ s|<li> <linkto class=></linkto>||;

	# A module header is turned into a defgroup and added to the package.
	if ($isModHdr == 1) {
	  if ($comment =~ m|^\s*<module>|i) {
	    $comment =~ s|<module>| \\anchor ${modnm}_anchor \\ingroup $pkgnm\n\\defgroup $modnm $modnm|i;
	    $nadded += 2;
	  }
	  $comment =~ s|</module>|<a class=anchor name="${modnm}_Class_List"></a>*/\n/** \\ingroup $modnm\n\\defgroup ${modnm}_internal_classes ${modnm}_internal_classes\n\\brief Internal $modnm classes and functions|i;
	  $comment =~ s|^\s*<summary>| \\brief |ig;
	  $comment =~ s|</summary>|<p>See <a href="#${modnm}_Class_List">below</a> for an overview of the classes in this module.|i;
	} else {
	  # A class or group header's summary is added to the module's defgroup.
	  if ($comment =~ m|^\s*<summary>|i) {
	    if ($FIRST_SUMMARY == 1) {
	      $comment =~ s|<summary>| \\anchor ${hdrnm}_summary \\ingroup $modnm \n\\brief |i;
	      $FIRST_SUMMARY = 0;
	    } else {
	      $comment =~ s|<summary>| \\ingroup $modnm \n\\brief |i;
	    }
	    $nadded += 1;
	    $summinx = @spanning_comment + 1;   # index of ingroup line
	  }
	  $comment =~ s|</summary>||i;
	}

	# Handle group tags (also the old fashioned +grp/-grp).
	# Turn a named group with a summary into a struct, so doxygen
	# puts it into the module header at the right place.
	# Keep track of the group level, so such a named group is ended
	# like a struct with };.
	# Named groups are always preceeded by an anchor for linkto references.
        # Unnamed groups are removed because doxygen handles them in a strange way.
        # Sometimes they do not appear in the summary output.
	$comment =~ s|^\s*\+grp|<group>|;
	$comment =~ s|^\s*-grp|</group>|;
	if ($comment =~ m|<group\s+name\s*=\s*(.*?)\s*>|i) {
          $named = 1;
	  $grpnm = $1;
	  $grpnm =~ s|\s*['".]\s*||g;
	  $grpnm =~ s|\s+|_|g;
	  $grpnm =~ s|:|_|g;
	  $comment = "\\anchor ${hdrnm}_$grpnm ";
	  # Only a named group at the highest level is turned into a struct.
	  # Do this only if it has a summary, which indicates it describes
	  # a group of functions outside a class.
	  if ($grouplevel == 0  &&  $summinx >= 0) {
	    $namedgrouplevel += 1;
	    $extraline = "struct ${hdrnm}_global_functions_${grpnm} {\n";
	    $nadded += 1;
	  } else {
	    # Otherwise open a doxygen group.
	    $extraline = "//@\{\n";
	    $nadded += 1;
	  }
	  $grouplevel += 1;
        } elsif ($comment =~ m|<group>|) {
          $named = 0;
##	  $comment =~ s|<group>| \\name='' @\{|i;
##	  $comment =~ s|<group>|@\{|i;
	  $comment =~ s|<group>||i;
	  $grouplevel += 1;
        } elsif ($comment =~ m|</group>|) {
	  if ($namedgrouplevel == $grouplevel) {
	    $line = "\};\n";
	    $newstate = $NON_COMMENT_BLOCK;
	    $namedgrouplevel -= 1;
	  } else {
            if ($named == 1) {
              $comment =~ s|</group>|@\}|i;
            } else {
              $comment =~ s|</group>||i;
            }
	  }
	  $grouplevel -= 1;
	}

	# Change an anchor tag.
	# Replace it by a doxygen \anchor tag and an <a name=> tag.
	# Prepend the doxygen name with the header name.
	# The latter one is used for internal <a href=> references.
	if ( $comment =~ m|<anchor\s+name\s*=\s*(.*)\s*>|i ) {
	  my $nm = $1;
	  my $dnm = $nm;
	  $dnm =~ s|\s*['".]\s*||g;
	  $dnm =~ s|\s+|_|g;
	  $dnm =~ s|:|_|g;
	  $comment =~ s|<anchor\s+name\s*=\s*(.*)\s*>| \\anchor $dnm <a name=$nm>|i;
	}
	$comment =~ s|</anchor>||ig;

	# Handle linkto tags. There can be multiple of them and they can be
	# spread over multiple lines.
	if ( $link==1 ) {
	  if ( $comment =~ m|(.*?)</linkto>(.*)|i ) {
	    my $b = $2;
	    $linkstr = handleLinkto ($linkstr . " $1");
	    $comment = "$linkstr $b";
	    $link = 0;
	  } else {
	    $linkstr .= " $comment";
	    $comment = "";
	  }
	}
	while ( $link==0  &&  $comment =~ m|<linkto|i ) {
	  if ( $comment =~ m|(.*?)<linkto(.*?)</linkto>(.*)|i ) {
	    my $a = $1;
	    my $c = $3;
	    my $linkstr = handleLinkto ("<linkto $2");
	    $comment = "$a $linkstr $c";
	  } elsif ( $comment =~ m|(.*?)<linkto(.*)|i ) {
	    $link = 1;
	    $linkstr = "<linkto $2";
	    $comment = $1;
	  }
	}

	# Handle linkfrom tags. There can be multiple of them and they can be
	# spread over multiple lines.
	# Doxygen has no counterpart for it, so simply remove it.
	if ( $linkfr==1 ) {
	  if ( $comment =~ m|(.*?)</linkfrom>(.*)|i ) {
	    $comment = $2;
	    $linkfr = 0;
	  } else {
	    $comment = "";
	  }
	}
	while ( $linkfr==0  &&  $comment =~ m|<linkfrom|i ) {
	  if ( $comment =~ m|(.*?)<linkfrom(.*?)</linkfrom>(.*)|i ) {
	    $comment = "$1 $3";
	  } elsif ( $comment =~ m|(.*?)<linkfrom(.*)|i ) {
	    $linkfr = 1;
	    $comment = $1;
	  }
	}

	# Convert the section tags to h3.
	$comment =~ s|<srcblock>| \\code |ig;
	$comment =~ s|</srcblock>| \\endcode |ig;
	$comment =~ s|<motivation>|<h3>Motivation</h3>|ig;
	$comment =~ s|</motivation>||ig;
	$comment =~ s|<synopsis>|<h3>Synopsis</h3>|ig;
	$comment =~ s|</synopsis>||ig;
	$comment =~ s|<example>|<h3>Example</h3> \\anchor ${hdrnm}_example|ig;
	$comment =~ s|</example>||ig;
	$comment =~ s|<etymology>|<h3>Etymology</h3>|ig;
	$comment =~ s|</etymology>||ig;
	$comment =~ s|<motivation>|<h3>Motivation</h3>|ig;
	$comment =~ s|</motivation>||ig;
	# Remove obsolete category tag (used in e.g. Map.h>).
	$comment =~ s|<category.*?>||i;

	# Use current date in todo if needed.
	if ( $comment =~ /<todo\s*(?:asof\s*=\s*["']?(.*?)['"]?.*?)?>/i )
	{
	  my $date = $1;
	  if ( $date ) {
	    $comment =~ s|<todo.*?>|<h3>To Do ($date)</h3><ul>|i;
	  } else {
	    $comment =~ s|<todo.*?>|<h3>To Do</h3><ul>|i;
	  }
	  $todo = 1;
	  $todostr = $comment;
	  $comment = "";
	}
	# Do not insert todo if empty (i.e. no <li> lines).
	if ( $todo ) {
	  if ( $comment =~ m|</todo>|i ) {
	    $comment = "";
	    $todo = 0;
	  } elsif ( $comment =~ m|<li>|i ) {
	    $comment = $todostr . $comment;
	    $todo = 0;
	  } else {
	    $todostr .= $comment;
	    $comment = ""
	  }
	}
	$comment =~ s|</todo>|</ul>|i;

	# Do not use <ul></ul> for an empty prerequisite.
	if ( $comment =~ /^\s*<prerequisite>/i ) {
	  $comment = "";
	  $prereq = 1;
	} elsif ( $prereq ) {
	  if ( $comment =~ m|^\s*</prerequisite>|i ) {
	    # No lines, so do not insert </ul>.
	    $comment = "";
	    $prereq = 0;
	  } elsif ( $comment !~ m|^\s*$| ) {
	    # First non-empty line, so prepend with <ul>.
	    $comment = "<h3>Prerequisite</h3><ul> " . $comment;
	    $prereq = 0;
	  }
	}
	$comment =~ s|^\s*</prerequisite>|</ul>|i;

	# Do not use <ul></ul> for an empty thrown.
	if ( $comment =~ /^\s*<thrown>/i ) {
	  $comment = "";
	  $thrown = 1;
	} elsif ( $thrown ) {
	  if ( $comment =~ m|^\s*</thrown>|i ) {
	    # No lines, so do not insert </ul>.
	    $comment = "";
	    $thrown = 0;
	  } elsif ( $comment !~ m|^\s*$| ) {
	    # First non-empty line, so prepend with <ul>.
	    $comment = "<h3>Thrown Exceptions</h3><ul> " . $comment;
	    $thrown = 0;
	  }
	}
	$comment =~ s|^\s*</thrown>|</ul>|i;

	# Fill in templating tags.
	# Use % to prevent doxygen from making a link to class Template.
	if ( $comment =~ /<templating\s*(?:arg\s*=\s*(\w+).*?)?>/ ||
	     $comment =~ /<templating\s*(?:arg\s*=\s*["']([^'"]+)['"].*?)?>/ )
	{
	  my $arg = $1;
	  if ( $arg ) {
	    $comment =~ s|<templating.*?>|<h3>%Template Type Argument Requirements ($arg)</h3><ul>|ig;
	  } else {
	    $comment =~ s|<templating.*?>|<h3>%Template Type Argument Requirements</h3><ul>|ig;
	  }
	}
	$comment =~ s|</templating>|</ul>|ig;

	# Fill in the visibility.
	# Use that in the ingroup used in the summary.
	if ( $comment =~ /<use\s+visibility\s*=\s*(.*?)\s*>/ )
	{
	  my $arg = $1;
	  if ( $arg =~ /export/i ) {
	    $comment =~ s|<use.*?>|<h3>Intended use:</h3> Public interface|ig;
	  } elsif ( $arg =~ /local/i ) {
	    $comment =~ s|<use.*?>|<h3>Intended use:</h3> Internal|ig;
	    # Change group in summary.
	    if ($summinx > 0) {
	      $modnmi = $modnm . "_internal_classes";
	      $last = @spanning_comment[$suminx];
	      $last =~ s|ingroup $modnm|ingroup $modnmi|;
	      @spanning_comment[$suminx] = $last;
	    }
	  } else {
	    $comment =~ s|<use.*?>|<h3>Intended use:</h3> $1|ig;
	  }
	}
	$comment =~ s|</use>||ig;

	# Organize review info nicely.
	if ( $review==1 ) {
	  if ( $comment =~ m|(.*?)>(.*)|i ) {
	    my $b = $2;
	    $reviewstr = handleReviewed ($reviewstr . " $1");
	    $comment = "$reviewstr $b";
	    $review = 0;
	  } else {
	    $reviewstr .= " $comment";
	    $comment = "";
	  }
	}
	while ( $review==0  &&  $comment =~ m|<reviewed|i ) {
	  if ( $comment =~ m|(.*?)<reviewed(.*?)>(.*)|i ) {
	    my $a = $1;
	    my $c = $3;
	    my $reviewstr = handleReviewed ("<reviewed $2");
	    $comment = "$a $reviewstr $c";
	  } elsif ( $comment =~ m|(.*?)<reviewed(.*)|i ) {
	    $review = 1;
	    $reviewstr = "<reviewed $2";
	    $comment = $1;
	  }
	}
	$comment =~ s|</reviewed>||i;

	# Turn note to <em>.
	# Capitalize the type.
	my $outtype = "Note";
	if ( $comment =~ /<note/i ) {
	  $emnote = 1;
	  if ( $comment =~ /<note\s+role\s*=\s*['"]?(tip|caution|warning)["']?.*?>/i )
	  {
	    my $type = lc $1;
	    $outtype = ucfirst $type;
	  }
	}
	# Doxygen complains about a . inside an <em> block.
	# So for the time being replace a . by a ;
	# Note that this is done for the full line with is not entirely correct.
	if ( $emnote ) {
	  $comment =~ s|\.|;|g;
	}
	$comment =~ s|<note.*?>|<br><strong>$outtype:</strong><em> |i;
	if ( $comment =~ m|</note>|i ) {
	  $emnote = 0;
	}
	$comment =~ s|</note>|</em><br>|i;

	# Replace <src>. Inside the block the special characters have to
	# be replaced. Note that multiple <src> can be on a line, but it
	# is also possible that the closing </src> is on another line.
	if ( $code==1 ) {
	  if ( $comment =~ m|(.*?)</src>(.*)|i ) {
	    my $a = $1;
	    my $b = $2;
	    $a =~ s/&/&amp;/g;
	    $a =~ s/"/&quot;/g;
	    $a =~ s/</&lt;/g;
	    $a =~ s/>/&gt;/g;
	    $comment = "$a</tt>$b";
	    $code = 0;
	  } else {
	    $comment =~ s/&/&amp;/g;
	    $comment =~ s/"/&quot;/g;
	    $comment =~ s/</&lt;/g;
	    $comment =~ s/>/&gt;/g;
	  }
	}
	while ( $code==0 && $comment =~ m|<src>|i ) {
	  if ( $comment =~ m|(.*?)<src>(.*?)</src>(.*)|i ) {
	    my $a = $1;
	    my $b = $2;
	    my $c = $3;
	    $b =~ s/&/&amp;/g;
	    $b =~ s/"/&quot;/g;
	    $b =~ s/</&lt;/g;
	    $b =~ s/>/&gt;/g;
	    $comment = "$a<tt>$b</tt>$c";
	  } elsif ( $comment =~ m|(.*?)<src>(.*)|i ) {
	    my $a = $1;
	    my $b = $2;
	    $b =~ s/&/&amp;/g;
	    $b =~ s/"/&quot;/g;
	    $b =~ s/</&lt;/g;
	    $b =~ s/>/&gt;/g;
	    $comment = "$a<tt>$b";
	    $code = 1;
	  }
	}
	# Doxygen treats a trailing .. as end of list. So escape second one.
	$comment =~ s|\.\.\s*$|.\\.|g;
	# Skip an empty line if needed.
	if ($nadded > 0) {
	  if ($comment =~ /^\s*$/) {
	    $nadded -= 1;
	    $skip = 1;
	  }
	}
      }
      # doxygen group tags need slashes.
      if ($comment =~ /^\s*@[\{\}]\s*$/) {
        $comment =~ s/\s+//g;
        $line = "//$comment\n";
        $newstate = $NON_COMMENT_BLOCK;
      }
    }
    # A blank line does not change anything.
    elsif ($line =~ /^\s*$/) {
      $newstate = $state;
      if ($nadded > 0) {
	$nadded -= 1;
	$skip = 1;
      }
      $comment = "";
    } else {
      $keephash = 0;
    }

    # Act depending on new and old state.
    if ($skip == 0) {
      if ($newstate == $COMMENT_BLOCK) {
        if ($state == $COMMENT_BLOCK) {
	  # Add comment to block.
	  push @spanning_comment, "\n$comment";
        } else {
	  # Begin the spanning comment block with given indentation.
	  push @spanning_comment, "/**$comment";
        }
	if ($extraline ne "") {
	  $line = $extraline;
	  $extraline = "";
	  $state = $newstate;
	  $newstate = $NON_COMMENT_BLOCK;
	}
      }
      if ($newstate == $NON_COMMENT_BLOCK) {
        if ($state == $COMMENT_BLOCK) {
	  # End comment block and write the block.
	  push @spanning_comment, " */\n";
	  print @spanning_comment;
###          print DOXOUT @spanning_comment;
	  @spanning_comment = ();
	  $summinx = -1;
        }
        # Write the line.
        print $line;
###        print DOXOUT $line;
	if ($extraline ne "") {
	  print $extraline;
###	  print DOXOUT $extraline;
	  $extraline = "";
	}
      }
      $state = $newstate;
    }
  }

  # Output remaining comments if there.
  if ($state == $COMMENT_BLOCK)
  {
    push @spanning_comment, " */\n";
    print @spanning_comment;
###    print DOXOUT @spanning_comment;
  }
}
