Added socat-chain.sh for layering protocols
diff --git a/CHANGES b/CHANGES
index 4aa4980..b1e2861 100644
--- a/CHANGES
+++ b/CHANGES
@@ -197,6 +197,10 @@
 	reads info from /proc/self/stat and searches for a device with matching
 	major and minor numbers.
 
+	Added socat-chain.sh that makes it possible to stack protocols, e.g. to
+	drive socks through TLS, or to use TLS over a serial line.
+	Tests: SOCAT_CHAIN_SOCKS4 SOCAT_CHAIN_SSL_PTY
+
 Corrections:
 	When a sub process (EXEC, SYSTEM) terminated with exit code other than
 	0, its last sent data might have been lost depending on timing of read/
diff --git a/Makefile.in b/Makefile.in
index 3ecd5d2..cf9f263 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -76,7 +76,8 @@
 
 
 DOCFILES = README README.FIPS CHANGES FILES EXAMPLES PORTING SECURITY DEVELOPMENT doc/socat.yo doc/socat.1 doc/socat.html FAQ BUGREPORTS COPYING COPYING.OpenSSL doc/dest-unreach.css doc/socat-openssltunnel.html doc/socat-multicast.html doc/socat-tun.html doc/socat-genericsocket.html
-SHFILES = daemon.sh mail.sh ftp.sh readline.sh \
+SHFILES = socat-chain.sh \
+	daemon.sh mail.sh ftp.sh readline.sh \
 	socat_buildscript_for_android.sh
 TESTFILES = test.sh socks4echo.sh proxyecho.sh readline-test.sh \
 	proxy.sh socks4a-echo.sh
@@ -131,6 +132,7 @@
 	mkdir -p $(DESTDIR)$(BINDEST)
 	$(INSTALL) -m 755 socat $(DESTDIR)$(BINDEST)/socat1
 	ln -s socat1 $(DESTDIR)$(BINDEST)/socat
+	$(INSTALL) -m 755 socat-chain.sh  $(DESTDIR)$(BINDEST)
 	$(INSTALL) -m 755 procan $(DESTDIR)$(BINDEST)
 	$(INSTALL) -m 755 filan $(DESTDIR)$(BINDEST)
 	mkdir -p $(DESTDIR)$(MANDEST)/man1
@@ -140,6 +142,7 @@
 uninstall:
 	rm -f $(DESTDIR)$(BINDEST)/socat
 	rm -f $(DESTDIR)$(BINDEST)/socat1
+	rm -f $(DESTDIR)$(BINDEST)/socat-chain.sh
 	rm -f $(DESTDIR)$(BINDEST)/procan
 	rm -f $(DESTDIR)$(BINDEST)/filan
 	rm -f $(DESTDIR)$(MANDEST)/man1/socat.1
