#!/bin/zsh
#
# NAME:
#	reporter
#
# SYNOPSIS:
#	reporter [all | aliases | bindings | completion | functions |
#			limits | options | variables | zstyles]
#
# DESCRIPTION:
#	"reporter" prints your current environment variables, shell
#	variables, limits, completion settings, and option settings to
#	stdout in the form of a script.
#
#	If you run into a zsh bug, someone can source the output script to
#	recreate most of the environment under which you were working.
#
#	IMPORTANT:	"source" this script, don't try to run it directly.
#			Otherwise it won't report the settings for your
#			current shell session.
#
# OPTIONS:
#	All command-line options can be abbreviated.
#
#	"aliases"	prints only aliases.
#	"bindings"	prints only "bindkey" commands.
#	"completion"	prints only "compctl" commands.
#	"functions"	prints "autoload" commands or actual functions.
#	"limits"	prints "limit" commands for things like cputime, etc.
#	"modules"	prints "zmodload" commands.
#	"options"	prints "setopt" commands.
#	"variables"	prints both shell and environment variables.
#	"zstyles"	prints "zstyle" commands
#
#	"all"		tries to find every useful setting under your shell.
#			This is the default, and it's the same as typing all
#			of the above options on the command line.
#
# CAVEATS:
#	Assumes that you have the following programs in your search path:
#		awk, cut, echo, grep, sed, sort
#	Assumes that your C preprocessor lives in /lib/cpp or /usr/ccs/lib/cpp.
#	Uses (and unsets) variables beginning with "reporter_".
#	Won't work for versions of zsh that are older than 3.1.3 or so.
#
# RESTRICTIONS:
#	DON'T:	pretend you wrote it, sell it, or blame me if it breaks.
#	DO:	as ye will an' ye harm none.
#					--Wiccan saying, I think
#
# BUGS:
#	I'm sure there are more than a few.  To be safe, run "zsh -f" before
#	sourcing the output from this script.  If you have "screen", you may
#	want to use that, too; I hammered my terminal settings beyond repair
#	when using an early version, and "screen" saved me from having to
#	login on another terminal.
#
# HISTORY:
#	The name was ripped off from the Emacs "reporter.el" function.
#	The idea came from a mail message to the ZSH mailing list:
#
# Begin Configuration Section
#

reporter_OSVersion="`uname -s`_`uname -r`"

#
# Solaris 2.x
#
case ${reporter_OSVersion} in
	SunOS_5.*)
		CPP=${CPP:-/usr/ccs/lib/cpp}
		AWK=${AWK:-nawk}	# GNU AWK doesn't come standard :-(
		;;
esac

#
# Default Values 
#

CPP=${CPP:-/lib/cpp}
AWK=${AWK:-awk}

#
# End Configuration Section
#

reporter_do_all=yes

for reporter_each
do
	case "$reporter_each"
	in
		ali*)	reporter_do_aliases=yes; reporter_do_all=no ;;
		b*)	reporter_do_bindings=yes; reporter_do_all=no ;;
		c*)	reporter_do_compctl=yes; reporter_do_all=no ;;
		f*)	reporter_do_fun=yes; reporter_do_all=no ;;
		l*)	reporter_do_lim=yes; reporter_do_all=no ;;
		m*)	reporter_do_mod=yes; reporter_do_all=no ;;
		o*)	reporter_do_setopt=yes; reporter_do_all=no ;;
		v*)	reporter_do_vars=yes; reporter_do_all=no ;;
		zs*|s*)	reporter_do_zstyle=yes; reporter_do_all=no ;;
		*)	;;
	esac
done

#
#	The "cshjunkiequotes" option can break some of the commands
#	used in the remainder of this script, so we check for that first
#	and disable it.  Similarly "shwordsplit" and "kshoptionprint".
#	We'll re-enable them later.
#

reporter_junkiequotes="no"
reporter_shwordsplit="no"
reporter_kshoptprint="no"
reporter_nounset="no"

if [[ -o cshjunkiequotes ]]
then
	reporter_junkiequotes="yes"
	unsetopt cshjunkiequotes
fi
if [[ -o shwordsplit ]]
then
	reporter_shwordsplit="yes"
	unsetopt shwordsplit
fi
if [[ -o kshoptionprint ]]
then
	reporter_kshoptprint="yes"
	unsetopt kshoptionprint
fi
if [[ -o nounset ]]
then
	reporter_nounset="yes"
	unsetopt nounset
fi

