#!/depot/path/expect --
# allow another user to share a shell (or other program) with you
# See kibitz(1) man page for complete info.
# Author: Don Libes, NIST
# Date: December 5, 1991
# Version: 1.4

# if environment variable "EXPECT_PROMPT" exists, it is taken as a regular
# expression which matches the end of your login prompt (but does not other-
# wise occur while logging in).

set prompt "(%|$|#) "		;# default prompt

trap exit SIGINT

if [llength $argv]<2 {
	send_user "usage: kibitz user \[program ...\]\n"
	send_user "   or: kibitz user@host \[program ...\]\n"
	exit
}

# 2nd user invokes it as "kibitz -####" but there is no
# reason to document this, as user is told this on the fly.
# known internally as user_number==2

# 1st user on remote machine invokes it as "kibitz -r user"
# and is  known internally as user_number==3 while
# locally known internally as user_number==1

log_user 0
set timeout -1

set user [lindex $argv 1]
if [string match -r $user] {
	send_user "KSYNC1"	;# this tells user_number 1 to prepare for
				;# possible error msgs
	set user_number 3
	# need to check that it exists first!
	set user [lindex $argv 2]
} else {
	set user_number [expr 1+(0==[string first - $user])]
}

# at this point, user_number and user are correctly determined

#exec rm -f $user_number
#debug -f $user_number 0

set user2_islocal 1	;# assume local at first

# later move inside following if $user_number == 1
# return true if x is a prefix of xjunk, given that prefixes are only
# valid at . delimiters
# if !do_if0, skip the whole thing - this is here just to make caller simpler
proc is_prefix {do_if0 x xjunk} {
	if 0!=$do_if0 {return 0}
	set split [split $xjunk .]
	for {set i [expr [llength $split]-1]} {$i>=0} {incr i -1} {
		if [string match $x [join [lrange $split 0 $i] .]] {return 1}
	}
	return 0
}

# get domainname.  Unfortunately, on some systems, domainname(1)
# returns NIS domainname which is not the internet domainname.
proc domainname {} {
	# open pops stack upon failure
	set file [open /etc/resolv.conf r]
	while {-1!=[gets $file buf]} {
		if 1==[scan $buf "domain %s" name] {return $name}
	}
	error "no domain declaration in /etc/resolv.conf" 
}

if $user_number==1 {
	if [llength $argv]>2 {
		set pid [eval [concat spawn [lrange $argv 2 end]]]
	} else {
		set pid [spawn $env(SHELL)]
	}

	set shell $spawn_id

	# is user2 remote?
	regexp (\[^@\]*)@*(.*) $user ignore tmp host
	set user $tmp
	if ![string match $host ""] {
		set h_rc [catch {exec hostname}	hostname]
		set d_rc [catch domainname 	domainname]

		if {![is_prefix $h_rc $host $hostname]
		 && ![is_prefix $d_rc $host $hostname.$domainname]} {
			set user2_islocal 0
		}
	}

	if !$user2_islocal {
		send_user "connecting to $host\n"
		spawn rlogin $host
		set userin $spawn_id
		set userout $spawn_id

		if [info exists env(EXPECT_PROMPT)] {
			set prompt $env(EXPECT_PROMPT)
		}

		set timeout 120
		expect {
			assword: {
				system stty -echo
				send_user "password (for $user) on $host: "
				expect_user -re "(.*)\n"
				send_user "\n"
				send "$expect_out(1,string)\r"
				# bother resetting echo?
				continue -expect
			} incorrect* {
				send_user "invalid password or account\n"
				exit
			} timeout {
				send_user "connection to $host timed out\n"
				exit
			} eof {
				send_user "connection to host failed: $expect_out(buffer)"
				exit
			} -re $prompt
		}
		send_user "starting kibitz on $host\n"
		# the kill protects user1 from receiving user3's
		# prompt if user2 exits via expect's exit.
		send "kibitz -r $user;kill -9 $$\r"

		# should also check for eof?
		expect {
			-re "kibitz -r $user.*KSYNC1" {}
			-re "kibitz -r $user.*(kibitz\[^\r\]*)" {
				send_user "unable to start kibitz on $host: \"$expect_out(1,string)\"\n"
				send_user "try rlogin by hand followed by \"kibitz $user\"\n"
				exit
			}
			timeout {
				send_user "unable to start kibitz on $host: "
				set expect_out(buffer) "timed out"
				set timeout 0; expect -re .+
				send_user $expect_out(buffer)
				exit
			}
		}
		expect {
			-re ".*\n" {
				# pass back diagnostics
				# should really strip out extra cr
				send_user $expect_out(buffer)
				continue -expect
			}
			KABORT exit
			default exit
			KSYNC2
		}
	}
} else {
	if $user_number==2 {
		set pid [string trimleft $user -]
	} else {
		# user_number==3
		# incredibly bogus, but I can't offhand think of any other way
		# to generate an id that will stay unique
		set pid [spawn /bin/cat]
	}
}