diff --git a/socat-chain.sh b/socat-chain.sh
new file mode 100755
index 0000000..865446d
--- /dev/null
+++ b/socat-chain.sh
@@ -0,0 +1,264 @@
+#! /usr/bin/env bash
+# Copyright Gerhard Rieger and contributors (see file CHANGES)
+# Published under the GNU General Public License V.2, see file COPYING
+
+# Shell script to build a chain of Socat instances connected via TCP sockets.
+# This allows to drive, e.g., PROXY-CONNECT over SSL, or SSL over serial.
+# Currently only a chain made from 3 addresses, resulting in two instances, is
+# implemented.
+# The 2nd address must be one of OPENSSL (SSL), PROXY-CONNECT (PROXY),
+# SOCKS4, SOCKS4A, SOCKS5
+
+# This is beta!
+
+# Examples:
+
+# Drive HTTP CONNECT (PROXY) over SSL
+# (establish an SSL tunnel to a proxy server, request being forwarded to a
+# telnet server):
+#   socat-chain.sh \
+#       STDIO \
+#       PROXY::<telnet-server>:23 \
+#       OPENSSL:<proxy-server>:8443
+
+# Accept connections that arrive on port 7777, encrypt the data, and send it
+# via socks server to final target:
+#   socat-chain.sh \
+#       TCP-L:7777,reuseaddr,fork \
+#       OPENSSL,verify=0 \
+#       SOCKS4:<socks-server>:<ssl-server>:8443
+
+# Receive SSL coming from a serial lie
+#   socat-chain.sh \
+#       /dev/ttyS0,cfmakeraw \
+#       SSL-L,cafile=server.pem,verify=0 \
+#       TCP4:localhost:80
+
+# Formally, this is what happens:
+#   socat-chain.sh addr1 addr2 addr3
+# results in something like:
+#   socat TCP-L:RANDOM addr3 &
+#   socat addr1 addr2:localhost:RANDOM
+# or on passive/listening addr2:
+#   socat addr2:RANDOM addr3 &
+#   socat addr1 TCP:localhost:RANDOM
+
+ECHO="echo -e"
+
+usage () {
+    $ECHO "Usage: $0 <options> <address1> <address2> <address3>"
+    $ECHO "	<address1> is typically a passive (listening) address like"
+    $ECHO "		TCP-L:1234"
+    $ECHO "	<address2> must be one of OPENSSL, PROXY, SOCK4, SOCKS4A, or SOCKS5,"
+    $ECHO "		or SSL-L (passive/listening)"
+    $ECHO "		Given server hostname and port are ignored and replaced by internal"
+    $ECHO "		communication point"
+    $ECHO "	<address3> is typically a client address with protocol like OPENSSL"
+    $ECHO "	<options>:"
+    $ECHO "		-d*  -S <sigmask>  -t <timeout>  -T <timeout> 	are passed to socat"
+    $ECHO "		-V	prints the socat commands before starting them"
+    $ECHO "Example to drive SOCKS over TLS:"
+    $ECHO "	$0 \\"
+    $ECHO "		TCP4-L:1234,reuseaddr,fork \\"
+    $ECHO "		SOCKS::<server>:<port> \\"
+    $ECHO "		OPENSSL:10.2.3.4:12345,cafile=..."
+    $ECHO "	Clients that connect to port 1234 will be forwarded to <server>:<port> using socks"
+    $ECHO "	over TLS"
+}
+
+
+LOCALHOST=127.0.0.1
+
+VERBOSE= QUIET= OPTS=
+while [ "$1" ]; do
+    case "X$1" in
+	X-h) usage; exit ;;
+	X-V) VERBOSE=1 ;;
+	X-q) QUIET=1; OPTS="-d0" ;;
+	X-d*|X-l?*) OPTS="$OPTS $1" ;;
+	X-b|X-S|X-t|X-T|X-l) OPT=$1; shift; OPTS="$OPTS $OPT $1" ;;
+	X-) break ;;
+	X-*) echo "$0: Unknown option \"$1\"" >&2
+	     usage >&2
+	     exit 1 ;;
+	*) break ;;
+    esac
+    shift
+done
+
+ARG0="$1"
+ARG1="$2"
+ARG2="$3"
+
+if [ -z "$ARG0" -o  -z "$ARG1" -o -z "$ARG2" ]; then
+    echo "$0: Three addresses required" >&2
+    usage >&2
+    exit 1
+fi
+
+
+mkprogname () {
+    ARG="$1"
+    if [[ "$ARG" =~ .*[:].* ]]; then
+	NAME="${ARG%%:*}"
+    elif [[ "$ARG" =~ .*[,].* ]]; then
+	NAME="${ARG%%,*}"
+    elif [ "X$ARG" = X- ]; then
+	NAME=stdio
+    else
+	NAME="$ARG"
+    fi
+    NAME="${NAME,,*}"
+    echo $NAME
+}
+
+
+# You may place a fork option in the first address
+# in which case the following internal listeners do fork too
+FORK=
+case "$ARG0" in
+    *,fork,*|*,fork) FORK=fork ;;
+esac
+
+# Split middle address for insertion of additional parts
+if [[ "$ARG1" =~ .*,.* ]]; then
+    ARG1A="${ARG1%%,*}"
+    ARG1B="${ARG1#*,}"
+else
+    ARG1A="$ARG1"
+    ARG1B=
+fi
+
+case "$0" in
+    */*) SOCAT=${0%/*}/socat ;;
+    *) SOCAT=socat ;;
+esac
+
+PORT=$($SOCAT -d -d TCP4-L:0,accept-timeout=0.000001 /dev/null 2>&1 |grep listening |sed 's/.*:\([1-9][0-9]*\)$/\1/')
+if [ -z "$PORT" ]; then
+    echo "$0: Failed to determine free TCP port" >&2
+    exit 1
+fi
+
+PASSIVE= 	# is the second address passive/listening/server?
+case "${ARG1A^^*}" in
+    OPENSSL|OPENSSL:*|SSL|SSL:.*)
+	OPTS1A=
+	#if [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^,]*\)\(.*\) ]]; then 	# bash 3
+	if [[ $ARG1A =~ ^([^:]*):([^:]*):([^,]*)(.*) ]]; then
+	    OPTS1A="${BASH_REMATCH[4]}"
+	#elif [[ $ARG1A =~ ^\([^,]*\)\(.*\) ]]; then 			# bash 3
+	elif [[ $ARG1A =~ ^([^,]*)(.*) ]]; then
+	    OPTS1A="${BASH_REMATCH[2]}"
+	else
+	    echo "$0: \"$ARG1A\": invalid arguments" >&2
+	    exit 1
+	fi
+	PROG1="${BASH_REMATCH[1]}"
+	NAME1=$(mkprogname "${BASH_REMATCH[1]}")
+	NAME2=$(mkprogname "$ARG2")
+	ARG1A=$PROG1:$LOCALHOST:$PORT$OPTS1A ;;
+    PROXY-CONNECT:*|PROXY:*)
+	#if ! [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^:]*\):\([^,]*\)\(.*\) ]]; then 	# bash 3
+	if ! [[ $ARG1A =~ ^([^:]*):([^:]*):([^:]*):([^,]*)(.*) ]]; then
+	    echo "$0: \"$ARG1A\": invalid arguments" >&2
+	    exit 1
+	fi
+	#echo "0:\"${BASH_REMATCH[0]}\" 1:\"${BASH_REMATCH[1]}\" 2:\"${BASH_REMATCH[2]}\" 3:\"${BASH_REMATCH[3]}\" 4:\"${BASH_REMATCH[4]}\""
+	PROG1="${BASH_REMATCH[1]}"
+	NAME1=$(mkprogname "${PROG1,,*}")
+	NAME2=$(mkprogname "$ARG2")
+	OPTS1A="${BASH_REMATCH[5]}"
+	ARG1A="$PROG1:$LOCALHOST:${BASH_REMATCH[3]}:${BASH_REMATCH[4]},proxyport=$PORT,$OPTS1A" ;;
+    SOCKS:*|SOCKS4:*|SOCKS4A*)
+	#if ! [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^:]*\):\([^:,]*\),* ]]; then 	# bash 3
+	if ! [[ $ARG1A =~ ^([^:]*):([^:]*):([^:]*):([^:,]*),* ]]; then
+	    echo "$0: \"$ARG1A\": invalid arguments" >&2
+	    exit 1
+	fi
+	PROG1="${BASH_REMATCH[1]}"
+	NAME1=$(mkprogname "${PROG1,,*}")
+	NAME2=$(mkprogname "$ARG2")
+	OPTS1A="${BASH_REMATCH[5]}"
+	ARG1A="$PROG1:$LOCALHOST:${BASH_REMATCH[3]}:${BASH_REMATCH[4]},socksport=$PORT,$OPTS1A" ;;
+    SOCKS5:*|SOCKS5-CONNECT*)
+	#if ! [[ $ARG1A =~ ^\([^:]*\):\([^:]*\):\([^:]*\):\([^:,]*\):\([^:,]*\),* ]]; then 	# bash 3
+	if ! [[ $ARG1A =~ ^([^:]*):([^:]*):([^:]*):([^:,]*):([^:,]*),* ]]; then
+	    echo "$0: \"$ARG1A\": invalid arguments" >&2
+	    exit 1
+	fi
+	PROG1="${BASH_REMATCH[1]}"
+	NAME1=$(mkprogname "${PROG1,,*}")
+	NAME2=$(mkprogname "$ARG2")
+	OPTS1A="${BASH_REMATCH[6]}"
+	ARG1A="$PROG1:$LOCALHOST:$PORT:${BASH_REMATCH[4]}:${BASH_REMATCH[5]},$OPTS1A" ;;
+    # Passive (server) addresses
+    OPENSSL-LISTEN|OPENSSL-LISTEN:*|SSL-L|SSL-L:.*)
+	PASSIVE=1
+	OPTS1A=
+	#if [[ $ARG1A =~ ^\([^:]*\):\([^,]*\)\(.*\) ]]; then 	# bash 3
+	if [[ $ARG1A =~ ^([^:]*):([^,]*)(.*) ]]; then
+	    OPTS1A="${BASH_REMATCH[3]}"
+	#elif [[ $ARG1A =~ ^\([^,]*\)\(.*\) ]]; then 		# bash 3
+	elif [[ $ARG1A =~ ^([^,]*)(.*) ]]; then
+	    OPTS1A="${BASH_REMATCH[2]}"
+	else
+	    echo "$0: \"$ARG1A\": invalid arguments" >&2
+	    exit 1
+	fi
+	PROG1="${BASH_REMATCH[1]}"
+	NAME1=$(mkprogname "$ARG0")
+	NAME2=$(mkprogname "${BASH_REMATCH[1]}")
+	ARG1A=$PROG1:$PORT$OPTS1A ;;
+    *) echo "$0: Unsupported address \"$ARG1A\"" >&2
+       usage >&2
+       exit 1 ;;
+esac
+
+ADDR1A="$ARG0"
+if [ -z "$PASSIVE" ]; then
+    ADDR1B="$ARG1A,bind=$LOCALHOST,$ARG1B"
+    ADDR2A="TCP4-L:$PORT,reuseaddr,$FORK,bind=$LOCALHOST,range=$LOCALHOST/32"
+else
+    ADDR1B="TCP4:$LOCALHOST:$PORT,bind=$LOCALHOST"
+    ADDR2A="$ARG1A,reuseaddr,$FORK,bind=$LOCALHOST,range=$LOCALHOST/32,$ARG1B"
+fi
+ADDR2B="$ARG2"
+
+
+pid1= pid2=
+trap '[ "$pid1" ] && kill $pid1 2>/dev/null; [ "$pid2" ] && kill $pid2 2>/dev/null' EXIT
+
+set -bm
+trap 'rc=$?; if ! kill -n 0 $pid2 2>/dev/null; then [ -z "$QUIET" -a $rc -ne 0 ] && echo "$0: socat-$NAME2 exited with rc=$rc" >&2; exit $rc; fi' SIGCHLD
+
+# Start instance 2 first, because instance 1 ("left") connects to 2
+if [ "$VERBOSE" ]; then
+    $ECHO "$SOCAT $OPTS -lp socat-$NAME2 \\
+	\"$ADDR2A\" \\
+	\"$ADDR2B\" &"
+fi
+$SOCAT $OPTS -lp socat-$NAME2 \
+	"$ADDR2A" \
+	"$ADDR2B" &
+pid2=$!
+sleep 0.1
+
+#trap 'if ! kill -n 0 $pid1 2>/dev/null; then [ -z "$QUIET" ] && echo "$0: socat-$NAME1 exited with rc=$?" >&2; kill $pid2 2>/dev/null; exit 1; elif ! kill -n 0 $pid2 2>/dev/null; then [ -z "$QUIET" ] && echo "$0: socat-$NAME2 exited with rc=$?" >&2; kill $pid1 2>/dev/null; exit 1; fi' SIGCHLD
+
+if [ "$VERBOSE" ]; then
+    $ECHO "$SOCAT $OPTS -lp socat-$NAME1 \\
+	\"$ADDR1A\" \\
+	\"$ADDR1B\""
+fi
+$SOCAT $OPTS -lp socat-$NAME1 \
+	"$ADDR1A" \
+	"$ADDR1B"
+#pid1=$!
+rc1=$?
+
+kill $pid2 2>/dev/null
+wait 2>/dev/null
+#wait -f
+
+exit $rc1
diff --git a/socks4echo.sh b/socks4echo.sh
index 103719a..48ea536 100755
--- a/socks4echo.sh
+++ b/socks4echo.sh
@@ -91,7 +91,7 @@
 fi
 if [ "$u" != "nobody" ]; then
     $ECHO "$SOCKSREPLY_FAILED"
-    echo "wrong socks user requested" >&2
+    echo "wrong socks user requested (expected \"nobody\")" >&2
     exit
 fi
 
diff --git a/test.sh b/test.sh
index c9bc3aa..f91973e 100755
--- a/test.sh
+++ b/test.sh
@@ -1044,7 +1044,7 @@
 	local uname="$(echo $UNAME |tr 'A-Z' 'a-z')"
 	for i in $unames; do
 	    if [ "$uname" = "$(echo "$i" |tr 'A-Z,' 'a-z ')" ]; then
-	        # good, mark as passed
+		# good, mark as passed
 		i=
 		break;
 	    fi
@@ -18838,6 +18838,154 @@
 N=$((N+1))
 
 
+# Test the socat-chain.sh script with SOCKS4 over UNIX-socket
+NAME=SOCAT_CHAIN_SOCKS4
+case "$TESTS" in
+*%$N%*|*%functions%*|*%scripts%*|*%socat-chain%*|*%listen%*|*%fork%*|*%ip4%*|*%tcp4%*|*%unix%*|*%socks4%*|*%socket%*|*%$NAME%*)
+TEST="$NAME: test socat-chain.sh with SOCKS4 over UNIX-socket"
+# Run a socks4 server on UNIX-listen
+# Connect with socat-chein.sh; check if data transfer is correct
+if ! eval $NUMCOND; then :
+# Remove unneeded checks, adapt lists of the remaining ones
+elif ! cond=$(checkconds \
+		  "" \
+		  "" \
+		  "" \
+		  "IP4 TCP LISTEN STDIO UNIX" \
+		  "TCP4-LISTEN PIPE STDIN STDOUT TCP4 UNIX UNIX-LISTEN" \
+		  "so-reuseaddr" \
+		  "tcp4 unix" ); then
+    $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N
+    numCANT=$((numCANT+1))
+    listCANT="$listCANT $N"
+    namesCANT="$namesCANT $NAME"
+else
+    ts="$td/test$N.sock"
+    tf="$td/test$N.stdout"
+    te="$td/test$N.stderr"
+    tdiff="$td/test$N.diff"
+    da="test$N $(date) $RANDOM"
+    CMD0="$TRACE $SOCAT $opts UNIX-LISTEN:$ts,reuseaddr EXEC:./socks4echo.sh"
+    CMD1="$TRACE ./socat-chain.sh $opts - SOCKS4::32.98.76.54:32109,socksuser=nobody UNIX:$ts"
+    printf "test $F_n $TEST... " $N
+    $CMD0 >/dev/null 2>"${te}0" &
+    pid0=$!
+    waitunixport $ts 1
+    #relsleep 1 	# if no matching wait*port function
+    echo "$da" |$CMD1 >"${tf}1" 2>"${te}1"
+    rc1=$?
+    kill $pid0 2>/dev/null; wait
+    if [ "$rc1" -ne 0 ]; then
+	$PRINTF "$FAILED (rc1=$rc1)\n"
+	echo "$CMD0 &"
+	cat "${te}0" >&2
+	echo "$CMD1"
+	cat "${te}1" >&2
+	numFAIL=$((numFAIL+1))
+	listFAIL="$listFAIL $N"
+	namesFAIL="$namesFAIL $NAME"
+    elif ! echo "$da" |diff - "${tf}1" >$tdiff; then
+	$PRINTF "$FAILED (diff)\n"
+	echo "$CMD0 &"
+	cat "${te}0" >&2
+	echo "$CMD1"
+	cat "${te}1" >&2
+	echo "// diff:" >&2
+	cat "$tdiff" >&2
+	numFAIL=$((numFAIL+1))
+	listFAIL="$listFAIL $N"
+	namesFAIL="$namesFAIL $NAME"
+    else
+	$PRINTF "$OK\n"
+	if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
+	if [ "$DEBUG" ];   then cat "${te}0" >&2; fi
+	if [ "$VERBOSE" ]; then echo "$CMD1"; fi
+	if [ "$DEBUG" ];   then cat "${te}1" >&2; fi
+	numOK=$((numOK+1))
+    fi
+fi # NUMCOND
+ ;;
+esac
+N=$((N+1))
+
+# Test the socat-chain.sh script by driving SSL over serial
+NAME=SOCAT_CHAIN_SSL_PTY
+case "$TESTS" in
+*%$N%*|*%functions%*|*%scripts%*|*%socat-chain%*|*%listen%*|*%fork%*|*%ip4%*|*%tcp4%*|*%unix%*|*%socket%*|*%socket%*|*%$NAME%*)
+TEST="$NAME: test socat-chain.sh with SSL over PTY"
+# Run a socat-chain.sh instance with SSL listening behind a PTY;
+# open the PTY with socat-chein.sh using SSL;
+# check if data transfer is correct
+if ! eval $NUMCOND; then :
+# Remove unneeded checks, adapt lists of the remaining ones
+elif ! cond=$(checkconds \
+		  "" \
+		  "" \
+		  "" \
+		  "IP4 TCP LISTEN OPENSSL STDIO PTY" \
+		  "TCP4-LISTEN SOCKETPAIR STDIN STDOUT TCP4 SSL SSL-L" \
+		  "so-reuseaddr" \
+		  "tcp4" ); then
+    $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N
+    numCANT=$((numCANT+1))
+    listCANT="$listCANT $N"
+    namesCANT="$namesCANT $NAME"
+elif [[ $BASH_VERSION =~ ^[1-3]\. ]]; then
+    $PRINTF "test $F_n $TEST... ${YELLOW}requires bash 4 or higher${NORMAL}\n" $N
+    numCANT=$((numCANT+1))
+    listCANT="$listCANT $N"
+    namesCANT="$namesCANT $NAME"
+else
+    gentestcert testsrv
+    tp="$td/test$N.pty"
+    tf="$td/test$N.stdout"
+    te="$td/test$N.stderr"
+    tdiff="$td/test$N.diff"
+    da="test$N $(date) $RANDOM"
+    CMD0="$TRACE ./socat-chain.sh $opts PTY,link=$tp SSL-L,cert=testsrv.pem,verify=0 SOCKETPAIR"
+    CMD1="$TRACE ./socat-chain.sh $opts - SSL,cafile=testsrv.crt,commonname=localhost $tp,cfmakeraw"
+    printf "test $F_n $TEST... " $N
+    $CMD0 >/dev/null 2>"${te}0" &
+    pid0=$!
+    waitfile $tp 1
+    echo "$da" |$CMD1 >"${tf}1" 2>"${te}1"
+    rc1=$?
+    kill $pid0 2>/dev/null
+    wait 2>/dev/null
+    if [ "$rc1" -ne 0 ]; then
+	$PRINTF "$FAILED (rc1=$rc1)\n"
+	echo "$CMD0 &"
+	cat "${te}0" >&2
+	echo "$CMD1"
+	cat "${te}1" >&2
+	numFAIL=$((numFAIL+1))
+	listFAIL="$listFAIL $N"
+	namesFAIL="$namesFAIL $NAME"
+    elif ! echo "$da" |diff - "${tf}1" >$tdiff; then
+	$PRINTF "$FAILED (diff)\n"
+	echo "$CMD0 &"
+	cat "${te}0" >&2
+	echo "$CMD1"
+	cat "${te}1" >&2
+	echo "// diff:" >&2
+	cat "$tdiff" >&2
+	numFAIL=$((numFAIL+1))
+	listFAIL="$listFAIL $N"
+	namesFAIL="$namesFAIL $NAME"
+    else
+	$PRINTF "$OK\n"
+	if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
+	if [ "$DEBUG" ];   then cat "${te}0" >&2; fi
+	if [ "$VERBOSE" ]; then echo "$CMD1"; fi
+	if [ "$DEBUG" ];   then cat "${te}1" >&2; fi
+	numOK=$((numOK+1))
+    fi
+fi # NUMCOND
+ ;;
+esac
+N=$((N+1))
+
+
 # end of common tests
 
 ##################################################################################
@@ -19015,7 +19163,7 @@
 	numFAIL=$((numFAIL+1))
 	listFAIL="$listFAIL $N"
 	namesFAIL="$namesFAIL $NAME"
-    elif ! echo "$da" |diff "${tf}1" - >$tdiff; then
+    elif ! echo "$da" |diff - "${tf}1" >$tdiff; then
 	$PRINTF "$FAILED (diff)\n"
 	echo "$CMD0 &"
 	cat "${te}0" >&2
diff --git a/xio-pty.c b/xio-pty.c
index 2e6f60c..585fb1f 100644
--- a/xio-pty.c
+++ b/xio-pty.c
@@ -38,7 +38,8 @@
 {
    /* we expect the form: filename */
    struct single *sfd = &xfd->stream;