#
#	UNAME
#
#	This shows your system name.  It's extremely system-dependent, so
#	we need a way to find out what system you're on.  The easiest
#	way to do this is by using "uname", but not everyone has that,
#	so first we go through the search path.
#
#	If we don't find it, then the only thing I can think of is to
#	check what's defined in your C compiler, and code in some exceptions
#	for the location of "uname" or an equivalent.  For example, Pyramid
#	has "uname" only in the ATT universe.  This code assumes that
#	the "-a" switch is valid for "uname".
#
#	This section of code sees what is defined by "cpp".  It was
#	originally written by brandy@tramp.Colorado.EDU (Carl Brandauer).
#	Additional error checking and sed hacking added by Ken Phelps.
#

reporter_cppdef=(`strings -3 ${CPP} |
	sed -n '
	/^[a-zA-Z_][a-zA-Z0-9_]*$/{
	s/.*/#ifdef &/p
	s/.* \(.*\)/"\1";/p
	s/.*/#endif/p
	}
	' | ${CPP} |sed '
	/^[	 ]*$/d
	/^#/d
	s/.*"\(.*\)".*/\1/'`)

reporter_uname=""

for reporter_each in `echo $PATH | sed -e 's/:/ /g'`
do
	if [[ -x $reporter_each/uname ]]
	then
		reporter_uname="$reporter_each/uname"
		break
	fi
done

case "$reporter_uname"
in
	"")	reporter_uname="echo not found on this system" ;;
	*)	;;
esac

for reporter_each in $reporter_cppdef
do
	case "$reporter_each"
	in
		pyr)	reporter_uname="/bin/att uname" ;;
		*)	;;
	esac
done

echo '# START zsh saveset'
echo '# uname: ' `eval $reporter_uname -a`
echo

unset reporter_cppdef
unset reporter_uname
unset reporter_each

#
#	ALIASES
#
#	Use "alias -L" to get a listing of the aliases in the form we want.
#

if [[ "$reporter_do_all" = "yes" || "$reporter_do_aliases" = "yes" ]]
then
	echo '# Aliases.'
	echo

	alias -L
fi

#
#	KEY BINDINGS
#
#	The -L option does most of the work.  The subshell is used to
#	avoid modifying things that will be recorded later.
#

if [[ "$reporter_do_all" = "yes" || "$reporter_do_bindings" = "yes" ]]
then
	echo
	echo "# Key bindings."
	echo
	bindkey -lL | grep -v ' \.safe$'
	(
		alias bindkey=bindkey
		bindkey () {
			[[ "$1" == "-N" ]] || return
			[[ "$2" == "--" ]] && shift
			[[ "$2" == ".safe" ]] && return
			echo
			builtin bindkey -L -M -- "$2"
		}
		eval "`builtin bindkey -lL`"
	)
fi

#
#	COMPLETION COMMANDS
#	Warning:  this won't work for zsh-2.5.03.
#

if [[ "$reporter_do_all" = "yes" || "$reporter_do_compctl" = "yes" ]]
then
	echo
	echo "# Completions."
	echo

	compctl -L
fi

#
#	FUNCTIONS
#

if [[ "$reporter_do_all" = "yes" || "$reporter_do_fun" = "yes" ]]
then
	echo 
	echo "# Undefined functions."
	echo

	autoload + | ${AWK} '{print "autoload " $1}'

	echo 
	echo "# Defined functions."
	echo

	(
		unfunction `autoload +` 2>/dev/null
		functions
	)
fi

#
#	LIMITS
#
#	"cputime" has to be handled specially, because you can specify
#	the time as just hours, or "minutes:seconds".
#

if [[ "$reporter_do_all" = "yes" || "$reporter_do_lim" = "yes" ]]
then
	echo
	echo '# Limits.'
	echo

	(
		set X `limit | grep "cputime" | grep -v "unlimited" |
			sed -e 's/:/ /g'`

		if test "$#" -gt 1
		then
			hr=$3
			min=$4
			sec=$5

			if test "$hr" -gt 0
			then
				echo "limit cputime ${hr}h"
			else
				echo "limit cputime $min:$sec"
			fi
		fi
	)

	limit | grep -v "cputime" | grep -v "unlimited" |
		sed -e 's/Mb/m/' -e 's/Kb/k/' |
		${AWK} 'NF > 1 {print "limit " $0}'
fi

#
#	MODULE LOADING COMMANDS
#

