#!/bin/sh -
PATH=$PATH:/sbin:/usr/sbin:/etc:/usr/etc; export PATH
umask 000

PIPE=
test -t 1 && grcat 2>&1 | grep -s -i usage > /dev/null &&
test -f /usr/share/grc/conf.ps && PIPE='grcat /usr/share/grc/conf.ps'

snapshot=/var/tmp/snaps
test -d /var/tmp || snapshot="${TMPDIR:-/tmp}"/snaps

killpid=killpid

# snaps - snapshot ps, convenience front-end for the ps, kill & renice commands
# Steve Kinzler, steve@kinzler.com, Sep 90/Jul 94/Mar 06/Jan 25
# https://kinzler.com/me/home.html#unix

action=snaps; user=; force=; priority=
snaps=; page=; perl=; gopt=; popt=; bad=

while :
do
	case $# in
	0)	break;;
	*)	case "$1" in
		-u)		user=t
				case "$action" in
				snaps)	action=list;;
				esac;;
		-l)		action=list;;
		-c)		action=count;;
		-k)		action=kill;;
		-[A-Z][A-Z]*)	action="kill $1";;
		-r)		shift; action="renice -n $1"; priority="$1";;
		-f)		force=t;;
		-s)		snaps=t;;
		-P)		page=P;;
		-E)		page=E;;
		-g)		perl=;;
		-p)		perl=t;;
		-i)		gopt="$1"; popt=i;;

		--)		shift; break;;
		-h)		bad=t; break;;
		-*)		bad=t; echo "$0: unknown option ($1)" 1>&2;;
		*)		break;;
		esac
		shift;;
	esac
done

case "$bad" in
?*)	cat << EOF 1>&2
usage: $0 [ -u ]
       [ -l | -c | [ -k ] [ -SIGNAL ] | -r priority ] [ -f ]
       [ -s ] [ -P | -E ] [ -g | -p ] [ -i ] [ regexp ... ]
Without arguments, just snapshots the system ps to the snapshot file,
$snapshot.
	-u	restricts consideration to user's current processes
	-l	lists processes (default with -u or regexps)
	-c	lists an ordered count of process names
	-k	kills processes
	-SIGNAL	use the given signal when killing processes (implies -k)
	-r	renices processes with the given priority
	-f	force kills or renices without confirmation
	-s	snapshots system ps even if the snapshot file already exists
	-P	runs the user's \$PAGER or 'more' to view any listing
	-E	runs the user's \$VISUAL, \$EDITOR or 'vi' to edit any listing
	-g	use grep regular expressions (default)
	-p	use perl regular expressions
	-i	use case-insensitive matching with the regexps
Consideration is restricted to processes with ps lines matching the given
regexps if any are specified, otherwise all processes are considered.
For system process consideration, the snapshot file is used if it already
exists, otherwise it is automatically created first.  For user process
consideration, ps is always run anew.  If kill or renice input is from a
tty and no -f option is given, confirmation is required before the kills
or renices.  If the special signal -DIE is given, the command '$killpid'
is used to kill the processes.
EOF
	exit 1;;
esac

case $# in
0)	;;
*)	case $action in
	snaps)	action=list;;
	esac;;
esac

tmp="${TMPDIR:-/tmp}/snaps$$"
tmpA="${TMPDIR:-/tmp}/snapsA$$"
tmpB="${TMPDIR:-/tmp}/snapsB$$"
trap "rm -f '$tmp' '$tmpA' '$tmpB'; exit" 0 1 2 13 15

# We have to try SysV ps first since some systems, eg aix3 and hpux,
# will silently do something, but not what we want, with BSD ps arguments.

# This gets the desired behavior with procps-2.0, eg.
PS_PERSONALITY=bsd; export PS_PERSONALITY

case "`(uname -s) 2> /dev/null`" in
FreeBSD)	bsd=t;;
*)		bsd=;;
esac