-   int ptyfd = -1, ttyfd = -1;
+   int ptyfd = -1; 	/* master */
+   int ttyfd = -1; 	/* slave */
 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
    bool useptmx = false;	/* use /dev/ptmx or equivalent */
 #endif
@@ -101,6 +102,18 @@
    if (applyopts_single(sfd, opts, PH_INIT) < 0)  return -1;
    applyopts2(sfd, -1, opts, PH_INIT, PH_EARLY);
 
+#if HAVE_OPENPTY
+   if (ptyfd < 0) {
+      int result;
+      if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
+	 Error4("openpty(%p, %p, %p, NULL, NULL): %s",
+		&ptyfd, &ttyfd, ptyname, strerror(errno));
+	 return -1;
+      }
+      Notice1("PTY is %s", ptyname);
+   }
+#endif /* HAVE_OPENPTY */
+
    applyopts(sfd, -1, opts, PH_PREBIGEN);
 
 #if defined(HAVE_DEV_PTMX)
@@ -109,7 +122,7 @@
 #  define PTMX "/dev/ptc"	/* AIX 4.3.3 */
 #endif
 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
-   if (useptmx) {
+   if (useptmx || ptyfd < 0) {
       if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) {
 	 Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s",
 	       strerror(errno));
@@ -117,7 +130,7 @@
       } else {
 	 ;/*0 Info1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620) -> %d", ptyfd);*/
       }
