#!/usr/bin/perl -w

use strict;
use FileHandle;

my $cmdline = join(' ',$0,@ARGV);
my $out = shift(@ARGV) or die "need output file";
my $in  = shift(@ARGV) or die "need input file";
my $inh = new FileHandle $in, "r";
die "could not open file $in" unless $inh;
open(HEADER,">$out.h");
open(SOURCE,">$out.c");

print SOURCE "/* this file was automatically generated by $cmdline */

#include \"base.h\"
\n";
my @funcnames=();
my @lines=<$inh>;
foreach(@lines) {
	s"#.*"";
	s"//.*"";
	if(m"^\s*$") {next}
	unless(/(\w+\s*\*?)\s+(\w+)\s*\((.*)\)\s*;/) {next}
	my $rettype=$1;
	my $funcname=$2;
	my $params=$3;
	print HEADER "extern $rettype redirecting_sys_$funcname($params);\n";
	print HEADER "extern $rettype (*orig_sys_$funcname)($params);\n";
	print SOURCE "$rettype (*orig_sys_$funcname)($params);\n";
	push(@funcnames,$funcname);
}
print SOURCE "
#define redir(a,b,c) b=sys_call_table[a];sys_call_table[a]=c
#define unredir(a,b,c) sys_call_table[a]=b

/* redirect syscalls (save old, install new) */
void init_redir_calltable(void)
{
";
foreach(@funcnames) {
	print SOURCE "\tredir(__NR_$_, orig_sys_$_, redirecting_sys_$_);\n";
}
print SOURCE "}

/* unredirect syscalls
 this is problematic when several syscall-redirecting modules are loaded
 and not unloaded in reverse order!!!
*/
void restore_redir_calltable(void)
{
";
foreach(@funcnames) {
	print SOURCE "\tunredir(__NR_$_, orig_sys_$_, redirecting_sys_$_);\n";
}
print SOURCE "}\n\n";

foreach(@lines) {
	if(m"^\s*$") {next}
	my $headonly=0; while(s/^-//g) {$headonly++;}
	my $redirtype=0; while(s/^\+//g) {$redirtype++;}
	$redirtype=("","d","w")[$redirtype];
	unless(/(\w+)\s+(\w+)\s*\((.*)\)\s*;/) {next}
	my $rettype=$1;
	my $funcname=$2;
	my $params=$3;
	my @params=split(/\s*,\s*/,$params);
	my @paramnames=();
	my @localparams=();
	my @localdefs=();
	my @inputcopy=();
	my @outputcopy=();
	my $varcount=0;
	foreach(@params) {
	  /(\w+)$/;
	  my $name=$1;push(@paramnames,$name);
	  if(/(.*)\*\s*\w+$/){
		my $n="local$varcount";
		local $_=$1;
		my $input=s/const//g;s/^\s*//;s/\s*$//;
		my $def="\t$_ $n";
		if(/char/) {$def.="[REDIR_BUFSIZE]";
			if($input) {my $c=($varcount==1)?" redirect0(local$varcount);":"";push(@inputcopy,"\tif(strncpy_from_user(local$varcount, $paramnames[$varcount], REDIR_BUFSIZE)<0) return -EFAULT;$c")}
			else {push(@outputcopy,"\t\tif(result>=0){if(copy_to_user($paramnames[$varcount], local$varcount, result)){return -EFAULT;}}\n")}
		} else {
			if($input) {push(@inputcopy,"\tif(copy_from_user(&local$varcount, $paramnames[$varcount], sizeof(local$varcount))){return -EFAULT;}")}
			else {push(@outputcopy,"\t\tif(copy_to_user($paramnames[$varcount], &local$varcount, sizeof(local$varcount))){return -EFAULT;}\n")}
		}
		push(@localdefs,$def.";");
		my $addr=(/char/)?"":"&";
		push(@localparams,"$addr$n");
	  } else {push(@localparams,$name)}
	  $varcount++;
	}
	my $paramnames=join(", ", @paramnames);
	my $localdefs=join("\n",@localdefs); #"char local0[REDIR_BUFSIZE+1];";
	#my $localcopy=join(";\n\t\t",@localcopys);
	my $localparams=join(", ", @localparams);
	my $inputcopy=join("\n\t",@inputcopy[1..$#inputcopy]);
	my $outputcopy=join("",@outputcopy);
#	strncpy_from_user(local0, $paramnames[0], REDIR_BUFSIZE);
	if($headonly>1) {next}
	if($headonly) {
		print SOURCE "
$rettype redirecting_sys_$funcname($params)\n{\n}\n";
	} else {
		print SOURCE "
$rettype redirecting_sys_$funcname($params)
{
$localdefs
$inputcopy[0]
	if(${redirtype}redirect0(local0)) {
		$rettype result;$inputcopy
		BEGIN_KMEM
			result = orig_sys_$funcname($localparams);
		END_KMEM
$outputcopy		if(no_fallback(result)) return result;
	}
	return orig_sys_$funcname($paramnames);
}
";}
}

print SOURCE "\n/* EOF */\n";
close(SOURCE);
close(HEADER);

