#!/usr/bin/perl -w
#
# TWiki Collaboration Platform, http://TWiki.org/
#
#****************************************************************************/
#* COPYRIGHT:                                                               */
#* Robert Bosch GmbH reserves all rights even in the event of industrial    */
#* property. We reserve all rights of disposal such as copying and passing  */
#* on to third parties.                                                     */
#* COPYRIGHT_END:                                                           */
#****************************************************************************/
#* AUTHOR:                                                                  */
#* CC/ESM2 Matthias Thullner                                                */
#* AUTHOR_END:                                                              */
#* ------------------------------------------------------------------------ */
#****************************************************************************/
#* HISTORY: (Newest entry always on top !)                                  */
#* ------------------------------------------------------------------------ */
#* $Log:  $
#* ------------------------------------------------------------------------ */
#* HISTORY_END:                                                             */
#****************************************************************************/

#****************************************************************************/
# main
#****************************************************************************/


BEGIN {
    # Set default current working directory
    if( $ENV{"SCRIPT_FILENAME"} && $ENV{"SCRIPT_FILENAME"} =~ /^(.+)\/[^\/]+$/ ) {
        chdir $1;
    }
    # Set library paths in @INC at compile time
    unshift @INC, '.';
    require 'setlib.cfg';
}


use TWiki::UI;
use TWiki::Contrib::TWiki2GoUpdate;
TWiki::UI::run( \&TWiki::Contrib::TWiki2GoUpdate::update );



__DATA__;

use strict;
use warnings;

use CGI;
use CGI::Carp qw(fatalsToBrowser);
use CGI qw(:any);
#use TWiki::Configure::CSS;


use TWiki;
use TWiki::Net;
use TWiki::Plugins;
#use TWiki::User;
use TWiki::UI::Edit;

use Getopt::Long;
use File::Find;
use File::stat;
use File::Copy;
use Cwd;


#my $root        = 'T:';
#my $destination = '../../twiki/';
#my $root        = 'D:/thu2si/TWiki/TWiki_offline/test_root/twiki/';
#my $destination = '../../twiki/';
my $root        = 'D:/thu2si/TWiki/twiki_mirror_20071221/twiki';
my $destination = 'D:/temp/twiki_test/twiki';


my ($version, $help, );
my $verbose               = 0;
my $debug                 = 0;

my $limit_txt             = 10;
my $limit_pub             = 10;

my $txt_maxsize           =   50000;
my $pub_maxsize           =   50000;

my $print_warnings        = 0;
my $print_access          = 1;
my $print_size            = 0;
my $print_date            = 0;

my $sync_all              = 0;
my $stop_scan             = 0;
my $stop_sync_pub         = 0;
my $cnt                   = 0;
my $cnt_web               = 0;
my $cnt_web_access        = 0;
my $cnt_txt               = 0;
my $cnt_pub               = 0;
my $cnt_to_sync           = 0;
my $cnt_txt_copy          = 0;
my $cnt_pub_copy          = 0;
my $cnt_error             = 0;
my $cnt_warning           = 0;
my $cnt_access_denied     = 0;
my $cnt_pub_access_denied = 0;
my $cnt_txt_too_big       = 0;
my $cnt_pub_too_big       = 0;


my %txts;
my %webs;

use Time::localtime;
my $duration = time();

my $session = "todo";

sub _printMsg;
sub _updatePubDir;

Getopt::Long::GetOptions(
                         "root=s"        => \$root,
                         "destination=s" => \$destination,
                         "sync_all"      => \$sync_all,
                         "limit_txt=i"   => \$limit_txt,
                         "limit_pub=i"   => \$limit_pub,
                         "txt_maxsize=s" => \$txt_maxsize,
                         "pub_maxsize=s" => \$pub_maxsize,
                         "print_access"  => \$print_access,
                         "verbose=i"     => \$verbose,
                         "debug:i"       => \$debug,
                         "version"       => \$version,
                         "help"          => \$help
                        ) or _printMsg ($session, "ERROR: GetOptions failed, try --help $!");



&main();

