The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
#
use strict ;

use App::Framework ;

# VERSION
our $VERSION = '1.003' ;

	# Create application and run it
	App::Framework->new()->go() ;


#=================================================================================
# SUBROUTINES EXECUTED BY APP
#=================================================================================

#----------------------------------------------------------------------
# Main execution
#
sub run
{
	my ($app) = @_ ;

	my %info ;
	
	# options
	my %opts = $app->options() ;

	# Get source
	my ($file) = @{$app->arglist()};

if ($opts{log})
{
	open my $fh, ">>$opts{log}" or die "Error: unable to append to log file $opts{log} : $!" ;
	print $fh "Processing $file\n" ;
}


	print "$file...\n" ;
	$info{'file'} = $file ;
	
	open my $fh, "<$file" ;
	my $line ;
	my $pkg ;
	my $fn ;
	my $fields ;
	my $pod ;
	my ($super, $call, $args) ;
	while (defined($line = <$fh>))
	{
		chomp $line ;
		$line =~ s/^\s+// ;
		$line =~ s/\s+$// ;


print "\n<$line>\n" if $opts{debug} ;
print "POD: $pod PKG:$pkg FN:$fn CALL:$call FIELDS:$fields\n" if $opts{debug} ;

		$line =~ s/^#.*// ;
		next unless $line ;
		
				
		## pod
		if ($line =~ /^=([\w]+)/)
		{
			if ($1 eq 'cut')
			{
				$pod = 0 ;
			}
			else
			{
				$pod = 1 ;
			}
		}
		
		next if $pod ;	
		
		
		## inside a call
		if ($call)
		{
			# see if call complete
			if ($line =~ /([^\)]*)\)\s*;/)
			{
				$args .= $1 ;
				my @args = split ',', $args ;
				foreach my $arg (@args)
				{
					$arg =~ s/^\s+// ;
					$arg =~ s/\s+$// ;
					$arg =~ s/^[\\\$\@\%]+// ;
				}
print " + + args (@args)\n" if $opts{debug} ;

				if ($super)
				{
					$fn = "$info{'isa'}:$fn" ;
				}
					
				push @{$info{'fn_calls'}{$fn}}, {
					'fn'	=> $call,
					'args'	=> \@args,
				} ;
				$call = undef ;
			}
			else
			{
				$args .= $line ;
			}
		}	
		
		# package App::Framework::Feature::Options ;
		if ($line =~ /^\s*package\s+([\w:_]+)/)
		{
			$pkg = $1 ;
			my $name = $pkg ;
			$name =~ s/::/_/g ;
			$info{'name'} = $name ;
			
			$pkg = pkg_name($pkg, \%opts) ;
			$info{'pkg'} = $pkg ;
			print "Package = $pkg\n" if $opts{debug} ;
		}
		
		# our @ISA = qw(NetPacket::Ethernet);
		if ($line =~ /^(?:\s*|my\s+|our\s+)\@ISA/)
		{
#			if ($line =~ /^(?:\s*|my\s+|our\s+)\@ISA\s*=\s*(?:qw.|['"])([\w+:_]+)/)
			if ($line =~ /\@ISA\s*=\s*(?:qw.|['"])([\w+:_]+)/)
			{
				my $isa = pkg_name($1, \%opts) ;
				$info{'isa'} = $isa ;
			}
			else
			{
				# undefined @ISA - try to use the last 'use'
				if ($info{'use'})
				{
					$info{'isa'} = $info{'use'}->[scalar(@{$info{'use'}})-1] ;
					
				}
			}
			print "ISA = $info{'isa'}\n" if $opts{debug};
		}
		
		# Use
		# use NetPacket::Ethernet;
		if ($line =~ /^\s*use\s+([\w+:_]+)/)
		{
			my $use = pkg_name($1, \%opts) ;
			$info{'use'} ||= [] ;
			push @{$info{'use'}}, $use ;
			print "USE = $use\n" if $opts{debug};
		}
		
		# Function definition
		# sub run
		if ($line =~ /^\s*sub\s+(\S+)/)
		{
			$info{'isa'} ||= 'UNIVERSAL' ;
			
			$fn = $1 ;
			$info{'fn_list'} ||= [] ;
			push @{$info{'fn_list'}}, $fn ;
			
			$info{'fn_details'}{$fn} = {
				'args'	=> [],
			} ;
			$info{'fn_calls'}{$fn} = [] ;

		}

		if ($fn)
		{
			# Function args
			# my () = @_ ;
			if ($line =~ /^\s*my\s*\(([^\)]*)\)\s*=\s*\@_\s*;/)
			{
				($args) = ($1) ;
print " + fn my ($args)\n" if $opts{debug} ;
				my @args = split ',', $args ;
				foreach my $arg (@args)
				{
					$arg =~ s/^\s+// ;
					$arg =~ s/\s+$// ;
					$arg =~ s/^[\\\$\@\%]+// ;
				}
print " + + args (@args)\n" if $opts{debug} ;
				$info{'fn_details'}{$fn}{'args'} = \@args ;
			}
						
			# method call
			# $this->access($options_aref) ;
			# $this->SUPER::access($options_aref) ;
			# App::Framework->access($options_aref) ;
			if ($line =~ /^[^#]*(?:\$\w+|[\w_:]+)\->(SUPER::){0,1}([^\s\(]+)\(([^\)]*)/)
			{
				($super, $call, $args) = ($1, $2, $3) ;
print " + fn call <$super>: $call($args)\n" if $opts{debug} ;

				# see if call complete
				if ($line =~ /\)\s*;/)
				{
					my @args = split ',', $args ;
					foreach my $arg (@args)
					{
						$arg =~ s/^\s+// ;
						$arg =~ s/\s+$// ;
						$arg =~ s/^[\\\$\@\%]+// ;
					}
	print " + + args (@args)\n" if $opts{debug} ;
					
					if ($super)
					{
						$call = "$info{'isa'}".":$call" ;
	print " + + call ($call)\n" if $opts{debug} ;
					}
					
					push @{$info{'fn_calls'}{$fn}}, {
						'fn'	=> $call,
						'args'	=> \@args,
					} ;
					$call = undef ;
				}
			}
		}
		
		#	my %FIELDS = (
		#		## Object Data
		#		'saved_app'	=> undef,
		#		'user'		=> 'nobody',
		#		'group'		=> 'nobody',
		#		'pid'		=> undef,
		#	) ;
		if ($line =~ /^\s*my\s+\%FIELDS\s*=\s*\(/)
		{
			++$fields ;
		}
		elsif ($fields)
		{
			if ($line =~ /^\s*['"]{0,1}([\w_]+)['"]{0,1}\s*=>\s*([^,]+),\s*/)
			{
				$info{'fields'} ||= [] ;
				push @{$info{'fields'}}, {
					'var'	=> $1,
					'val'	=> $2,
				} ;
print " + field: $1 => $2\n" if $opts{debug} ;
			}
			if ($line =~ /^\s*\)\s*;/)
			{
				$fields=0 ;
			}
			
		}
	}

	
	close $fh ;

	if ($opts{cpp})
	{
		write_cpp($file, \%info, \%opts) ;
	}
	else
	{
#		write_c($file, $opts{dir}, \%info, \%opts) ;
		write_single_cpp($file, \%info, \%opts) ;
	}

}

#----------------------------------------------------------------------
sub pkg_name
{
	my ($pkg, $opts_href) = @_ ;
	
	if ($opts_href->{pkgpath})
	{
		$pkg =~ s/::/_/g ;
	}
	else
	{
		my @fields = split '::', $pkg ;
		$pkg = pop @fields ;
	}	
	return $pkg ;
}


##----------------------------------------------------------------------
#sub write_c
#{
#	my ($file, $dir, $info_href, $opts_href) = @_ ;
#	
#	my ($base, $src_dir) = (fileparse($file, '\..*?'))[0,1] ;
#	my $outfile = "$dir/$base.c" ;
#	open my $out_fh, ">$outfile" or die "Error: cannot write to $outfile : $!";
#
#	my $pkg = $info_href->{pkg} ;
#	print $out_fh "namespace $pkg ; \n{\n" ;
#	foreach my $fn (@{$info_href->{'fn_list'}})
#	{
#		print $out_fh "void ${pkg}::$fn()\n{\n" ;
#		foreach my $call (@{$info_href->{'fn_details'}{$fn}})
#		{
#			print $out_fh "\t${pkg}::$call() ;\n"
#		}
#		print $out_fh "}\n\n" ;
#	}
#	print $out_fh "}\n"  ;
#	close $out_fh ;
#	
#}


#----------------------------------------------------------------------
sub write_cpp
{
	my ($file, $info_href, $opts_href) = @_ ;
	
#	my ($base, $src_dir) = (fileparse($file, '\..*?'))[0,1] ;

	my $pkg = $info_href->{pkg} ;
	my $base = $pkg ;
	my $dir = $opts_href->{standalone} ? $opts_href->{dir} : '';

die "Error: no name" unless $base ;
		
	## h file ######################################################################
	my $outfile = "$dir/$base.h" ;
	my $out_fh ;

	my $inc_ext = "h" ;
	if ($opts_href->{pm})
	{
		$inc_ext = "pm" ;
		$outfile = "$dir/$base.pm" ;
	}
	
	if ($dir)
	{
		open $out_fh, ">$outfile" or die "Error: cannot write to $outfile : $!";
	}
	else
	{
		$out_fh = \*STDOUT ;
	}
	
	print $out_fh "// $info_href->{file}\n\n" ;

	#class DrivesCombo : public KComboBox
	#00036 {
	#00037 public:
	#00038     DrivesCombo(QWidget* parent, const char* name = 0);
	#00039     virtual ~DrivesCombo();
	#00040 
	#00041 };

	foreach my $use (@{$info_href->{'use'}})
	{
		print $out_fh "#include \"$use.$inc_ext\"\n" ;
	}
	print $out_fh "#include \"$info_href->{isa}.$inc_ext\"\n" if $info_href->{isa} ;
	my $isa = $info_href->{isa} || $info_href->{'use'} || 'UNIVERSAL' ;
	
	print $out_fh "class $pkg : public $isa\n{\n" ;
	
	## Public
	print $out_fh "\npublic:\n" ;

	# fields
	foreach my $field_href (@{$info_href->{'fields'}})
	{
		next if $field_href->{'var'} =~ m/^_/ ;
		print $out_fh "\tint $field_href->{'var'} ;\n" ;
	}
	print $out_fh "\n" ;
	
	# methods
	foreach my $fn (@{$info_href->{'fn_list'}})
	{
		next if $fn =~ m/^_/ ;
		
		my $details_href = $info_href->{'fn_details'}{$fn} ;
		my $argstr = "" ;
		if ($opts_href->{args})
		{
			$argstr = join(', ', @{$details_href->{args}}) ;
		}
		print $out_fh "\tvoid $fn($argstr) ;\n" ;
	}

	## Protected
	print $out_fh "\nprotected:\n" ;

	# fields
	foreach my $field_href (@{$info_href->{'fields'}})
	{
		next unless $field_href->{'var'} =~ m/^_/ ;
		print $out_fh "\tint $field_href->{'var'} ;\n" ;
	}
	print $out_fh "\n" ;
	
	# methods
	foreach my $fn (@{$info_href->{'fn_list'}})
	{
		next unless $fn =~ m/^_/ ;
		
		my $details_href = $info_href->{'fn_details'}{$fn} ;
		my $argstr = "" ;
		if ($opts_href->{args})
		{
			$argstr = join(', ', @{$details_href->{args}}) ;
		}
		print $out_fh "\tvoid $fn($argstr) ;\n" ;
	}

	print $out_fh "};\n"  ;
	if ($dir)
	{
		close $out_fh ;
	}

	
	## cpp file ######################################################################
	$outfile = "$dir/$base.cpp" ;
	if ($opts_href->{pm})
	{
		$outfile = "$dir/$base.pm" ;
	}


	if ($dir)
	{
		open $out_fh, ">>$outfile" or die "Error: cannot write to $outfile : $!";
	}
	else
	{
		$out_fh = \*STDOUT ;
	}

print $out_fh "// $info_href->{file}\n\n" ;

#	my $def = uc $base ;
#	print $out_fh "#ifdef $def\n" ;
#	print $out_fh "#define $def\n" ;


	if ($dir && !$opts_href->{pm})
	{
		print $out_fh "#include \"$base.$inc_ext\"\n\n" ;
	}
		
	foreach my $fn (@{$info_href->{'fn_list'}})
	{
		print $out_fh "void ${pkg}::$fn()\n{\n" ;
		foreach my $call_href (@{$info_href->{'fn_calls'}{$fn}})
		{
			my $argstr = "" ;
			if ($opts_href->{args})
			{
				$argstr = join(', ', @{$call_href->{args}}) ;
			}
			print $out_fh "\t$call_href->{fn}($argstr) ;\n"
		}
		print $out_fh "}\n\n" ;
	}
#	print $out_fh "#endif\n" ;

	if ($dir)
	{
		close $out_fh ;
	}
	
}

#----------------------------------------------------------------------
sub write_single_cpp
{
	my ($file, $info_href, $opts_href) = @_ ;
	
#	my ($base, $src_dir) = (fileparse($file, '\..*?'))[0,1] ;

	my $pkg = $info_href->{pkg} ;
	my $base = $pkg ;
	my $dir = $opts_href->{standalone} ? $opts_href->{dir} : '';

die "Error: no name" unless $base ;
		
	## h file ######################################################################
	my $out_fh ;
	my $outfile = "$dir/$base.cpp" ;
	my $inc_ext = "cpp" ;

#	my $outfile = "$dir/$base.h" ;
#
#	my $inc_ext = "h" ;
#	if ($opts_href->{pm})
#	{
#		$inc_ext = "pm" ;
#		$outfile = "$dir/$base.pm" ;
#	}
#	
#	if ($dir)
#	{
#		open $out_fh, ">$outfile" or die "Error: cannot write to $outfile : $!";
#	}
#	else
#	{
#		$out_fh = \*STDOUT ;
#	}

#	if ($opts_href->{pm})
#	{
#		$outfile = "$dir/$base.pm" ;
#	}


	if ($dir)
	{
		open $out_fh, ">$outfile" or die "Error: cannot write to $outfile : $!";
	}
	else
	{
		$out_fh = \*STDOUT ;
	}
	
	print $out_fh "// $info_href->{file} h\n\n" ;

	#class DrivesCombo : public KComboBox
	#00036 {
	#00037 public:
	#00038     DrivesCombo(QWidget* parent, const char* name = 0);
	#00039     virtual ~DrivesCombo();
	#00040 
	#00041 };

	foreach my $use (@{$info_href->{'use'}})
	{
		print $out_fh "#include \"$use.$inc_ext\"\n" ;
	}
	print $out_fh "#include \"$info_href->{isa}.$inc_ext\"\n" if $info_href->{isa} ;
	my $isa = $info_href->{isa} || $info_href->{'use'} || 'UNIVERSAL' ;
	
	print $out_fh "class $pkg : public $isa\n{\n" ;
	
	## Public
	print $out_fh "\npublic:\n" ;

	# fields
	foreach my $field_href (@{$info_href->{'fields'}})
	{
		next if $field_href->{'var'} =~ m/^_/ ;
		print $out_fh "\tint $field_href->{'var'} ;\n" ;
	}
	print $out_fh "\n" ;
	
	# methods
	foreach my $fn (@{$info_href->{'fn_list'}})
	{
		next if $fn =~ m/^_/ ;
		
		my $details_href = $info_href->{'fn_details'}{$fn} ;
		my $argstr = "" ;
		if ($opts_href->{args})
		{
			$argstr = join(', ', @{$details_href->{args}}) ;
		}
		print $out_fh "\tvoid $fn($argstr) ;\n" ;
	}

	## Protected
	print $out_fh "\nprotected:\n" ;

	# fields
	foreach my $field_href (@{$info_href->{'fields'}})
	{
		next unless $field_href->{'var'} =~ m/^_/ ;
		print $out_fh "\tint $field_href->{'var'} ;\n" ;
	}
	print $out_fh "\n" ;
	
	# methods
	foreach my $fn (@{$info_href->{'fn_list'}})
	{
		next unless $fn =~ m/^_/ ;
		
		my $details_href = $info_href->{'fn_details'}{$fn} ;
		my $argstr = "" ;
		if ($opts_href->{args})
		{
			$argstr = join(', ', @{$details_href->{args}}) ;
		}
		print $out_fh "\tvoid $fn($argstr) ;\n" ;
	}

	print $out_fh "};\n"  ;
#	if ($dir)
#	{
#		close $out_fh ;
#	}

	
	## cpp file ######################################################################
#	$outfile = "$dir/$base.cpp" ;
#	if ($opts_href->{pm})
#	{
#		$outfile = "$dir/$base.pm" ;
#	}
#
#
#	if ($dir)
#	{
#		open $out_fh, ">>$outfile" or die "Error: cannot write to $outfile : $!";
#	}
#	else
#	{
#		$out_fh = \*STDOUT ;
#	}

print $out_fh "// $info_href->{file} cpp\n\n" ;

#	my $def = uc $base ;
#	print $out_fh "#ifdef $def\n" ;
#	print $out_fh "#define $def\n" ;

#	if ($dir && !$opts_href->{pm})
#	{
#		print $out_fh "#include \"$base.$inc_ext\"\n\n" ;
#	}
		
	foreach my $fn (@{$info_href->{'fn_list'}})
	{
		print $out_fh "void ${pkg}::$fn()\n{\n" ;
		foreach my $call_href (@{$info_href->{'fn_calls'}{$fn}})
		{
			my $argstr = "" ;
			if ($opts_href->{args})
			{
				$argstr = join(', ', @{$call_href->{args}}) ;
			}
			print $out_fh "\t$call_href->{fn}($argstr) ;\n"
		}
		print $out_fh "}\n\n" ;
	}
#	print $out_fh "#endif\n" ;

	if ($dir)
	{
		close $out_fh ;
	}
	
}


#=================================================================================
# SETUP
#=================================================================================
__DATA__

[SUMMARY]

Convert

[NAMEARGS]

source:f

[OPTIONS]

-standalone		Create output file

Run in standalone mode (i.e. not under doxygen) and create standalone files

-dir=s			Output directory [default=doxygen/src]

Directory into which the generated files are to stored (standalone mode)

-pm				Perl module files

Inn standalone mode, write out files as perl modules rather than .h/.cpp files

-makefile=s		Create makefile

Specify the Makefile path to create a doxygen makefile

-cpp=i			Create C++ [default=1]

Create pseudo C++ rather than pseudo C

-args=i			Show args [default=0]

Show function arguments

-pkgpath		Full path names

Use full path name for package rather than using the last name. For example, for App::Framework::Core, set the class to App_Framework_Core
rather than Core

[DESCRIPTION]

B<$name> reads the perl module ...


__END__

'/^=([\w]+)/: ($1 eq 'cut' ? pod=0 : pod=1);'

		## pod
		if ($line =~ /^=([\w]+)/)
		{
			if ($1 eq 'cut')
			{
				$pod = 0 ;
			}
			else
			{
				$pod = 1 ;
			}
		}

'pod:::FILTER_NEXT'
		
		next if $pod ;	
		
'call:/([^\)]*)\)\s*;/:????????'		
		## inside a call
		if ($call)
		{
			# see if call complete
			if ($line =~ /([^\)]*)\)\s*;/)
			{
				$args .= $1 ;
				my @args = split ',', $args ;
				foreach my $arg (@args)
				{
					$arg =~ s/^\s+// ;
					$arg =~ s/\s+$// ;
					$arg =~ s/^[\\\$\@\%]+// ;
				}
print " + + args (@args)\n" if $opts{debug} ;

				if ($super)
				{
					$fn = "$info{'isa'}:$fn" ;
				}
					
				push @{$info{'fn_calls'}{$fn}}, {
					'fn'	=> $call,
					'args'	=> \@args,
				} ;
				$call = undef ;
			}
			else
			{
				$args .= $line ;
			}
		}	

'/^\s*package\s+([\w:_]+)/:pkg=$1;:set_package()'	
sub set_package
{
			my $name = $pkg ;
			$name =~ s/::/_/g ;
			$info{'name'} = $name ;
			
			$pkg = pkg_name($pkg, \%opts) ;
			$info{'pkg'} = $pkg ;
	
}	
		# package App::Framework::Feature::Options ;
		if ($line =~ /^\s*package\s+([\w:_]+)/)
		{
			$pkg = $1 ;
			my $name = $pkg ;
			$name =~ s/::/_/g ;
			$info{'name'} = $name ;
			
			$pkg = pkg_name($pkg, \%opts) ;
			$info{'pkg'} = $pkg ;
			print "Package = $pkg\n" if $opts{debug} ;
		}

HASH:		
cond=>'/^(?:\s*|my\s+|our\s+)\@ISA/', flags=>'FILTER_START_IF', label=>'ISA'
label=>'ISA', flags=>'FILTER_IF', cond=>'/\@ISA\s*=\s*(?:qw.|['"])([\w+:_]+)/', vars=>'isa = pkg_name($1, \%opts);'
label=>'ISA', flags=>'FILTER_ELSE', cond=>'use', vars=>'isa=use->[scalar(@{use)-1] ;'

ARRAY: [cond, vars, flags, call, label]

['/^(?:\s*|my\s+|our\s+)\@ISA/', 											, 'FILTER_START_IF', 	, 'ISA']
['/\@ISA\s*=\s*(?:qw.|['"])([\w+:_]+)/', 	'isa = pkg_name($1, \%opts);'	, 'FILTER_IF', 			, 'ISA']
['use', 									'isa=use->[scalar(@{use)-1] ;'	, 'FILTER_ELSE', 		, 'ISA']

		# our @ISA = qw(NetPacket::Ethernet);
		if ($line =~ /^(?:\s*|my\s+|our\s+)\@ISA/)
		{
#			if ($line =~ /^(?:\s*|my\s+|our\s+)\@ISA\s*=\s*(?:qw.|['"])([\w+:_]+)/)
			if ($line =~ /\@ISA\s*=\s*(?:qw.|['"])([\w+:_]+)/)
			{
				my $isa = pkg_name($1, \%opts) ;
				$info{'isa'} = $isa ;
			}
			else
			{
				# undefined @ISA - try to use the last 'use'
				if ($info{'use'})
				{
					$info{'isa'} = $info{'use'}->[scalar(@{$info{'use'}})-1] ;
					
				}
			}
			print "ISA = $info{'isa'}\n" if $opts{debug};
		}

'/^\s*use\s+([\w+:_]+)/','use||=[]; push @{use}, pkg_name($1, \%opts) ;'	
	
		# Use
		# use NetPacket::Ethernet;
		if ($line =~ /^\s*use\s+([\w+:_]+)/)
		{
			my $use = pkg_name($1, \%opts) ;
			$info{'use'} ||= [] ;
			push @{$info{'use'}}, $use ;
			print "USE = $use\n" if $opts{debug};
		}
		
		# Function definition
		# sub run
		if ($line =~ /^\s*sub\s+(\S+)/)
		{
			$info{'isa'} ||= 'UNIVERSAL' ;
			
			$fn = $1 ;
			$info{'fn_list'} ||= [] ;
			push @{$info{'fn_list'}}, $fn ;
			
			$info{'fn_details'}{$fn} = {
				'args'	=> [],
			} ;
			$info{'fn_calls'}{$fn} = [] ;

		}

'fn:/^\s*my\s*\(([^\)]*)\)\s*=\s*\@_\s*;/','fn_details{$fn}{'args'} = args($1);'
		if ($fn)
		{
			# Function args
			# my () = @_ ;
			if ($line =~ /^\s*my\s*\(([^\)]*)\)\s*=\s*\@_\s*;/)
			{
				($args) = ($1) ;
print " + fn my ($args)\n" if $opts{debug} ;
				my @args = split ',', $args ;
				foreach my $arg (@args)
				{
					$arg =~ s/^\s+// ;
					$arg =~ s/\s+$// ;
					$arg =~ s/^[\\\$\@\%]+// ;
				}
print " + + args (@args)\n" if $opts{debug} ;
				$info{'fn_details'}{$fn}{'args'} = \@args ;
			}

'fn:/^[^#]*(?:\$\w+|[\w_:]+)\->(SUPER::){0,1}([^\s\(]+)\(([^\)]*)/', 'super=$1;call=$2;args=$3;', FILTER_START_IF, CALL
'/\)\s*;/', 'args=args(args);' FILTER_IF, CALL
'super:/\)\s*;/', '$call = "$isa".":$call" ;' FILTER_IF, CALL
'/\)\s*;/', '$call = "$isa".":$call" ;' FILTER_IF, CALL
						
			# method call
			# $this->access($options_aref) ;
			# $this->SUPER::access($options_aref) ;
			# App::Framework->access($options_aref) ;
			if ($line =~ /^[^#]*(?:\$\w+|[\w_:]+)\->(SUPER::){0,1}([^\s\(]+)\(([^\)]*)/)
			{
				($super, $call, $args) = ($1, $2, $3) ;
print " + fn call <$super>: $call($args)\n" if $opts{debug} ;

				# see if call complete
				if ($line =~ /\)\s*;/)
				{
					my @args = split ',', $args ;
					foreach my $arg (@args)
					{
						$arg =~ s/^\s+// ;
						$arg =~ s/\s+$// ;
						$arg =~ s/^[\\\$\@\%]+// ;
					}
	print " + + args (@args)\n" if $opts{debug} ;
					
					if ($super)
					{
						$call = "$info{'isa'}".":$call" ;
	print " + + call ($call)\n" if $opts{debug} ;
					}
					
					push @{$info{'fn_calls'}{$fn}}, {
						'fn'	=> $call,
						'args'	=> \@args,
					} ;
					$call = undef ;
				}
			}
		}
		
		#	my %FIELDS = (
		#		## Object Data
		#		'saved_app'	=> undef,
		#		'user'		=> 'nobody',
		#		'group'		=> 'nobody',
		#		'pid'		=> undef,
		#	) ;
		if ($line =~ /^\s*my\s+\%FIELDS\s*=\s*\(/)
		{
			++$fields ;
		}
		elsif ($fields)
		{
			if ($line =~ /^\s*['"]{0,1}([\w_]+)['"]{0,1}\s*=>\s*([^,]+),\s*/)
			{
				$info{'fields'} ||= [] ;
				push @{$info{'fields'}}, {
					'var'	=> $1,
					'val'	=> $2,
				} ;
print " + field: $1 => $2\n" if $opts{debug} ;
			}
			if ($line =~ /^\s*\)\s*;/)
			{
				$fields=0 ;
			}
			
		}