if {[info commands exp_version] != {}} {
	set exp_version_4 [regexp {^4} [exp_version]]
} else {
	set exp_version_4 [regexp {^4} [expect_version]]
}

# Backward compatibility until we're using expect 5 everywhere
if {$exp_version_4} {
	global wait_error_index wait_errno_index wait_status_index
	set wait_error_index 0
	set wait_errno_index 1
	set wait_status_index 1
} else {
	set wait_error_index 2
	set wait_errno_index 3
	set wait_status_index 3
}
				
proc set_from_env {varname default_value} {
    global env
    upvar $varname v

    if [info exists env($varname)] {
	set v $env($varname)
    } else {
	set v $default_value
    }
}
proc expect_tcl_prompt {} {
    global kadmin_tcl_spawn_id
    expect {
	-i $kadmin_tcl_spawn_id
	-re "^% $" { }
	-re . { perror "unexpected output {$expect_out(buffer)} from subprocess, expecting tcl prompt" }
	timeout { perror "timeout waiting for tcl prompt" }
	eof { perror "eof from subprocess when expecting tcl prompt" }
    }
}
proc send_tcl_cmd_await_echo {cmd} {
    global kadmin_tcl_spawn_id
    send -i $kadmin_tcl_spawn_id "$cmd\n"
    expect {
	-i $kadmin_tcl_spawn_id
	-ex "$cmd\r\n" { }
	timeout { perror "timeout waiting for tcl subprocess to echo input" }
	eof { perror "eof waiting for tcl subprocess to echo input" }
    }
}
proc expect_kadm_ok {} {
    global kadmin_tcl_spawn_id
    expect {
	-i $kadmin_tcl_spawn_id
	-re "^OK KADM5_OK \[^\n\]*\n" {}
	-re "^ERROR \[^\n\]*\n" { perror "kadmin tcl subprocess reported unexpected error" }
	-re "^marshall_new_creds: \[^\n\]*\n" { exp_continue }
	-re "^gssapi_\[^\n\]*\n" { exp_continue }
	-re "^\r?\n" { exp_continue }
	eof	{ perror "kadmin tcl subprocess died" }
	default { perror "didn't get ok back" }
    }
}
proc setup_database {} {
    global env spawn_id kadmin_tcl_spawn_id TESTDIR CANON_HOST

    # XXXXX
    set_from_env TOP {/x/x/x/x/x}
    send_user "TOP=$TOP\n"

    set_from_env TESTDIR $env(TOP)/testing
    set_from_env CLNTTCL $TESTDIR/util/kadm5_clnt_tcl
    set_from_env TCLUTIL $TESTDIR/tcl/util.t
    set env(TCLUTIL) $TCLUTIL
    set env(PATH) "$TOP/install/admin:$env(PATH)"

    # $VERBOSE ?

    if [info exists spawn_id] { set x $spawn_id }
    spawn $CLNTTCL
    set kadmin_tcl_spawn_id $spawn_id
    if [info exists x] { set spawn_id $x }

    expect_tcl_prompt
    # tcl 8.4 for some reason screws up autodetection of output EOL
    # translation.  Work around it for now.
    send_tcl_cmd_await_echo "if { \[info commands fconfigure\] != \"\" } { fconfigure stdout -translation lf }"
    expect_tcl_prompt
    send_tcl_cmd_await_echo "source {$TCLUTIL}"
    expect_tcl_prompt
    send_tcl_cmd_await_echo "set h {$CANON_HOST}"
    expect {
	-ex "$CANON_HOST\r\n" { }
	timeout { perror "timeout waiting for subprocess" }
	eof { perror "eof from subprocess" }
    }
    expect_tcl_prompt

    send_tcl_cmd_await_echo {kadm5_init admin admin $KADM5_ADMIN_SERVICE null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 server_handle}
    expect_kadm_ok
    expect "^% "
    send_tcl_cmd_await_echo {kadm5_create_principal $server_handle [simple_principal server/$h] {KADM5_PRINCIPAL} admin}
    expect_kadm_ok
    expect "^% "
    send_tcl_cmd_await_echo {kadm5_randkey_principal $server_handle server/$h key null}
    expect_kadm_ok
    expect "^% "
    send_tcl_cmd_await_echo {kadm5_create_principal $server_handle [simple_principal notserver/$h] {KADM5_PRINCIPAL} admin}
    expect_kadm_ok
    expect "^% "
    send_tcl_cmd_await_echo {kadm5_randkey_principal $server_handle notserver/$h key null}
    expect_kadm_ok
    expect "^% "
    send_tcl_cmd_await_echo {kadm5_destroy $server_handle}
    expect_kadm_ok
    expect "^% "
    wait -nowait -i $spawn_id
    close -i $spawn_id
}