sub main
  {
    my $query = new CGI;
    my $thePathInfo    = $query->path_info();
    my $theRemoteUser  = $query->remote_user();    # mth CS-Bugzilla#7244
    my $theUrl         = $query->url;
    my $topicName      = $query->param( 'TopicName' );

    my @meta = (
                 CGI::meta({ 'http-equiv'=>'Pragma', content=>'no-cache' }),
                 CGI::meta({ 'http-equiv'=>'Cache-Control', content=>'no-cache' }),
                 CGI::meta({ 'http-equiv'=>'Expires', content=>0 }),
                 CGI::meta({ name=>'robots', content=>'noindex' }),
                 #CGI::style( { -type=>'text/css' }, TWiki::Configure::CSS::css())
                );

    # Generate standard page header
    my $hdr = CGI::start_html(
                               -title => 'TWiki2GoUpdate script',
                               -head => \@meta,
                               -class => 'patternNoViewPage');

    print CGI::header('text/html'). $hdr;

    _printMsg ($session, "TWiki2Go sync log");

    _printMsg ($session, ' $Id: zz_update_twiki2go.pm ');
    # print CGI::br();

    if ( $debug )
      {
        _printMsg ($session, "DEBUG: root=$root");
        _printMsg ($session, "DEBUG: sync_all=$sync_all");
        $print_access = 1;
      }

    $destination =~ s|\\|/|g;
    $root        =~ s|\\|/|g;

    #my $data = "$root/data";
    my $cwd = cwd;
    my $msg = "updating ";
    $msg .= "$limit_txt " if $limit_txt;
    $msg .= "TWiki source files and ";
    $msg .= "$limit_pub " if $limit_pub;
    $msg .= "attachments from \"$root\" to \"$destination/data\" (cwd=\"$cwd\")";
    _printMsg ($session, "$msg");

    #_printMsg ($session, "!*** updating twiki2go_update script ***");
    (my $scriptname = $0) =~ s|.*/||;
    _updateScript( "$root/bin/$scriptname", "$0" );

    _printMsg ($session, "!*** scanning WebPreferences for access wrights ***");
    _scanWebs();

    _printMsg ($session, "!*** scanning data ***");
    _scanData (\%webs, "$root/data");

    _printMsg ($session, "!*** updating topics ***");
    _updateData(\%txts, "$root/data", "$destination/data");

    _printMsg ($session, "!*** updating attachments and other files ***");
    _updatePub( \%webs, "$root/pub", "$destination/pub");

    _printStatistics();
  }

##################################################################
# subfunctions
##################################################################
sub _scanWebs()
  {
    # scan webs: settings, ...
    opendir (DATA, "$root/data") or _printMsg ($session, "ERROR: cannot open web \"$root/data\": $!");
    foreach my $web ( readdir(DATA) )
      {
        if ( not -f $web
             and $sync_all )
          {
            $webs{"$web"}{access} = 1;
            $cnt_web++;
            $cnt_web_access++;
            _printMsg ($session, "$cnt_web) $web  (!!sync_all!!)");
          }
        elsif  ( -f "$web"
                 or $web =~ /\./             # file with name *.*
               )
          {
            # this is a file, not a web folder
            _printMsg ($session, "   DEBUG: file $web will not be synced") if $debug;

          }
        elsif (
                   $web =~ /^_/             # not updating _default and _empty web
                or $web =~ /Trash/ )        # not updating Trash web
          {
            # this webs do not get an access key, so the files are not scanned and copied later on
            $cnt_web++;
            _printMsg ($session, "$cnt_web) $web web WILL NOT BE SYNCED") if $print_access;
          }
        else
          {
            _printMsg ($session, "scanning dir $web") if $debug;

            $webs{"$web"}{access} = 1;
            if ( open (FILE, "$root/data/$web/WebPreferences.txt") )
              {
                #print "$web: " . $webs{"$web"}{access} . "\n";
                $cnt_web++;
                while ( <FILE> )
                  {
                    my $line = $_;
                    _printMsg ($session, "$web (" . $webs{"$web"}{access} . "): $line") if ( $debug > 1 );
                    if ( $webs{"$web"}{access}
                         and $line =~ /^(   |\t)+\*\s+Set\s+(ALLOWWEBVIEW|DENYWEBVIEW)\s*=\s*\w/ )
                      {
                        $webs{"$web"}{WebPreferences_access} .= $line;
                        $webs{"$web"}{access} = 0;
                      }
                  }
                if ( $webs{"$web"}{access} )
                  {
                    _printMsg ($session, "$cnt_web) $web web will be synced") if $print_access;
                    $cnt_web_access++;
                  }
                else 
                  {
                    _printMsg ($session, "$cnt_web) $web web <-- ACCESS DENIED: " . $webs{"$web"}{WebPreferences_access} ) if $print_access;
                  }
                close FILE or _printMsg ($session, "ERROR: cannot close \"$root/data/$web/WebPreferences.txt\"");
              }
            else
              {
                delete $webs{"$web"};
                _printMsg ($session, "ERROR: cannot open \"$root/data/$web/WebPreferences.txt\": $!\n");
              }
          }
      }
    closedir DATA or print "ERROR: cannot close \"$root/data\": $!";
  } #end sub _scanWebs


