#!/usr/bin/perl
#    Big Sister network monitor - generate graph from performance history data
#    Copyright (C) 2000-2005  Thomas Aeby
#
#    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., 675 Mass Ave, Cambridge, MA 02139, USA.
#

#=============================================================================
#
$BigSister::common::Usage	  = "[-D level]";
#
#=============================================================================

@BigSister::common::options = ( );
use lib "$ENV{BIGSISTER_CHROOT}/usr/local/bigsister/bin"; use lib "$ENV{BIGSISTER_CHROOT}/usr/local/bigsister/uxmon"; #inslib
use BigSister::common;
proginit();

use strict;
require bscgi;

use Statusmon::Statusmon;
use Statusmon::Grapher;
use DBCapsulator;
use DBCapsulator::SearchFilter;
use cfgfile;
use FileHandle;
use File::Path;
use Time::Local;
use POSIX "strftime";

our $dl = $BigSister::common::dl;

our $grapher = new Statusmon::Grapher();
$grapher -> read_config();

our @colors = (
    "0000FF",
    "FF0000",
    "010101",
    "D0D020",
    "FF00FF",
    "00FFFF"
);

our $cachedir = $BigSister::common::fs{"tmp"}."/bigsisgraph";
mkpath( $cachedir, 0700 );

DBCapsulator::config( (new cfgfile( "db.cfg" )) -> read_file() );
DBCapsulator::schemadir( $BigSister::common::fs{'etc'} );

our $perfdb = database DBCapsulator( "perf" );
our $perfinfodb = database DBCapsulator( "perfinfo" );
our $perfvardb = database DBCapsulator( "variable" );
our $hostdb = database DBCapsulator( "host" );

our( $days, $host, $graph, $start, $mode, $end, $zoom, $hostid, $inputvars, $sessionid );
our( %graphs, $disp, $skin, $formvars, $cgivars, %graphtemplates, $maingraphdef );

bscgi::mainloop( \&one_pass, "structured_bg", 300 );


sub one_pass {
    my( $skinset, %cgivars ) = @_;

    %graphs = ();
    unless( -o $cachedir ) {
	die( "cache directory $cachedir is owned by someone else - please remove it!" );
	exit 1;
    }
    $cgivars = \%cgivars;
    die "performance database unavailable" 
	unless( $perfdb && $perfinfodb && $perfvardb && $hostdb );
    $disp = $BigSister::common::display;
    $skin = $skinset;
    $formvars = {};
    $formvars->{"HOST"} = $host = $cgivars->{"HOST"};
    $formvars->{"GRAPH"} = $graph = $cgivars->{"GRAPH"};
    $formvars->{"START"} = $start = $cgivars->{"START"} || (time-7*24*3600);
    $formvars->{"MODE"} = $mode = $cgivars->{"MODE"};
    $formvars->{"END"} = $end = $cgivars->{"END"} || time;
    $formvars->{"XW"} = $cgivars->{"xw"} || $cgivars->{"XW"} || 800;
    $formvars->{"YW"} = $cgivars->{"yw"} || $cgivars->{"YW"} || 400;
    $formvars->{"SESSID"} = $sessionid = $cgivars->{"SESSID"} || rand(2^31);

    $cgivars->{"CHANGEDATE"} && ($mode = "CHOOSEDATE");
    $cgivars->{"DOWNLOAD"} && ($mode = "DOWNLOAD");
    ($mode eq "SRVIMG") && return( serve_image() );
    ($mode eq "DOWNLOAD") && return( download_csv() );

    my @index = $grapher->get_index( $host );
    %graphtemplates = ();
    $maingraphdef = undef;
    for( my $i=0; $i<=$#index; $i++ ) {
	my $graphdef = $grapher->read_graphdef( $index[$i]->{"fileid"} ) if($index[$i]->{"graph-id"} eq $graph);
	$maingraphdef = $graphdef if( $graphdef->{"graph"} && ! $maingraphdef );
	$graphtemplates{$graphdef->{"graph"}} = 1 if( $graphdef->{"graph"} );
    }

    unavailable() unless $maingraphdef;

    ($mode eq "SETDATE") && set_date();
    (($mode eq "CHOOSEDATE") || ! $mode) && choose_date();
    ($mode eq "DETAILS") && print_graph();

}