if [[ "$reporter_do_all" = "yes" || "$reporter_do_mod" = "yes" ]]
then
	echo
	if ( zmodload ) >& /dev/null; then
		echo "# Modules."
		echo
		zmodload -d -L
		echo
		zmodload -ab -L
		echo
		zmodload -ac -L
		echo
		zmodload -ap -L
		echo
		zmodload -L
	else
		echo "# Modules: zmodload not available."
	fi
fi

#
#	NON-ARRAY VARIABLES
#
#	We run this in a subshell to preserve the parameter module state
#	in the current shell.  Also, reset the prompt to show you're now
#	in a test shell.
#

if [[ "$reporter_do_all" = "yes" || "$reporter_do_vars" = "yes" ]]
then
	echo
	echo "# Non-array variables."
	echo

	(
		zmodload -u `zmodload | grep parameter` 2>/dev/null

		echo "ARGC=0"
		eval `typeset + |
			grep -v 'array ' |
			grep -v 'association ' |
			grep -v 'undefined ' |
			grep -v ' ARGC$' |
			grep -v '^reporter_' |
			grep -wv '[!#$*0?@_-]$' |
			sed -e 's/.* \(.*\)/print -r -- \1=${(qq)\1};/' \
			    -e 's/^\([^ ]*\)$/print -r -- \1=${(qq)\1};/'`
		echo "prompt='test%'"
	)

#
#	ARRAY VARIABLES
#
#	Run this in a subshell to preserve the parameter module state in
#	the current shell.
#

	echo
	echo "# Array variables."
	echo

	(
		zmodload -u `zmodload | grep parameter` 2>/dev/null

		echo "argv=()"
		eval `{ typeset + | grep 'array ' ;
				typeset + | grep 'association ' } |
			grep -v 'undefined ' |
			grep -v ' argv$' |
			grep -v ' reporter_' |
			grep -v ' [!#$*0?@_-]$' |
			sed 's/.* \(.*\)/print -r -- \1=\\\(${(qq)\1}\\\);/'`
	)

#
#	EXPORTED VARIABLES
#
#	Run this in a subshell to preserve the parameter module state in
#	the current shell.
#

	echo
	echo "# Exported variables."
	echo

	(
		zmodload -u `zmodload | grep parameter` 2>/dev/null

		export | grep -v "^'*"'[!#$*0?@_-]'"'*=" |
			${AWK} -F'=' '{print "export " $1}'
	)
fi

#
#	SETOPT
#
#	We exclude interactive because "setopt interactive" has no effect.
#	A few special options are dealt with separately; see the comments
#	near the start of the script.
#

if [[ "$reporter_do_all" = "yes" || "$reporter_do_setopt" = "yes" ]]
then
	echo
	echo '# Setopt.'
	echo

	(
		setopt | grep -v 'interactive' | ${AWK} '{print "setopt " $0}'

		case "$reporter_junkiequotes"
		in
			yes)	echo "setopt cshjunkiequotes" ;;
			*)	;;
		esac
		case "$reporter_shwordsplit"
		in
			yes)	echo "setopt shwordsplit" ;;
			*)	;;
		esac
		case "$reporter_kshoptprint"
		in
			yes)	echo "setopt kshoptionprint" ;;
			*)	;;
		esac
		case "$reporter_nounset"
		in
			yes)	echo "setopt nounset" ;;
			*)	;;
		esac
	) | sort
fi

#
#	STYLES
#

if [[ "$reporter_do_all" = "yes" || "$reporter_do_zstyle" = "yes" ]]
then
	echo
	echo '# Styles.'
	echo

	zstyle -L
fi

echo
echo '# END zsh saveset'

#
#	Don't put an exit here, or you'll get a nasty surprise when you
#	source this thing.  Get rid of variables created when processing
#	command line.
#

unset reporter_do_all
unset reporter_do_aliases
unset reporter_do_bindings
unset reporter_do_compctl
unset reporter_do_fun
unset reporter_do_lim
unset reporter_do_setopt
unset reporter_do_vars

#
#	Turn various options back on if necessary, in case run via ".".
#

case "$reporter_junkiequotes"
in
	yes)	setopt cshjunkiequotes ;;
	*)	;;
esac
case "$reporter_shwordsplit"
in
	yes)	setopt shwordsplit ;;
	*)	;;
esac
case "$reporter_kshoptprint"
in
	yes)	setopt kshoptionprint ;;
	*)	;;
esac
case "$reporter_nounset"
in
	yes)	setopt nounset ;;
	*)	;;
esac

unset reporter_junkiequotes
unset reporter_shwordsplit
unset reporter_kshoptprint
unset reporter_nounset

unset reporter_OSVersion
