#!/bin/sh

TMP=${MPRTMP-/tmp}
MPRAWK=${MPRAWK-awk}

nmfile=$TMP/mprmap.nm.$$
logfile=$TMP/mprmap.log.$$
flfile=$TMP/mprmap.fl.$$

popt=
fopt=
fofiles=
lopt=
Iopt=,
iopt=
mprso=

for arg in "$@"; do
	case "$1" in
	-p)	popt=-p;;
	-f)	fopt=true;;
	-F )	fopt=true; fofiles=$fofiles${fofiles:+,}"$2"; shift;;
	-F*)	fopt=true; fofiles=$fofiles${fofiles:+,}`echo "$1" | sed 's/-F//'`;;
	-l)	lopt=true; fopt=true;;
	-i )	iopt=$iopt${iopt:+$Iopt}"$2"; shift;;
	-i*)	iopt=$iopt${iopt:+$Iopt}`echo "$1" | sed 's/-i//'`;;
	-I )	Iopt="$2"; shift;;
	-I*)	Iopt=`echo "$1" | sed 's/-I//'`;;
	--)	shift; break;;
	-*)	echo "mprmap: warning: ignoring unknown option $1" >&2;;
	*)	break;;
	esac
	shift
done

case "$#" in
1|2)	;;
*)	echo "usage: mprmap [-f|-F a.c,b.c,...] [-l] [-Ix] [-i foo,bar,...] [-p] a.out [log]" >&2
	exit 1;;
esac

aout=`mprwhich "$1"` || exit 1
shift

status=1
set -e
trap 'set +e; rm -f $nmfile $logfile $flfile; trap 0; exit $status' 0 1 2 13 15

MPRFILTER=`mprfilter mpr "$@"`
MPRSO=`mprso "$aout"`; eval "$MPRSO"

case "$#" in
0)	case "$fopt$mprso" in
	?*)	gzip -1 >$logfile
		MPRFILTER="gzip -dc $logfile";;
	esac;;
esac

case "$mprso" in
"")	mprnm $popt "$aout" >$nmfile
	case "$fopt" in
	?*)	$MPRFILTER | mprfl ${fofiles:+-F"$fofiles"} "$aout" >$flfile;;
	esac;;
*)	$MPRFILTER | mprfl-so $popt ${fofiles:+-F"$fofiles"} -n $nmfile -f $flfile "$aout";;
esac

MAPOPT="-v nmfile=$nmfile"
case "$mprso" in
?*)	MAPOPT=$MAPOPT" -v nmdirect=true";;
esac
case "$fopt" in
?*)	MAPOPT=$MAPOPT" -v flfile=$flfile";;
esac

$MPRFILTER |

$MPRAWK -v aout="$aout" $MAPOPT '
	BEGIN {
		FS="	"	# tab
		i=1
		while (getline <nmfile) {
			if (nmdirect != "") {
				memoize[$1]=$2
				i++
			}
			else {
				addr[i] = $1
				fn[i++] = $2
			}
		}
		addr[i] = addr[i-1]	# sentinel
		naddr = i-1
		if (flfile)
			while (getline <flfile)
				fl[$1] = "("$2","$3")"
		FS=OFS=":"
	}
	function bt(n, i) {
		printf "%s", $1
		if (flfile)
			for (i=2; i<n; i++)
				printf ":%s%s", mapfn($i), mapfl($i)
		else
			for (i=2; i<n; i++)
				printf ":%s", mapfn($i)
		for (i=n; i<=NF; i++)
			printf ":%s", $i
		printf "\n"
	}
	function mapfn(pc, l,u,m) {
		if (pc in memoize)
			return memoize[pc]
		if (nmdirect == "") {
			l=1; u=naddr
			while (l <= u) {	# binary search
				m = int((l+u)/2)
				if (addr[m] > pc)
					u=m-1
				else if (addr[m+1] <= pc)
					l=m+1
				else
					return memoize[pc] = fn[m]
			}
		}
		printf "mprmap: warning: cannot map pc to name (pc=%d; NR=%d)\n", pc, NR |"cat 1>&2"
		return memoize[pc] = pc
	}
	function mapfl(pc) {
		if (pc in fl)
			return fl[pc]
		else
			return "()"
	}
	/^m/ { bt(NF-1) }
	/^f/ { bt(NF) }
	/^r/ { bt(NF-2) }' |

$MPRAWK -v iopt="$iopt" -v Iopt="$Iopt" -v fopt=$fopt -v lopt=$lopt '
	BEGIN {
		# escape special regex chars /^[]|()*+ (for C++)
		gsub("\\/", "\\/", iopt)
		gsub("\\^", "\\^", iopt)
		gsub("\\[", "\\[", iopt)
		gsub("\\]", "\\]", iopt)
		gsub("\\|", "\\|", iopt)
		gsub("\\(", "\\(", iopt)
		gsub("\\)", "\\)", iopt)
		gsub("\\*", "\\*", iopt)
		gsub("\\+", "\\+", iopt)
		split(iopt, iarr, Iopt)
		if (fopt != "" && lopt == "")
			delline=1
	}
	{
		sub(/^m.*:malloc(\([^:]*\))?:/, "m:")
		sub(/^m.*:realloc(\([^:]*\))?:/, "m:")
		sub(/^r.*:realloc(\([^:]*\))?:/, "r:")
		sub(/^f.*:free(\([^:]*\))?:/, "f:")
		sub(/^f.*:realloc(\([^:]*\))?:/, "f:")
		for (i in iarr) {
			gsub(":"iarr[i]":", ":")
			gsub(":"iarr[i]"\([^:]*\):", ":")
		}
		if (delline)
			gsub(/,[0-9]+\)/, ")")
		print
	}'

status=0
