#!perl

# Useage: $0 error_report_file >check_list

# Check the error report file for malloc() leaks.

#       libber.c:   221:    1: cmdToken: arg: token = +
#       libber.c:   221:    1: cmdToken: arg: token = .\*
#       libber.c:   120:    1: cmdToken: mem: malloc = 1ab2655a (0002)
#       libber.c:   120:    1: cmdToken: mem: malloc = 1ab26572 (0008)
#       libber.c:   111:    1: cmdToken: mem: free = 1ab26572
#       libber.c:   120:    1: cmdToken: mem: malloc = 1ab26572 (000f)
#       libber.c:   120:    1: cmdToken: mem: calloc = 1ab26572 (000f*000f)
#         list.c:   602:    2: appList: arg: flags = 1; name = .\MSG_0000.OBJ
#         list.c:   521:    3: inList: arg: flags = 7; name = .\MSG_0000.OBJ
#         list.c:   111:    3: inList: mem: free = 1ab20000
#         list.c:   120:    2: appList: mem: malloc = 1ab26586 (0013)

#      libber.c:   461:    1: >main
#        list.c:   194:    2: |   >Falloc
#        list.c:   195:    2: |   |   arg: paras = 65535
#        list.c:   218:    2: |   |   inf: allocated 32062 @22c1
#        list.c:   219:    2: |   <Falloc
#        list.c:   194:    2: |   >Falloc
#        list.c:   195:    2: |   |   arg: paras = 65535
#        list.c:   211:    2: |   <Falloc
#        list.c:   534:    2: |   >clrList
#       types.c:   143:    3: |   |   >U_free


$[ = 0;
%mem = ();

sub deIndent {		# deIndent current line
	local($line, $h, $fnfo);
	return unless /^([^:]*:[^:]*):[^:]*:\s*/;
	$line = $&;
	$h = $';
	return unless $h =~ /^[<>\|]/;
	while($h =~ /^\|\s*/) {
		$h = $';
	}
	if($h =~ /^>/) {
		$h = $';
		$h =~ s/\s*$//;
		push(@fct, $h);
		$h = $_;
		$h .= "\n" unless /\n$/;
		push(@nfo, $h);
		$_ = '';
	} elsif($h =~ /^</) {
		pop(@fct);
		pop(@nfo);
		$_ = '';
	} else {
		$_ = $line . $fct[$#fct] . ': ' . $h;
	}
}

sub pr {
	local($h, $cnt) = "@_";
	local(@q) = split(/\s+/,$_);
	print "$q[1] $q[2] $h: @q[3..$#q]\n";
	foreach $cnt (1..$#nfo) {
		print $nfo[$#nfo - $cnt];
	}
}

sub Xmalloc {
	local($addr, $cnt) = @_;

		if(defined $mem{$addr}) {
			&pr("double malloc");
		} else {
			$mem{$addr} = $line;
			foreach $cnt (0..$#nfo) {
				$mem{$addr} .= $nfo[$cnt];
			}
		}
}

sub Xfree {
	local($addr) = @_;

		return if $addr eq "00000000";	# free(NULL) is a NOP

		if(!defined $mem{$addr}) {
			&pr("free non-allocated");
		} else {
			delete $mem{$addr};
		}
}

while(<>) {
	&deIndent;
print STDERR "$.\r";
	@h = split(/\s+/, $_);
	next unless $h[5] eq "mem:";
	$line = $_;

	if($h[6] eq "malloc" || $h[6] eq "strdup" || $h[6] eq "calloc") {
		&Xmalloc($h[8], $h[9]);
	} elsif($h[6] eq "free") {
		&Xfree($h[8]);
	} elsif($h[6] eq "realloc") {
		$h[10] =~ s/^\[//;
		$h[10] =~ s/\]$//;
		&Xfree($h[10]);
		&Xmalloc($h[8], $h[9]);
	} else {
		&pr("unknown cmd $h[6]");
	}
}

print STDERR "                   \r";

while(($addr, $line) = each(%mem)) {
	@nfo = split(/\n/, $line);
	$_ = shift(@nfo);
	foreach $cnt (0..$#nfo) {
		$nfo[$cnt] .= "\n";
	}
	&pr("Unfreed");
}