sub _updateScript( $$ )
  {
    my ($rootfile, $destinationfile) = @_;

    my $stat_root = stat( "$rootfile" ) or print "ERROR: cannot stat $rootfile: $!";
    my $stat_destination = stat( "$destinationfile" ) or print "ERROR: cannot stat $destinationfile";

#mth    if ( ($stat_root->mtime - $stat_destination->mtime) > 7200 )
    if ( ($stat_root->mtime - $stat_destination->mtime) > 10 )
      {
	_printMsg ($session, "!*** updating script ***");
	if ( copy ("$rootfile", "$destinationfile") )
	  {
	    _printMsg ($session, "\"$rootfile\" copied to \"$destinationfile") if $debug;
	  }
	else
	  {
	    _printMsg ($session, "ERROR: cannot copy \"$rootfile\" to \"$destinationfile\": $!");
	    $cnt_error++;
	  }
      }
    _printMsg ($session, "     root: atime=" . $stat_root->atime . " mtime=" . $stat_root->mtime . " ctime=" . $stat_root->ctime ) if $debug;
    _printMsg ($session, "     destination: atime=" . $stat_destination->atime . " mtime=" . $stat_destination->mtime . " ctime=" . $stat_destination->ctime ) if $debug;

  }

sub _scanData( $$ )
  {
    my ($rh_webs, $dir) = @_;
  LIMIT_TXT:
    foreach my $web ( sort keys %{ $rh_webs } )
      {
	if ( $rh_webs->{"$web"}{access} )
	  {
	    opendir (FILE, "$dir/$web") or _printMsg ($session, "ERROR: cannot open data dir \"$dir/$web\": $!");
	    foreach my $file ( readdir(FILE) )
	      {
		# for development: only eval the first x files
		if ( $cnt >= $limit_txt and $limit_txt != 0 )
		  {
		    #_printStatistics();
		    next LIMIT_TXT;
		  }

		$cnt++;
		if ( $file =~ /\.txt$/  )
		  { # only scan sources *.txt
		    $cnt_txt++;
		    $cnt_to_sync++;
		    my $lc_file = lc($file);
		    $txts{"$web/$lc_file"}{files}{"$web/$file"} = 1;
		    _printMsg ($session, "$cnt) \"$web/$file\" scanned") if $debug;
		  }
		else
		  {
		    _printMsg ($session, "       no TWiki source: $file") if $debug;
		  }
	      }

	    # abort with to many errors
	    if ( $cnt_error >= 100 )
	      {
		_printMsg ($session, "ERROR: too many errors -> aborting");
		_printStatistics();
		exit;
	      }
	  }
	else
	  {
	    #no access to complete web
	  }
      } #end foreach my $web
  } #end sub _scanDara
	

