#!/bin/sh -

# xbuf - print/save/rotate the contents of X11 cut and selection buffers
# Steve Kinzler, steve@kinzler.com, Oct 98/Oct 04/Aug 08/Dec 15/Oct 20/Apr 25
# https://kinzler.com/me/home.html#x11

# see also xclip(1)

z=; op=; buf=

case "$1" in
-z)		z="$1";  shift;;
esac
case "$1" in
-[oOiIaAwWdusf@1-9]|-io)
		op="$1"; shift;;
--)		shift;;
-*)		op=bad;;
esac

case "$op" in
'')		op=-o; test ! -t 0 -a -t 1 && op=-i
		case "$op,$#" in
		-o,0)	op=-O; set b 0 p;;
		esac;;
esac
case "$op,$#" in
-f,0)		op=bad;;
-f,*)		;;
-[AWdu@1-9],0)	set 8;;
-s,0)		set 0 1 8;;
-s,2)		set x "$@" 8; shift;;
*,0)		set 0;;
esac
buf="$1"

case "$op,$#" in
-s,3)		;;
-s,1)		op=bad;;
-[oOiIawf],*|-io,*|*,1)	;;
*)		op=bad;;
esac
for buf
do
	case "$op" in
	-f)	test -f "$buf" || op=bad;;
	*)	case "$op,$buf" in
		-[AWdu@1-9],[0-9]*)	;;
		-[AWdu@1-9],*)		op=bad;;
		*,[0-9]*|*,[psb])	;;
		*)			op=bad;;
		esac;;
	esac
done

case "$op" in
bad)	cat <<EOF 1>&2; exit 1;;
xbuf [ -z ] [ -o | -O | -i | -I | -a | -w | -io ] [ # | p | s | b ] ...
xbuf [ -z ] [ -A | -W | -d | -u ] #
xbuf        -s [ buf1 buf2 [ viabuf ] ]
xbuf [ -z ] -f file ...
xbuf        [ -@ | -[1-9] ] #
	-z	do not input to a buffer if the input is empty
	-o	output the given buffers (default)
	-O	output the first non-empty of the given buffers
	-i	input stdin to the given buffers (default if piped to)
	-I	input stdin to the first non-empty of the given buffers if any
	-a	input stdin lines one to each given buffer
	-A	input stdin lines one to each buffer 0 thru # (max, default 8)
	-w	input stdin words one to each given buffer
	-W	input stdin words one to each buffer 0 thru # (max, default 8)
	-io	pass through stdin to stdout while saving to given buffers
	-d	rotate the first # (default 8) buffers down/plus one position
	-u	rotate the first # (default 8) buffers up/minus one position
	-s	swap buffers (dflt 0 and 1) using the given buffer (default 8)
	-f	input any given files to buffers 0 thru the number of files
	-@	label and output all buffers for viewing (default first 8)
	-[1-9]	as -@ but only output the first [1-9] lines of the buffers
The default buffer is 0.  If no operation option is given and no buffers are
specified and the command is not piped to, the default will be to output the
first non-empty of the clipboard selection, 0 and primary selection buffers.
Numbered buffers are cut buffers; p indicates the primary selection buffer;
s the secondary selection buffer; b the clipboard selection buffer.  Final
newlines are not saved.  Flag ordering is significant.  -A, -W, -f, -@ and
-[1-9] will shift \$XBUF_BASE buffers if defined.
EOF
esac

case "$op" in
-o)	for buf
	do
		case "$buf" in
		[0-9]*)	(xcb -V) > /dev/null 2>&1 && { xcb -p "$buf"
						       continue; }
			xprop -root -notype "CUT_BUFFER$buf" |
			perl -0e 's/\n//g;
				  s/^[^=]*=\s*"?//; s/"$//;
					s/\\\\/\001/g;
					s/\\t/\t/g;
					s/\\n/\n/g;
					s/\\"/"/g;
					s/\001/\\/g;		print';;
		*)	# perl KLUDGE to try to fix multi-byte high chars
			#   in xsel output
			xsel -"$buf" -l /dev/null |
			  perl -pe 's/\303([\200-\277])/chr(ord($1) + 0100)/ge;
				    s/\302([\200-\277])/$1/g';;
		esac
	done;;

-O)	for buf
	do
		case `"$0" -o "$buf"` in
		'')	continue;;
		*)	exec "$0" -o "$buf";;
		esac
	done;;

-i)	in="`perl -0pe chomp`"
	case "$z,$in" in
	-z,)	exit;;
	esac
	for buf
	do
#		/bin/echo -n "$in" |	# breaks for large $in
		printf '%s' "$in" |
		case "$buf" in
		[0-9]*)	xcb -s "$buf";;
		*)	xsel -i -"$buf" -l /dev/null;;
		esac
	done;;

-I)	for buf
	do
		case `"$0" -o "$buf"` in
		'')	exec "$0" -i "$buf";;
		*)	continue;;
		esac
	done;;

-a)	while read line
	do
		case "$#" in
		0)	exit;;
		*)	test -n "$z" -a -z "$line" ||
				echo "$line" | tr -d '\012' | "$0" -i "$1"
			shift;;
		esac
	done;;

-A)	case "$XBUF_BASE" in
	[0-9]|[1-9][0-9])
		n=$XBUF_BASE; buf=`expr "$buf" + $XBUF_BASE`;;
	*)	n=0;;
	esac
	while read line
	do
		test "$n" -gt "$buf" && break
		test -n "$z" -a -z "$line" ||
			echo "$line" | tr -d '\012' | xcb -s "$n"
		n=`expr "$n" + 1`
	done;;

-w)	perl -pe 's/^\s+//;   s/\s+$//;
		  s/\s+/\n/g; s/.$/$&\n/' | "$0" $z -a "$@";;

-W)	perl -pe 's/^\s+//;   s/\s+$//;
		  s/\s+/\n/g; s/.$/$&\n/' | "$0" $z -A "$buf";;

-io)	"$0" -i $z "$@"; exec "$0" -o "$buf";;

-d)	exec xcb -n "$buf" -r  1;;
-u)	exec xcb -n "$buf" -r -1;;

-s)	"$0" -o "$1" | "$0" -i "$3"
	"$0" -o "$2" | "$0" -i "$1"
	"$0" -o "$3" | "$0" -i "$2"
	"$0" -i "$3" < /dev/null;;

-f)	case "$XBUF_BASE" in
	[0-9]|[1-9][0-9])	n=$XBUF_BASE;;
	*)			n=0;;
	esac
	for file
	do
		test -n "$z" -a ! -s "$file" || xcb -s "$n" < "$file"
		n=`expr "$n" + 1`
	done;;

-[@1-9])case "$XBUF_BASE" in
	[0-9]|[1-9][0-9])
		n=$XBUF_BASE; buf=`expr "$buf" + $XBUF_BASE`;;
	*)	n=0;;
	esac
	for buf in p s b `perl -e "\$, = ' '; print $n .. $buf-1"`
	do
		echo "======== $buf ========" | color on_blue
		case "$op" in
		-@)	"$0" -o "$buf";		   echo;;
		*)	"$0" -o "$buf" | head $op; echo;;
		esac
	done;;
esac