if ![info exists CANON_HOST] {
    set CANON_HOST $env(QUALNAME)
    setup_database
    file delete $env(RPC_TEST_KEYTAB)
    exec $env(TOP)/cli/kadmin -p admin -w admin ktadd -k $env(RPC_TEST_KEYTAB) server/$CANON_HOST
}


proc kinit {princ pass lifetime} {
	global kinit
	global wait_error_index wait_errno_index wait_status_index

	spawn -noecho $kinit -5 -l $lifetime $princ
	expect {
		-re "Password for $princ.*: " { send "$pass\n"; expect eof }
		timeout { perror "Timeout waiting for kinit"; close }
		eof
	}

	set ret [wait]
	if {[lindex $ret $wait_error_index] == -1} {
	 	perror \
	    "wait(kinit $princ) returned error [lindex $ret $wait_errno_index]"
	} else {
		if {[lindex $ret $wait_status_index] != 0} {
			perror \
		    "kinit $princ failed with [lindex $ret $wait_status_index]"
		}
	}
}

proc flush_server {} {
	global server_id
	global expect_out

	verbose "flushing server output" 1

	while {1} {
		set timeout 5

		expect {
			-i $server_id
			-re "^.+$" {
			    verbose "server output: $expect_out(buffer)"
			}
			timeout { break }
		}
	}
}

proc start_client {testname ccname user password lifetime count
		  {target ""}} {
	global env CLIENT PROT hostname server_port spawn_id verbose

	if {$target == ""} {
		set target "server@$hostname"
	}

	set env(KRB5CCNAME) FILE:[pwd]/krb5cc_rpc_test_$ccname
	kinit $user $password $lifetime

	if {$verbose > 0} {
		spawn $CLIENT -a 1 -s 1 -m 1 $PROT $hostname $server_port $target $count
	} else {
		spawn $CLIENT $PROT $hostname $server_port $target $count
	}

	verbose "$testname: client $ccname started"

	unset env(KRB5CCNAME)
}

proc eof_client {testname ccname id status} {
	verbose "$testname: eof'ing for client $ccname" 1

	expect {
		-i $id
		-re "^marshall_new_creds\[^\n\]*\n" { exp_continue }
		-re "^gssapi_\[^\n\]*\n" { exp_continue }
		-re "^\r?\n" { exp_continue }
		eof { verbose $expect_out(buffer) 1 }
		timeout { 
			fail "$testname: timeout waiting for client $ccname to exit"
		}
	}
	wait_client $testname $ccname $id $status
}


proc wait_client {testname ccname id status} {
	global env
	global kill
	global kdestroy
	global wait_error_index wait_errno_index wait_status_index

	verbose "$testname: waiting for client $ccname" 1

	set ret [wait -i $id]
	if {[lindex $ret $wait_error_index] == -1} {
		fail \
       "$testname: wait $ccname returned error [lindex $ret $wait_errno_index]"
	} else {
		if {[lindex $ret $wait_status_index] == $status} {
			pass "$testname: client $ccname"
		} else {
			fail "$testname: client $ccname: unexpected return status [lindex $ret $wait_status_index], should be $status."
		}
	}

	set env(KRB5CCNAME) FILE:[pwd]/krb5cc_rpc_test_$ccname
 	if {[catch "exec $kdestroy -5"] != 0} {
 		perror "$testname: cannot destroy client $ccname ccache"
 	}

	unset env(KRB5CCNAME)
}