case "$action" in
snaps)	if ps -ef > "$tmpA" 2> /dev/null
	then
		case `grep ' \\\\_ ' "$tmpA"` in
		?*)				      bsd=t;;
		*)	test `wc -l < "$tmpA"` -le 5 && bsd=t;;
		esac
	else
		bsd=t
	fi

	case "$bsd" in
	?*)	ps aguwwx > "$tmpA" 2> /dev/null || ps auwwx > "$tmpA";;
	esac

	trap '' 1 2 13 15
	cat "$tmpA" > "$snapshot"

	exit;;
esac

case "$user" in
?*)	if ps -f > "$tmpA" 2> /dev/null
	then
		case `grep ' \\\\_ ' "$tmpA"` in
		?*)				      bsd=t;;
		*)	test `wc -l < "$tmpA"` -le 5 && bsd=t;;
		esac
	else
		bsd=t
	fi

	case "$bsd" in
	?*)	ps guwwx > "$tmpA" 2> /dev/null || ps uwwx > "$tmpA";;
	esac

	snapshot="$tmpA";;

*)	case "$snaps" in
	?*)	$0;;
	*)	test -s "$snapshot" || $0;;
	esac;;
esac

case $# in
0)	sed 1d < "$snapshot" > "$tmpB" || exit 2;;

*)	case "$perl" in
	?*)	for regexp
		do
			perl -ne "m$regexpo$popt && ! m$0o && $. > 1 &&
				  print" < "$snapshot" || exit 2
		done > "$tmpB" || exit 2;;

	*)	for regexp
		do
			sed 1d < "$snapshot" | grep $gopt "$regexp"
		done | grep -v "$0" > "$tmpB" || exit 2;;
	esac;;
esac

case "$page" in
[EeVv]*)	PIPE=; page=${VISUAL-${EDITOR-vi}};;
?*)		PIPE=; page=${PAGER-more};;
*)		       page=cat;;
esac

case "$action" in
list)	sed 1q < "$snapshot" > "$tmp" &&
	cat < "$tmpB"	    >> "$tmp" &&
	case "$PIPE" in
	?*)	$page "$tmp" | $PIPE;;
	*)	$page "$tmp";;
	esac;;

count)	dots=`sed 's/CMD$//; s/COMD$//; s/COMMAND$//; s/././g; 1q' "$snapshot"`
	case "$dots" in
	'')	echo "$0: cannot determine COMMAND column of ps output" 1>&2
		exit 3;;
	esac

	sed "s/^$dots//
	     s/.*(\(.*\)).*/\1/
	     s/^[ 	]*//
	     s/[ 	].*//
	     s/^-//
	     s,.*/,," < "$tmpB" | sort | uniq -c | sort -nr > "$tmp" || exit 2
	case "$PIPE" in
	?*)	$page "$tmp" | $PIPE;;
	*)	$page "$tmp";;
	esac;;

kill*|renice*)
	case "$action" in
	kill*-DIE)	action="$killpid";;
	renice*)	case "`renice 2>&1`" in
			*renice?priority*)	action="renice $priority";;
			esac
	esac

	xopt=
	if test -z "$force" -a -t 0
	then
		hdr=`sed 1q < "$snapshot"` || exit 2
		echo "$hdr"    > "$tmp"    || exit 2
		cat < "$tmpB" >> "$tmp"    || exit 2
		case "$PIPE" in
		?*)	$page "$tmp" | $PIPE;;
		*)	$page "$tmp";;
		esac
		hdr=`echo "$hdr" | sed 's/[^a-zA-Z0-9_ ]/\\\\&/g'`
		grep -v "^$hdr$" "$tmp" > "$tmpB" || exit 2

		echo "$action all these? [n] " | tr -d '\012'
		read answer

		case "$answer" in
		[yY]*)	xopt=-p;;
		*)	exit;;
		esac
	fi

	set x `sed 1q "$snapshot" | sed -n 's/^PID .*//p; s/ PID .*//p' | wc`
	shift

	case "$1" in
	1)	;;
	*)	echo "$0: cannot determine PID column of ps output" 1>&2
		exit 3;;
	esac

	awk "{ print \$($2 + 1) }" < "$tmpB" | xargs $xopt $action;;
esac