sub one_graph {
    my( $graph ) = @_;

    my $template = $grapher->getgraphtemplate( $graph );
    unavailable() unless( $template );

    $hostid = hostid( $host );

    # determine which variables this graph is interested in
    my $vardb = database DBCapsulator( "variable" ) or die "no access to database (table variable)";
    $vardb->search();
    my $vars = {};
    while( my $record = $vardb->getnext() ) {
        $vars->{$record->{"id"}} = $record;
    }

    my $inputids = {};
    my $inputvarids = {};

    my $input = $template->{"input"};
    foreach my $var (values %$vars) {
        my $name = $var->{"name"};
	$name .= ".0";
	if( $name =~ /^$input$/ ) {
	    $inputids->{$var->{"id"}} = $var;
	    my @groups = ($1,$2,$3);
	    my $localvars = { "HOST" => $host };
	    foreach my $group ("GROUP", "GROUP2", "GROUP3") {
	        my $val = shift @groups;
		$val = '\d+' if( $val eq "0" );
		$localvars->{$group} = $val;
	    }
	    foreach my $inputvar (@{$template->{"input-var"}}) {
	        my $name = $inputvar->[1];
		$name =~ s#([\.\[\]\(\)\@])#\\$1#g;
		my $expanded = expand( $name, $localvars );
		foreach my $varfound ( grep( ($_->{"name"} . ".0") =~ /^$expanded$/, values %$vars ) ) {
		    $inputvarids->{$varfound->{"id"}} = $varfound;
		}
	    }
	}
    }

    $inputvars = track_inputvars( $inputvarids ) if( %$inputvarids );
    track_graphs( $inputids, $template );
}


sub mkfilter {
    my( $ids, $timeoffset ) = @_;

    $timeoffset ||= 0;
    my $filtexp = "";
    for my $id (keys %$ids) {
        $filtexp .= " OR " if( $filtexp );
        $filtexp .= "variable = $id";
    }
    $filtexp = "host = $hostid AND time >= ".($start-$timeoffset)." AND time <= $end AND ($filtexp)";

    my $filter = new DBCapsulator::SearchFilter();
    $filter->sqlexpr( $filtexp );
    $filter->addorder( "time" );
    return $filter;
}