sub _updateData( $$ )
  {
    my ($rh_txts, $root, $destination) = @_;
    my $file;

    my $cnt_sync = 0;

    foreach my $lc_file ( sort keys %{$rh_txts} )
      {
	my $cnt_same_name = 0;
	foreach my $filename ( sort keys %{$rh_txts->{"$lc_file"}{files}} )
	  {
	    $cnt_same_name++;
	    $file = "$filename";
	    $cnt_sync++;
	  }

	if ( $cnt_same_name > 1 )
	  {
	    _printMsg ($session, "$lc_file is a topic which exists several times with same name");
	    # call subroutine to find out what file to sync
	  }

	 _printMsg ($session, "DEBUG: checking \"$file\"") if $debug;

	my $stat_root = stat( "$root/$file" ) or _printMsg ($session, "ERROR: cannot stat file \"$root/$file\": $!");
	#print "            root: atime=" . $stat_root->atime . " mtime=" . $stat_root->mtime . " ctime=" . $stat_root->ctime . "\n" if $debug;

	if ( $stat_root->size > $txt_maxsize )
	  { # file bigger than x bytes
	    _printMsg ($session, "$cnt_sync/$cnt_txt) WARNING: \"$file\" not copied, file is to big: ". $stat_root->size . " bytes") if $print_size;
	    $cnt_txt_too_big++;
	  }
	else
	  { # now sync file
	    my $copy;
	    (my $web = $file) =~ s|/[^/]*$||;
	    if ( not -d "$destination/$web")
	      {
		# create path
		_printMsg ($session, "INFO: \"$destination/$web\" does not exist -> creating it");
		mkdir ("$destination/$web") or print "ERROR: cannot create dir \"$destination/$web\": $! ";
	      }

	    if ( -f "$destination/$file" )
	      {
		_printMsg ($session, "    \"$destination/$file\" found") if $debug;

		my $stat_destination = stat( "$destination/$file" ) or print "ERROR: cannot stat file $file";
		_printMsg ($session, "     destination: atime=" . $stat_destination->atime . " mtime=" . $stat_destination->mtime . " ctime=" . $stat_destination->ctime ) if $debug;
		_printMsg ($session, "            root: atime=" . $stat_root->atime . " mtime=" . $stat_root->mtime . " ctime=" . $stat_root->ctime ) if $debug;

		my $time_diff = $stat_root->mtime - $stat_destination->mtime;

		_printMsg ($session, "              time_diff=$time_diff") if $debug;
		if ( $time_diff > 7200 ) #2h newer in root
		  {
		    # update file from root
		    $copy = 1;
		  }
		elsif ( $time_diff < -7200 ) #destination is newer
		  {
		    _printMsg ($session, "WARNING: \"$file\" destination is newer than root ($time_diff: )") if $print_date;
		    $cnt_warning++;
		    $copy = 0;
		  }
		else
		  {
		    # files are +/- x sec of the same age -> do not sync
		    $copy = 0;
		  }
	      }
	    else
	      {
		# file does not exist
		$copy = 1;
	      }

	    if ( $copy )
	      {
		if ( copy ("$root/$file", "$destination/$file") )
		  {
		    $cnt_txt_copy++;
		    _printMsg ($session, "$cnt_sync/$cnt_to_sync) $file copied");
		    _printMsg ($session, "$cnt_sync/$cnt_to_sync) \"$root/$file\" copied to \"$destination/$file") if $debug;
		  }
		else
		  {
		    _printMsg ($session, "ERROR: cannot copy \"$root/$file\" to \"$destination/$file\": $!");
		    $cnt_error++;
		  }
	      } #end if ( $copy )
	  }

	# abort with to many errors
	if ( $cnt_error >= 100 )
	  {
    	    _printMsg ($session, "ERROR: too many errors -> aborting");
	    print_statistics();
	    exit 0;
	  }
      } # end foreach my $lc_file
  }# end sub _updateData

sub _updatePub( $$ )
  {
    my ($rh_webs, $root, $destination) = @_;

    foreach my $web ( sort keys %{ $rh_webs } )
      {
	_printMsg ($session, "DEBUG: checking files in web $web") if $debug;
	if ( $rh_webs->{"$web"}{access} )
	  {
	    _printMsg ($session, "DEBUG: updating files in web $web") if $debug;
      if ( -d "$root/$web" )
        {
	        _updatePubDir ( "$root/$web", "$destination/$web" );
	      }
	  }
	else
	  {
	    #no access to complete web
	  }
      } #end foreach my $web
  } #end sub _scanDara