set local_io [expr ($user_number==3)||$user2_islocal]
if $local_io||($user_number==2) {
	set userinfile /tmp/exp0.$pid
	set useroutfile /tmp/exp1.$pid
}

if $user_number!=3 {
    expect_before -i $user_spawn_id \C] {
	send_user "\nto exit kibitz, enter: exit\n"
	send_user "to suspend kibitz, press appropriate job control sequence\n"
	send_user "to return to kibitzing, enter: return\n"
	interpreter
	send_user "returning to kibitz\n"
	continue -expect
    }

    proc prompt1 {} {
	return "kibitz[info level].[history nextid]> "
    }
}

set timeout -1

# kibitzer executes following code
if $user_number==2 {
	# for readability, swap variables
	set tmp $userinfile
	set userinfile $useroutfile
	set useroutfile $tmp

	if ![file readable $userinfile] {
		send_user "Eh?  No one is asking you to kibitz.\n"
		exit -1
	}
	spawn cat $userinfile

	set userin $spawn_id

	set userout [open $useroutfile w]
	# open will hang until other user's cat starts

	system stty -echo raw
	send_user "Escape sequence is ^\]\n"

	expect {
		-i $user_spawn_id -re .+ {
			puts $userout $expect_out(buffer) nonewline
			flush $userout
			continue -expect
		}
		-i $userin -re .+ {
			send_user $expect_out(buffer)
			continue -expect
		}
	}
	exit
}

# only user_numbers 1 and 3 execute remaining code

proc abort {} {
	global user_number

	# KABORT tells user_number 1 that user_number 3 has run into problems
	# and is exiting, and diagnostics have been returned already
	if $user_number==3 {send_user KABORT}
	exit
}

if $local_io {
    proc mknod {f} {
	if 0==[catch {exec mknod $f p}] return
	# some systems put mknod in wierd places
	if 0==[catch {exec /usr/etc/mknod $f p}] return	;# Sun
	if 0==[catch {exec /etc/mknod $f p}] return	;# AIX, Cray
	send_user "Couldn't figure out how to make a fifo - where is mknod?\n"
	abort
    }

    trap {exec rm -f $userinfile $useroutfile; exit} SIGINT

    # create 2 fifos to communicate with other user
    mknod $userinfile
    mknod $useroutfile
    # make sure other user can access despite umask
    exec chmod 666 $userinfile $useroutfile

    send_user "writing to $user to request kibitzing\n"

    # can't use exec since write insists on being run from a tty!
    set rc [catch {
	   system echo "Can we talk?  Run: \"kibitz -$pid\"" | /bin/write $user
		}
	]
    if $rc abort

    set userout [open $useroutfile w]
    # open will hang until other user's cat starts

    spawn cat $userinfile
    set userin $spawn_id
}

system stty -echo raw

if $user_number==3 {
	send_user "KSYNC2"	;# this tells user_number 1 to send data

	expect {
		-i $user_spawn_id -re .+ {
			puts $userout $expect_out(buffer) nonewline
			flush $userout
			continue -expect
		}
		-i $userin -re .+ {
			send_user $expect_out(buffer)
			continue -expect
		} eof {
			wait -i $userin
		}
	}
} else {
	send_user "Escape sequence is ^\]\n"

	expect {
		-i $user_spawn_id -re .+ {
			send -i $shell $expect_out(buffer)
			continue -expect
		}

		-i $userin -re .+ {
			send -i $shell $expect_out(buffer)
			continue -expect
		} eof {
			wait -i $userin
			close -i $shell
		}

		-i $shell -re .+ {
			send_user $expect_out(buffer)
			if $local_io {
				puts $userout $expect_out(buffer) nonewline 
				flush $userout
			} else {
				send -i $userout $expect_out(buffer)
			}
			continue -expect
		}
	}

	wait -i $shell
}

if $local_io {exec rm $userinfile $useroutfile}