sub track_graphs() {
    my( $inputids, $template ) = @_;

    my $filter = mkfilter( $inputids );
    $perfdb->search( $filter );
    $perfdb->clear_sortcache( 500 );

    my $recent = -1;
    my $nextchange = -1;
    my %varbindings;
    my $currentvars;
    my $inputpattern = $template->{"input"};
    while( my $record = $perfdb->getnext_locallysorted( $filter ) ) {
	next unless( defined( $inputids->{$record->{"variable"}} ) );
	next if( $record->{"time"} < $recent );
	$recent = $record->{"time"};

	if( $recent >= $nextchange ) {
	    # inputvars have changed, so input => graph bindings must be cleared
	    %varbindings = ();
	    my $i = 1;
	    while( ($i<=$#$inputvars) && ($inputvars->[$i]->{"time"} <= $recent) ) {
	        $i++;
	    }
	    $currentvars = $inputvars->[$i-1];
	    if( $i >= $#$inputvars ) {
	        $nextchange = time +7200;
	    }
	    else {
	        $nextchange = $inputvars->[$i]->{"time"};
	    }
	}

	my $var = ($inputids->{$record->{"variable"}}->{"name"}).".".($record->{"valindex"});
	if( defined( $varbindings{$var} ) ) {
	    store_value( $varbindings{$var}, $record->{"time"}, $record->{"value"} );
	}
	elsif( $var =~ /^$inputpattern$/ ) {
	    my $vars = {
	       "GROUP" => $1,
	       "GROUP2" => $2,
	       "GROUP3" => $3,
	       "HOST" => $host,
	       "GRAPH" => $template->{"graph"},
	    };
	    foreach my $var (@{$template->{"input-var"}}) {
	        my $varname = expand( $var->[1], $vars );
		$vars->{$var->[0]} = $currentvars->{$varname};
	    }
	    my $valueid = expand( 
	        $template->{"value-identifier"} || '$HOST.$GRAPH.$GROUP.$GROUP2.$GROUP3',
		$vars
	    );
	    #print "valid: $valueid\n";
	    if( defined $graphs{$valueid} ) {
	        $varbindings{$var} = $graphs{$valueid};
	    }
	    else {
	        my @valids = keys %graphs;
	        my $id = $#valids+1;
		my $graphinst = { 
		    "id" => $id,
		    "deffile" => "$cachedir/graph.$sessionid.$id.def",
		    "datafile" => "$cachedir/graph.$sessionid.$id",
		    "value-identifier" => $valueid,
		    "legend" => expand( $template->{"legend"}, $vars ),
		    "unit-label" => $template->{"unit-label"},
		    "graph" => $template->{"graph"},
		};
		my $fh = $graphinst->{"filehandle"} = new FileHandle();
		open( _DEF, ">".($graphinst->{"deffile"}) ) || die "failed to create temp file";
		while( my( $key, $value ) = each %$graphinst ) {
		    $value =~ s/[\r\n]/ /gms;
		    print _DEF "$key = $value\n";
		}
		close _DEF;
		open( $fh, ">".($graphinst->{"datafile"}) ) || die "failed to create temp file";
		$varbindings{$var} = $graphinst;
		$graphs{$valueid} = $graphinst;
	    }
	}
	else {
	    $varbindings{$var} = 0;
	}
    }

}


sub store_value {
    my( $graphinst, $time, $value ) = @_;

    return unless( $graphinst );
    my $file = $graphinst->{"filehandle"};
    print $file "$time,$value\n";
}



sub track_inputvars() {
    my( $inputvarids ) = @_;

    my $filter = mkfilter( $inputvarids, 7200 );
    $perfinfodb->search( $filter );
    $perfinfodb->clear_sortcache( 500 );
    my $recent = -1;
    my $current = { "time" => "0" };
    my @modlist = ( $current );
    while( my $record = $perfinfodb->getnext_locallysorted( $filter ) ) {
	next unless( defined( $inputvarids->{$record->{"variable"}} ) );
	next if( $record->{"time"} < $recent );
	$recent = $record->{"time"};
	my $var = ($inputvarids->{$record->{"variable"}}->{"name"}).".".($record->{"valindex"});
	unless( defined( $current->{$var} ) ) {
	    $current->{$var} = $record->{"value"};
	}
	elsif( $current->{$var} ne $record->{"value"} ) {
	    if( $current->{"time"} != $record->{"time"} ) {
		my %next = %$current;
		$next{"time"} = $record->{"time"};
		$current = \%next;
		push( @modlist, $current );
	    }
	    $current->{$var} = $record->{"value"};
	}
    }

#foreach my $tracked (@modlist) {
#    print "\n";
#    foreach my $var (sort keys %$tracked) {
#        print "$var = $tracked->{$var}\n";
#    }
#}

    return \@modlist;
}



sub hostid {
    my( $host ) = @_;
    my $hostdb = database DBCapsulator( "host" ) or die "no access to host database";
    my $filter = new DBCapsulator::SearchFilter();
    $filter->sqlexpr( "name = '$host'" );
    $hostdb->search($filter);
    while( my $record = $hostdb->getnext() ) {
        next unless( $record->{"name"} eq $host );
	return( $record->{"id"} );
    }
    die "host not found";
}


sub expand {
    my( $expr, $vars ) = @_;

    my $expanded = "";
    while( $expr =~ /^(.*?)\$([A-Z0-9]+)/i ) {
	$expanded .= $1;
	$expr = $';
	if( defined( $vars->{$2} ) ) {
	    $expanded .= $vars->{$2};
	}
	else {
	    $expanded .= "\$$2";
	}
    }
    $expanded .= $expr;
    return $expanded;
}


sub unavailable {
    my( $msg ) = @_;
    $msg = "($msg)" if( $msg );
    die "Graph for host $host, ID $graph unavailable $msg";
}


sub read_defs {
    opendir( DIR, "$cachedir" );
    my @defs = ();
    foreach my $file (readdir(DIR)) {
        if( $file =~ /^graph.$sessionid.(\d+).def$/ ) {
	    open( _DEF, "<$cachedir/$file" );
	    my $def = {};
	    while( <_DEF> ) {
	        chomp;
		if( /^(.*?) = (.*)$/ ) {
		    $def->{$1} = $2;
		}
	    }
	    close _DEF;
	    push( @defs, $def );
	}
    }
    closedir DIR;
    return \@defs;
}
        


## @fn int parse_date($text,$time)
# Parses a date/time spec. The formats recognized include
#
#   d.m.y
#   d.m
#   m/d
#   m/d/y
#
# where the year is accepted in abbreviated (e.g. 05 for 2005) and
# full form. The date can be completed by a time in the form
#
#   h:m
#
# so that a complete date/time spec might look like this:
#
#   d.m.y, h:m
#
# The parser is very (too?) tolerant and basically it assumes that
# the date/time spec is valid.
#
# @param text The date/time spec string to be parsed
# @param time (optional) The default time if the text param
#   does not include a time. If none given, time defaults to 00:00
# @return returns the unix time that corresponds with the date/time
#   spec, undef if the spec does not parse.
sub parse_date {
    my( $text, $time ) = @_;

    my( $hour, $min, $day, $mon, $year );
    if( ($text =~ /(\d{1,2}):(\d{1,2})/)
        || ($time =~ /(\d{1,2}):(\d{1,2})/) ) {
        $hour = $1;
	$min = $2;
    }
    else {
        $hour = $min = 0;
    }

    my $currentyear = ((localtime())[5]) + 1900;
    if( $text =~ /(\d+)\.(\d+)(\.\d+|)/ ) {
        $day = $1;
	$mon = $2;
	$year = $3 || $currentyear;
    }
    elsif( $text =~ m#(\d+)/(\d+)(/\d+|)# ) {
        $mon = $1;
	$day = $2;
	$year = $3 || $currentyear;
    }
    else {
        return undef;
    }
    
    $year =~ s#[/\.]##;
    # timelocal does handle the $year < 100 case
    if( $hour == 24 ) {
	return( timelocal(0,$min,$hour-24,$day,$mon-1,$year) + 24*3600 );
    }
    else {
	return( timelocal(0,$min,$hour,$day,$mon-1,$year) );
    }
}


sub remembervars {
    my $text = "";
    while( my( $var, $value ) = each %$formvars ) {
        next if( $var eq "REMEMBERARGS" );
        $text .= '<input type="hidden" name="'.$var.'" value="'.($value).'">'."\n";
    }
    $formvars->{"REMEMBERARGS"} = $text;
}


sub choose_date {
    $formvars->{"MODE"} = "SETDATE";
    remembervars();
    $formvars->{"FROM"} = strftime( "%d.%m.%y, %H:%M", localtime( $formvars->{"START"} ) );
    $formvars->{"TO"} = strftime( "%d.%m.%y, %H:%M", localtime( $formvars->{"END"} ) );
    $formvars->{"GTITLE"} = $maingraphdef->{"title"};
    print "Content-Type: text/html\n\n";
    print $disp->replace_vars( $skin, $formvars, $skin->{"histgraph_setdate.proto"} );
}
 

sub set_date {
    $formvars->{"START"} = $start = parse_date( $cgivars->{"setfromdate"} ) || (time-7*24*3600);
    $formvars->{"END"} = $end = parse_date( $cgivars->{"settodate"}, "24:00" ) || time;

    foreach my $graph (keys %graphtemplates) {
        one_graph( $graph );
    }
    foreach my $graphinst (values %graphs) {
        close( $graphinst->{"filehandle"} );
    }
    $mode = "DETAILS";
}


sub print_graph {
    $formvars->{"MODE"} = "DETAILS";
    remembervars();

    opendir( CACHE, "$cachedir" );
    foreach my $file (readdir(CACHE)) {
        next unless( -f "$cachedir/$file" );
        my $age = time-((stat("$cachedir/$file"))[9]);
	if( (($file =~ /.png/) && $age > 300)
	  || ($age > 4*3600) ) {
	    unlink( "$cachedir/$file" );
	}
    }

    $formvars->{"FROM"} = strftime( "%d.%m.%y, %H:%M", localtime( $formvars->{"START"} ) );
    $formvars->{"TO"} = strftime( "%d.%m.%y, %H:%M", localtime( $formvars->{"END"} ) );
    $formvars->{"GTITLE"} = $maingraphdef->{"title"};

    my $defs = read_defs();
    my( $currentdef ) = grep( $_->{"id"} == $cgivars->{"graphid"} ,@$defs );
    unless( $currentdef ) {
        $currentdef = $defs->[0];
    }

    my $opts = "";
    foreach my $def (sort { $a->{'legend'} cmp $b->{'legend'} } @$defs) {
        $opts .= "<option value=\"$def->{'id'}\"".
	    (($def == $currentdef)?' selected="1"':"").
	    ">$def->{'legend'}</option>\n";
    }
    $formvars->{"GRAPHS"} = $opts;

    if( $currentdef ) {
        my $fname = gen_graph( $currentdef );
	if( $fname ) {
	    $formvars->{"IMAGE"} = '<img src="?MODE=SRVIMG&file='.$fname.'" border="0">';
	}
	else {
	    $formvars->{"IMAGE"} = "In order for graphing to work, R (see <a href=\"http://www.r-project.org/\">http://www.r-project.org/</a>) and ghostscript need to be installed. It seems, they are not.";
	}
    }

    my $selected_type = $cgivars->{"type"} || "tval";
    foreach my $type qw( tval density hist denshist ) {
        $formvars->{"SEL".uc($type)} = "selected=\"1\"" if( $selected_type eq $type );
    }
    $formvars->{"BREAKS"} = $cgivars->{"breaks"} || "auto";
    foreach my $wday (@{$cgivars->{'@wday'} || [0,1,2,3,4,5,6]}) {
        $formvars->{"CHKWD$wday"} = "checked=\"1\"";
    }
    $formvars->{"DAYTSTART"} = $cgivars->{"daytstart"} || "00:00";
    $formvars->{"DAYTEND"} = $cgivars->{"daytend"} || "24:00";

    print "Content-Type: text/html\n\n";
    print $disp->replace_vars( $skin, $formvars, $skin->{"histgraph_graph.proto"} );
} 


sub extract_data {
    my( $def ) = @_;

    my $dayfrom = $cgivars->{"daytstart"} || "00:00";
    my $dayto = $cgivars->{"daytend"} || "24:00";
    $dayfrom = ($dayfrom =~ /(\d+):(\d+)/) ? ($1*3600+$2*60) : 0;
    $dayto = ($dayto =~ /(\d+):(\d+)/) ? ($1*3600+$2*60) : 24*3600;
    my %wdays = map { $_ => 1 } @{$cgivars->{'@wday'} || [0,1,2,3,4,5,6]};

    my $type = $cgivars->{"type"} || "tval";

    my $scaledown = grep( $_ eq $type, "tval" );
    my $step = ($end - $start) / $formvars->{"XW"} / 1;
    my( $lasttime, $minimum, $maximum );
    if( $scaledown ) {
	my( $dstart, $dend );
        open( IN, "<$def->{'datafile'}" );
	$dstart = <IN>;
	$dstart =~ s/,.*//gms;
	seek( IN, -500, 2 );
	$_ = <IN>;
	while( <IN> ) {
	    $dend = $_;
	};
	$dend =~ s/,.*//gms;
        $step = ($dend - $dstart) / $formvars->{"XW"} / 1 if( $dend > $dstart );
	close IN;
    }
        
    $lasttime = -$step;

    my $tmpdata = "$sessionid.".rand(2^31).".data";
    open( IN, "<$def->{'datafile'}" );
    open( OUT, ">$cachedir/$tmpdata" );
    while( <IN> ) {
        chomp;
        my( $time, $val ) = split(",", $_);
	my($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime($time);
	my $daysec = $hour*3600 + $min*60 + $sec;
	if( 
	    $wdays{$wday}
	    && ($daysec >= $dayfrom)
	    && ($daysec <= $dayto)
	) {
	    if( $scaledown ) {
	        if( $time - $lasttime < $step ) {
		    $minimum = $val if( $val < $minimum );
		    $maximum = $val if( $val > $maximum );
		}
		else {
		    if( defined $minimum ) {
		        print OUT record($lasttime,$minimum);
			print OUT record($lasttime,$maximum) unless( $minimum == $maximum );
		    }
		    $lasttime = $time;
		    $minimum = $maximum = $val;
		}
	    }
	    else {
		print OUT record($time,$val);
	    }
	}
    }
    if( defined $minimum ) {
	print OUT record($lasttime,$minimum);
	print OUT record($lasttime,$maximum) unless( $minimum == $maximum );
    }
    close IN;
    close OUT;
    return( "$cachedir/$tmpdata" );
}


sub record {
    my( $time, $val ) = @_;

    $time = strftime( "%Y-%m-%d %H:%M:%S", localtime( $time ) );
    return( "$time,$val\n" );
}

sub gen_graph {
    my( $def ) = @_;

    my $type = $cgivars->{"type"} || "tval";

    my $device = "pngalpha";
    if( `gs -sDEVICE=pngalpha 2>&1 </dev/null` =~ /unknown device/ims ) {
        $device = "png16m";
    }
    my $tmpdata = extract_data($def);

    my $filename = "$sessionid.".rand(2^31).".png";
    my $xw = int($formvars->{"XW"}/12)/10;
    my $yw = int($formvars->{"YW"}/12)/10;
    my $r = "temp <- read.table(file='$tmpdata',sep=',',header=FALSE)
bitmap('$cachedir/$filename',type='$device',width=$xw,height=$yw,res=120)
par(bg='white',bty='o',cex=1.5)
";

    my $main = $def->{"title"};
    my $legend = $def->{"legend"};
    my $unit = $def->{"unit-label"};

    my $breaks = $cgivars->{"breaks"};
    my $breakstr;
    $breakstr = ", breaks=$breaks" if( $breaks =~ /^\d+$/ );

    if( $type eq "tval" ) {
        $r .= "plot(as.POSIXct( temp\$V1 ), temp\$V2, type='l', main='$legend', sub='Time / Value', xlab='Date', ylab='$unit', col='blue' )\n";
	$r .= "grid(col='grey',lwd=2)\n";
    }
    elsif( $type eq "hist" ) {
        $r .= "hist(temp\$V2, main='$legend', xlab='$unit', sub='Histogram', ylab='Samples'$breakstr )\n";
	$r .= "grid(col='grey',lwd=2)\n";
    }
    elsif( $type eq "density" ) {
        $r .= "plot(density(temp\$V2), main='$legend', xlab='$unit', sub='Density', ylab='Density' )\n";
	$r .= "grid(col='grey',lwd=2)\n";
    }
    elsif( $type eq "denshist" ) {
        $r .= "mintemp <- min(temp\$V2)
maxtemp <- max(temp\$V2)
par(mar=c(5,4,4,4))
plot(density(temp\$V2), main='$legend', xlab='$unit', sub='Density & Histogram', ylab='Density', xlim=c(mintemp,maxtemp) )

# second graph in the same chart
par(new=T)
hist(temp\$V2, main='', xlab='', sub='', ylab='', axes=F, xlim=c(mintemp,maxtemp),border='#000050'$breakstr )
# rechts Ordinate fuer Histogramm zeichnen
axis(side=4,col='#000050')
# rechts Achsenbeschriftung
mtext('Samples',side=4,line=2,col='#000050',cex=1.5)
par(new=F)
grid(col='grey',lwd=2)
";
    }

    open( R, "|R --no-save --no-readline > $cachedir/rresult" );
    print R $r;
    close R;
    unlink( "$tmpdata" );
    return $? ? undef : $filename;
}


sub serve_image {
    print "Content-Type: image/png\n\n";
    my $file = $cgivars->{"file"};
    return() unless( $file =~ /^[0-9png\.]+$/ms );
    open( FILE, "<$cachedir/$file" );
    eval {
        binmode FILE;
	binmode STDOUT;
    };
    print <FILE>;
    close FILE;
}


sub download_csv {
    print "Content-Disposition: attachment;filename=data.csv\nContent-Type: text/comma-separated-values\n\n";
    my $defs = read_defs();
    my( $currentdef ) = grep( $_->{"id"} == $cgivars->{"graphid"} ,@$defs );
    # prevent downscaling
    $cgivars->{"type"} = "data";
    my $tmpdata = extract_data( $currentdef );
    return unless( $tmpdata );
    open( IN, "<$tmpdata" );
    print <IN>;
    close IN;
    unlink( $tmpdata );
}

    