-      if (ptyfd >= 0 && ttyfd < 0) {
+      if (ptyfd >= 0) {
 	 /* we used PTMX before forking */
 	 /*0 extern char *ptsname(int);*/
 #if HAVE_GRANTPT	/* AIX, not Linux */
@@ -146,17 +159,6 @@
       }
    }
 #endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */
-#if HAVE_OPENPTY
-   if (ptyfd < 0) {
-      int result;
-      if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
-	 Error4("openpty(%p, %p, %p, NULL, NULL): %s",
-		&ptyfd, &ttyfd, ptyname, strerror(errno));
-	 return -1;
-      }
-      Notice1("PTY is %s", ptyname);
-   }
-#endif /* HAVE_OPENPTY */
 
    if (!retropt_string(opts, OPT_SYMBOLIC_LINK, &linkname)) {
       xio_unlink(linkname, E_ERROR);
@@ -178,7 +180,7 @@
    applyopts_cloexec(ptyfd, opts);/*!*/
    sfd->dtype    = XIODATA_PTY;
 
-   applyopts(sfd, ptyfd, opts, PH_FD);
+   applyopts(sfd, ttyfd, opts, PH_FD);
 
    {
       /* special handling of user-late etc.; with standard behaviour (up to
diff --git a/xiohelp.c b/xiohelp.c
index 4641dbd..9db54c4 100644
--- a/xiohelp.c
+++ b/xiohelp.c
@@ -225,8 +225,8 @@
 	int isnum,
 	const char *syntax)
 {
-	Error4("%s: wrong number of parameters (%d instead of %d): usage: %s",
-	       addr, isnum, expectnum, syntax);
+	Error5("%s: wrong number of parameters (%d instead of %d): usage: %s%s",
+	       addr, isnum, expectnum, addr, syntax);
 	return -1;
 }