sub _updatePubDir ( $$ )
  {
    my ( $root, $destination) = @_;

    _printMsg ($session, "DEBUG: updating files $root -> $destination") if $debug;

    if ( not -d "$destination")
      {
	# create dir
 _printMsg ($session, "INFO: creating pub dir \"$destination\"");
	if ( mkdir ("$destination") )
	  {
	  	# no error -> continue
	  }
	else
	  {
	    _printMsg ($session, "ERROR: cannot create pub dir \"$destination\": $! ");
	    $cnt_error++;
	  }
      }

    opendir (FILE, "$root") or _printMsg ($session, "ERROR: cannot open pub dir \"$root\": $!");
    foreach my $file ( readdir(FILE) )
      {
	# for development: only eval the first x files
	if ( $cnt_pub >= $limit_pub and $limit_pub != 0 )
	  {
	    #_printStatistics();
	    next;
	  }

	if ( $file =~ /^\./ 
	     or $file =~ /,v$/ )
	  {
	    # do not update this file
	    next;
	  }

	$cnt++;
	if ( -f "$root/$file"  )
	  { # attachment
	    $cnt_pub++;

	    _printMsg ($session, "DEBUG: updating attachment $file") if $debug;
	    my $stat_root = stat( "$root/$file" ) or _printMsg ($session, "ERROR: cannot stat root attachment \"$root/$file\": $!");

	    if ( $stat_root->size > $pub_maxsize )
	      { # file bigger than x bytes
		_printMsg ($session, "$cnt_pub_copy) WARNING: attachment \"$root/$file\" not copied, file is to big: ". $stat_root->size . " bytes") if $print_size;
		$cnt_pub_too_big++;
	      }
	    else #if ( $stat_root->size > $pub_maxsize )
	      {
		# continue updating attachment
		
		my $copy;
		if ( not -d "$destination")
		  {
		    # create dir
		    if ( mkdir ("$destination") )
		      {
			_printMsg ($session, "INFO: created pub dir \"$destination\"");
		      }
		    else
		      {
			_printMsg ($session, "ERROR: cannot create pub dir \"$destination\": $! ");
		      }
		  }
		if ( -f "$destination/$file" )
		  {
		    my $stat_destination = stat( "$destination/$file" ) or print "ERROR: cannot stat attachment \"$destination/$file\": $!";
		    _printMsg ($session, "     destination: atime=" . $stat_destination->atime . " mtime=" . $stat_destination->mtime . " ctime=" . $stat_destination->ctime ) if $debug;
		    _printMsg ($session, "            root: atime=" . $stat_root->atime        . " mtime=" . $stat_root->mtime        . " ctime=" . $stat_root->ctime        ) if $debug;

		    my $time_diff = $stat_root->mtime - $stat_destination->mtime;

		    _printMsg ($session, "              time_diff=$time_diff") if $debug;
		    if ( $time_diff > 7200 ) #2h newer in root
		      {
			# file from root was updated
			$copy = 1;
		      }
		    elsif ( $time_diff < -7200 ) #destination is newer
		      {
			_printMsg ($session, "WARNING: \"$destination/$file\" is newer than root ($time_diff: )") if $print_date;
			$cnt_warning++;
			$copy = 0;
		      }
		    else
		      {
			# files are +/- x sec of the same age -> do not sync
			$copy = 0;
		      }
		  }
		else
		  {
		    # file does not exist
		    $copy = 1;
		  }

		if ( $copy )
		  {
		    if ( copy ("$root/$file", "$destination/$file") )
		      {
			$cnt_pub_copy++;
			_printMsg ($session, "$cnt_pub_copy) $root/$file copied") if not $debug;
			_printMsg ($session, "$cnt_pub_copy) \"$root/$file\" copied to \"$destination/$file") if $debug;
		      }
		    else
		      {
			_printMsg ($session, "ERROR: cannot copy \"$root/$file\" to \"$destination/$file\": $!");
			$cnt_error++;
	              }
		  } #end if ( $copy )
	      }
	  }
	else
	  {
	    # subdir
	    _printMsg ($session, "   found subdir $file") if $debug;
	    _updatePubDir( "$root/$file", "$destination/$file" );
	  }
      }

    # abort with to many errors
    if ( $cnt_error >= 100 )
      {
	_printMsg ($session, "ERROR: too many errors -> aborting");
	_printStatistics();
	exit;
      }
  }



sub _printStatistics()
  {
    my $offset = 6;

    my $msg = "";
    $msg .= " " x ( $offset - length($cnt) )                   . "$cnt files scanned\n";
    $msg .= " " x ( $offset - length($cnt_web) )               . "$cnt_web TWiki webs found\n";
    $msg .= " " x ( $offset - length($cnt_web_access) )        . "$cnt_web_access TWiki webs without TWikiAccessControl found (this ones will be updated)\n" if $print_access;
    $msg .= " " x ( $offset - length($cnt_txt) )               . "$cnt_txt TWiki topics found\n";
    $msg .= " " x ( $offset - length($cnt_txt_copy) )          . "$cnt_txt_copy TWiki topics copied\n";
    $msg .= " " x ( $offset - length($cnt_txt_too_big) )       . "$cnt_txt_too_big TWiki topics not copied, because they are bigger than $txt_maxsize bytes\n" if $cnt_txt_too_big;
    $msg .= " " x ( $offset - length($cnt_access_denied) )     . "$cnt_access_denied TWiki topics not copied, because of access rights\n" if $cnt_access_denied;
    $msg .= " " x ( $offset - length($cnt_pub) )               . "$cnt_pub files found in ./twiki/pub/\n";
    $msg .= " " x ( $offset - length($cnt_pub_copy) )          . "$cnt_pub_copy attachments copied\n";
    $msg .= " " x ( $offset - length($cnt_pub_too_big) )       . "$cnt_pub_too_big attachments not copied, because they are bigger than $pub_maxsize bytes\n" if $cnt_pub_too_big;
    $msg .= " " x ( $offset - length($cnt_pub_access_denied) ) . "$cnt_pub_access_denied attachments not copied, because of access rights\n" if $cnt_pub_access_denied;
    $msg .= " " x ( $offset - length($cnt_error) )             . "$cnt_error errors ocured\n";

    _printMsg ($session, "!*** statistics ***\n");
    _printMsg ($session, "<pre>\n$msg</pre>");

    my $now = localtime->year() + 1900 ."/". localtime->mon()."/".localtime->mday()." ".localtime->hour().":".localtime->min();
    $duration = time() - $duration;
    _printMsg ($session, "!*** $now: $0 finished in $duration seconds ***\n");

    _printMsg ($session, "go back to <a rel=\"nofollow\" href=\"/twiki/bin/view.pl/TWiki/TWiki2GoUpdate\">TWiki.TWiki2GoUpdate</a><br />");


    my $logfile = "$destination/data/TWiki/TWiki2GoUpdateLog.txt"; 
    open ( LOG, ">$logfile" ) or _printMsg ($session, "ERROR: cannot write $logfile: $!");
    print LOG "__last update done $now:__ _(now: \%GMTIME{\$year/\$mo/\$day \$hour:\$min}\%)_ <br />\n";
    print LOG "<pre>\n";
    print LOG "$msg";
    print LOG "</pre>\n";

    close LOG;

    print CGI::end_html(),"\n";
  } #end sup _printStatistics

sub _printMsg()
  {
    my( $session, $msg ) = @_;

    if( 0
        and $session->inContext('command_line') ) 
      {
        $msg =~ s/&nbsp;/ /go;
      } 
    else 
      {
        if( $msg =~ s/^\!// ) 
          {
            $msg = CGI::h3( CGI::span( { class=>'twikiAlert' }, $msg ));
          } 
        elsif( $msg =~ /ERROR:/ ) 
          {
            # 
            $msg = CGI::h4( CGI::span( { class=>'twikiAlert' }, $msg ));
            #$msg =~ s/(.*)/CGI::span( { class=>'warn' }, $1 )/ge;
            #$msg .= CGI::br();
          } 
        #elsif( $msg =~ /WARNING:/ ) 
        #  {
        #    # 
        #    $msg = CGI::h5( CGI::span( { class=>'twikiAlert' }, $msg ));
        #    #$msg =~ s/(.*)/CGI::span( { class=>'warn' }, $1 )/ge;
        #    #$msg .= CGI::br();
        #  } 
        #elsif( $msg =~ /^[A-Z]/ ) 
        #  {
        #    # SMELL: does not support internationalised script messages
        #    $msg =~ s/^([A-Z].*)/CGI::h3($1)/ge;
        #  } 
        #elsif( $msg =~ /DEBUG:/ ) 
        #  {
        #    # 
        #    $msg = CGI::li( CGI::span( { class=>'twikiAlert' }, $msg ));
        #    #$msg =~ s/(.*)/CGI::span( { class=>'warn' }, $1 )/ge;
        #    #$msg .= CGI::br();
        #  } 
        else 
          {
            $msg =~ s/(\*\*\*.*)/CGI::span( { class=>'twikiAlert' }, $1 )/ge;
            $msg =~ s/^\s\s/&nbsp;&nbsp;/go;
            $msg =~ s/^\s/&nbsp;/go;
            $msg .= CGI::br();
          }
        $msg =~ s/==([A-Z]*)==/'=='.CGI::span( { class=>'twikiAlert' }, $1 ).'=='/ge;
      }
    print $msg,"\n";
  }
