diff --git a/check.t b/check.t
index 593a6b0..b5ec7b1 100644
--- a/check.t
+++ b/check.t
@@ -1,4 +1,4 @@
-# $MirOS: src/bin/mksh/check.t,v 1.415 2011/03/06 17:06:17 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.416 2011/03/06 17:08:10 tg Exp $
 # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
 # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
 # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@@ -25,7 +25,7 @@
 # http://www.research.att.com/~gsf/public/ifs.sh
 
 expected-stdout:
-	@(#)MIRBSD KSH R39 2011/03/05
+	@(#)MIRBSD KSH R39 2011/03/06
 description:
 	Check version of shell.
 stdin:
@@ -2045,7 +2045,7 @@ stdin:
 	print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} ve={$ve} vf={$vf} |"
 expected-stdout:
 	function foo {
-	    vc= <<- EOF 
+		vc= <<-EOF 
 	=c $x \x40=
 	EOF
 	
@@ -6962,6 +6962,492 @@ expected-stdout:
 	.
 	16#4F68 16#24 2 1
 ---
+name: comsub-torture
+description:
+	Check the tree dump functions work correctly
+stdin:
+	if [[ -z $__progname ]]; then echo >&2 call me with __progname; exit 1; fi
+	while IFS= read -r line; do
+		if [[ $line = '#1' ]]; then
+			lastf=0
+			continue
+		elif [[ $line = EOFN ]]; then
+			fbody=$fbody$'\n'$line
+			continue
+		elif [[ $line != '#'* ]]; then
+			fbody=$fbody$'\n\t'$line
+			continue
+		fi
+		if (( lastf )); then
+			x="inline_${nextf}() {"$fbody$'\n}\n'
+			print -nr -- "$x"
+			print -r -- "${x}typeset -f inline_$nextf" | $__progname
+			x="function comsub_$nextf { x=\$("$fbody$'\n); }\n'
+			print -nr -- "$x"
+			print -r -- "${x}typeset -f comsub_$nextf" | $__progname
+			x="function reread_$nextf { x=\$(("$fbody$'\n)|tr u x); }\n'
+			print -nr -- "$x"
+			print -r -- "${x}typeset -f reread_$nextf" | $__progname
+		fi
+		lastf=1
+		fbody=
+		nextf=${line#?}
+	done <<'EOD'
+	#1
+	#TCOM
+	vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
+	#TPAREN_TPIPE_TLIST
+	(echo $foo  |  tr -dc 0-9; echo)
+	#TAND_TOR
+	cmd  &&  echo ja  ||  echo nein
+	#TSELECT
+	select  file  in  *;  do  echo  "<$file>" ;  break ;  done
+	#TFOR_TTIME
+	for  i  in  {1,2,3}  ;  do  time  echo  $i ;  done
+	#TCASE
+	case  $foo  in  1)  echo eins;; 2) echo zwei  ;; *) echo kann net bis drei zählen;;  esac
+	#TIF_TBANG_TDBRACKET_TELIF
+	if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
+	#TWHILE
+	i=1; while (( i < 10 )); do echo $i; let ++i; done
+	#TUNTIL
+	i=10; until  (( !--i )) ; do echo $i; done
+	#TCOPROC
+	cat  *  |&  ls
+	#TFUNCT_TBRACE_TASYNC
+	function  korn  {  echo eins; echo zwei ;  }
+	bourne  ()  {  logger *  &  }
+	#IOREAD_IOCAT
+	tr  x  u  0<foo  >>bar
+	#IOWRITE_IOCLOB_IOHERE_noIOSKIP
+	cat  >|bar  <<'EOFN'
+	foo
+	EOFN
+	#IOWRITE_noIOCLOB_IOHERE_IOSKIP
+	cat  1>bar  <<-EOFI
+	foo
+	EOFI
+	#IORDWR_IODUP
+	sh  1<>/dev/console  0<&1  2>&1
+	#COMSUB_EXPRSUB
+	echo $(true) $((1+ 2))
+	#QCHAR_OQUOTE_CQUOTE
+	echo fo\ob\"a\`r\'b\$az
+	echo "fo\ob\"a\`r\'b\$az"
+	echo 'fo\ob\"a\`r'\''b\$az'
+	#OSUBST_CSUBST_OPAT_SPAT_CPAT
+	[[ ${foo#blub} = @(bar|baz) ]]
+	#0
+	EOD
+expected-stdout:
+	inline_TCOM() {
+		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
+	}
+	inline_TCOM() {
+		vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" 
+	} 
+	function comsub_TCOM { x=$(
+		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
+	); }
+	function comsub_TCOM {
+		x=$(vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" ) 
+	} 
+	function reread_TCOM { x=$((
+		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
+	)|tr u x); }
+	function reread_TCOM {
+		x=$(( vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" ) | tr u x ) 
+	} 
+	inline_TPAREN_TPIPE_TLIST() {
+		(echo $foo  |  tr -dc 0-9; echo)
+	}
+	inline_TPAREN_TPIPE_TLIST() {
+		( echo $foo | tr -dc 0-9 
+		  echo ) 
+	} 
+	function comsub_TPAREN_TPIPE_TLIST { x=$(
+		(echo $foo  |  tr -dc 0-9; echo)
+	); }
+	function comsub_TPAREN_TPIPE_TLIST {
+		x=$(( echo $foo | tr -dc 0-9 ; echo ) ) 
+	} 
+	function reread_TPAREN_TPIPE_TLIST { x=$((
+		(echo $foo  |  tr -dc 0-9; echo)
+	)|tr u x); }
+	function reread_TPAREN_TPIPE_TLIST {
+		x=$(( ( echo $foo | tr -dc 0-9 ; echo ) ) | tr u x ) 
+	} 
+	inline_TAND_TOR() {
+		cmd  &&  echo ja  ||  echo nein
+	}
+	inline_TAND_TOR() {
+		cmd && echo ja || echo nein 
+	} 
+	function comsub_TAND_TOR { x=$(
+		cmd  &&  echo ja  ||  echo nein
+	); }
+	function comsub_TAND_TOR {
+		x=$(cmd && echo ja || echo nein ) 
+	} 
+	function reread_TAND_TOR { x=$((
+		cmd  &&  echo ja  ||  echo nein
+	)|tr u x); }
+	function reread_TAND_TOR {
+		x=$(( cmd && echo ja || echo nein ) | tr u x ) 
+	} 
+	inline_TSELECT() {
+		select  file  in  *;  do  echo  "<$file>" ;  break ;  done
+	}
+	inline_TSELECT() {
+		select file in * 
+		do
+			echo "<$file>" 
+			break 
+		done 
+	} 
+	function comsub_TSELECT { x=$(
+		select  file  in  *;  do  echo  "<$file>" ;  break ;  done
+	); }
+	function comsub_TSELECT {
+		x=$(select file in * ; do echo "<$file>" ; break ; done ) 
+	} 
+	function reread_TSELECT { x=$((
+		select  file  in  *;  do  echo  "<$file>" ;  break ;  done
+	)|tr u x); }
+	function reread_TSELECT {
+		x=$(( select file in * ; do echo "<$file>" ; break ; done ) | tr u x ) 
+	} 
+	inline_TFOR_TTIME() {
+		for  i  in  {1,2,3}  ;  do  time  echo  $i ;  done
+	}
+	inline_TFOR_TTIME() {
+		for i in {1,2,3} 
+		do
+			time echo $i 
+		done 
+	} 
+	function comsub_TFOR_TTIME { x=$(
+		for  i  in  {1,2,3}  ;  do  time  echo  $i ;  done
+	); }
+	function comsub_TFOR_TTIME {
+		x=$(for i in {1,2,3} ; do time echo $i ; done ) 
+	} 
+	function reread_TFOR_TTIME { x=$((
+		for  i  in  {1,2,3}  ;  do  time  echo  $i ;  done
+	)|tr u x); }
+	function reread_TFOR_TTIME {
+		x=$(( for i in {1,2,3} ; do time echo $i ; done ) | tr u x ) 
+	} 
+	inline_TCASE() {
+		case  $foo  in  1)  echo eins;; 2) echo zwei  ;; *) echo kann net bis drei zählen;;  esac
+	}
+	inline_TCASE() {
+		case $foo in
+		(1)
+			echo eins 
+			;;
+		(2)
+			echo zwei 
+			;;
+		(*)
+			echo kann net bis drei zählen 
+			;;
+		esac 
+	} 
+	function comsub_TCASE { x=$(
+		case  $foo  in  1)  echo eins;; 2) echo zwei  ;; *) echo kann net bis drei zählen;;  esac
+	); }
+	function comsub_TCASE {
+		x=$(case $foo in (1) echo eins  ;; (2) echo zwei  ;; (*) echo kann net bis drei zählen  ;; esac ) 
+	} 
+	function reread_TCASE { x=$((
+		case  $foo  in  1)  echo eins;; 2) echo zwei  ;; *) echo kann net bis drei zählen;;  esac
+	)|tr u x); }
+	function reread_TCASE {
+		x=$(( case $foo in (1) echo eins  ;; (2) echo zwei  ;; (*) echo kann net bis drei zählen  ;; esac ) | tr u x ) 
+	} 
+	inline_TIF_TBANG_TDBRACKET_TELIF() {
+		if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
+	}
+	inline_TIF_TBANG_TDBRACKET_TELIF() {
+		if ! [[ 1 = 1 ]] 
+		then
+			echo eins 
+		elif [[ 1 = 2 ]] 
+		then
+			echo zwei 
+		else
+			echo drei 
+		fi 
+	} 
+	function comsub_TIF_TBANG_TDBRACKET_TELIF { x=$(
+		if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
+	); }
+	function comsub_TIF_TBANG_TDBRACKET_TELIF {
+		x=$(if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi ) 
+	} 
+	function reread_TIF_TBANG_TDBRACKET_TELIF { x=$((
+		if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
+	)|tr u x); }
+	function reread_TIF_TBANG_TDBRACKET_TELIF {
+		x=$(( if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi ) | tr u x ) 
+	} 
+	inline_TWHILE() {
+		i=1; while (( i < 10 )); do echo $i; let ++i; done
+	}
+	inline_TWHILE() {
+		i=1 
+		while let " i < 10 " 
+		do
+			echo $i 
+			let ++i 
+		done 
+	} 
+	function comsub_TWHILE { x=$(
+		i=1; while (( i < 10 )); do echo $i; let ++i; done
+	); }
+	function comsub_TWHILE {
+		x=$(i=1 ; while let " i < 10 " ; do echo $i ; let ++i ; done ) 
+	} 
+	function reread_TWHILE { x=$((
+		i=1; while (( i < 10 )); do echo $i; let ++i; done
+	)|tr u x); }
+	function reread_TWHILE {
+		x=$(( i=1 ; while let " i < 10 " ; do echo $i ; let ++i ; done ) | tr u x ) 
+	} 
+	inline_TUNTIL() {
+		i=10; until  (( !--i )) ; do echo $i; done
+	}
+	inline_TUNTIL() {
+		i=10 
+		until let " !--i " 
+		do
+			echo $i 
+		done 
+	} 
+	function comsub_TUNTIL { x=$(
+		i=10; until  (( !--i )) ; do echo $i; done
+	); }
+	function comsub_TUNTIL {
+		x=$(i=10 ; until let " !--i " ; do echo $i ; done ) 
+	} 
+	function reread_TUNTIL { x=$((
+		i=10; until  (( !--i )) ; do echo $i; done
+	)|tr u x); }
+	function reread_TUNTIL {
+		x=$(( i=10 ; until let " !--i " ; do echo $i ; done ) | tr u x ) 
+	} 
+	inline_TCOPROC() {
+		cat  *  |&  ls
+	}
+	inline_TCOPROC() {
+		cat * |& 
+		ls 
+	} 
+	function comsub_TCOPROC { x=$(
+		cat  *  |&  ls
+	); }
+	function comsub_TCOPROC {
+		x=$(cat * |&  ls ) 
+	} 
+	function reread_TCOPROC { x=$((
+		cat  *  |&  ls
+	)|tr u x); }
+	function reread_TCOPROC {
+		x=$(( cat * |&  ls ) | tr u x ) 
+	} 
+	inline_TFUNCT_TBRACE_TASYNC() {
+		function  korn  {  echo eins; echo zwei ;  }
+		bourne  ()  {  logger *  &  }
+	}
+	inline_TFUNCT_TBRACE_TASYNC() {
+		function korn {
+			echo eins 
+			echo zwei 
+		} 
+		bourne() {
+			logger * & 
+		} 
+	} 
+	function comsub_TFUNCT_TBRACE_TASYNC { x=$(
+		function  korn  {  echo eins; echo zwei ;  }
+		bourne  ()  {  logger *  &  }
+	); }
+	function comsub_TFUNCT_TBRACE_TASYNC {
+		x=$(function korn { echo eins ; echo zwei ; } ; bourne() { logger * &  } ) 
+	} 
+	function reread_TFUNCT_TBRACE_TASYNC { x=$((
+		function  korn  {  echo eins; echo zwei ;  }
+		bourne  ()  {  logger *  &  }
+	)|tr u x); }
+	function reread_TFUNCT_TBRACE_TASYNC {
+		x=$(( function korn { echo eins ; echo zwei ; } ; bourne() { logger * &  } ) | tr u x ) 
+	} 
+	inline_IOREAD_IOCAT() {
+		tr  x  u  0<foo  >>bar
+	}
+	inline_IOREAD_IOCAT() {
+		tr x u <foo >>bar 
+	} 
+	function comsub_IOREAD_IOCAT { x=$(
+		tr  x  u  0<foo  >>bar
+	); }
+	function comsub_IOREAD_IOCAT {
+		x=$(tr x u <foo >>bar ) 
+	} 
+	function reread_IOREAD_IOCAT { x=$((
+		tr  x  u  0<foo  >>bar
+	)|tr u x); }
+	function reread_IOREAD_IOCAT {
+		x=$(( tr x u <foo >>bar ) | tr u x ) 
+	} 
+	inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() {
+		cat  >|bar  <<'EOFN'
+		foo
+	EOFN
+	}
+	inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() {
+		cat >|bar <<"EOFN" 
+		foo
+	EOFN
+	
+	} 
+	function comsub_IOWRITE_IOCLOB_IOHERE_noIOSKIP { x=$(
+		cat  >|bar  <<'EOFN'
+		foo
+	EOFN
+	); }
+	function comsub_IOWRITE_IOCLOB_IOHERE_noIOSKIP {
+		x=$(cat >|bar <<"EOFN" 
+		foo
+	EOFN
+	) 
+	} 
+	function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP { x=$((
+		cat  >|bar  <<'EOFN'
+		foo
+	EOFN
+	)|tr u x); }
+	function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP {
+		x=$(( cat >|bar <<"EOFN" 
+		foo
+	EOFN
+	) | tr u x ) 
+	} 
+	inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() {
+		cat  1>bar  <<-EOFI
+		foo
+		EOFI
+	}
+	inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() {
+		cat >bar <<-EOFI 
+	foo
+	EOFI
+	
+	} 
+	function comsub_IOWRITE_noIOCLOB_IOHERE_IOSKIP { x=$(
+		cat  1>bar  <<-EOFI
+		foo
+		EOFI
+	); }
+	function comsub_IOWRITE_noIOCLOB_IOHERE_IOSKIP {
+		x=$(cat >bar <<-EOFI 
+	foo
+	EOFI
+	) 
+	} 
+	function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP { x=$((
+		cat  1>bar  <<-EOFI
+		foo
+		EOFI
+	)|tr u x); }
+	function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP {
+		x=$(( cat >bar <<-EOFI 
+	foo
+	EOFI
+	) | tr u x ) 
+	} 
+	inline_IORDWR_IODUP() {
+		sh  1<>/dev/console  0<&1  2>&1
+	}
+	inline_IORDWR_IODUP() {
+		sh 1<>/dev/console <&1 2>&1 
+	} 
+	function comsub_IORDWR_IODUP { x=$(
+		sh  1<>/dev/console  0<&1  2>&1
+	); }
+	function comsub_IORDWR_IODUP {
+		x=$(sh 1<>/dev/console <&1 2>&1 ) 
+	} 
+	function reread_IORDWR_IODUP { x=$((
+		sh  1<>/dev/console  0<&1  2>&1
+	)|tr u x); }
+	function reread_IORDWR_IODUP {
+		x=$(( sh 1<>/dev/console <&1 2>&1 ) | tr u x ) 
+	} 
+	inline_COMSUB_EXPRSUB() {
+		echo $(true) $((1+ 2))
+	}
+	inline_COMSUB_EXPRSUB() {
+		echo $(true ) $((1+ 2)) 
+	} 
+	function comsub_COMSUB_EXPRSUB { x=$(
+		echo $(true) $((1+ 2))
+	); }
+	function comsub_COMSUB_EXPRSUB {
+		x=$(echo $(true ) $((1+ 2)) ) 
+	} 
+	function reread_COMSUB_EXPRSUB { x=$((
+		echo $(true) $((1+ 2))
+	)|tr u x); }
+	function reread_COMSUB_EXPRSUB {
+		x=$(( echo $(true ) $((1+ 2)) ) | tr u x ) 
+	} 
+	inline_QCHAR_OQUOTE_CQUOTE() {
+		echo fo\ob\"a\`r\'b\$az
+		echo "fo\ob\"a\`r\'b\$az"
+		echo 'fo\ob\"a\`r'\''b\$az'
+	}
+	inline_QCHAR_OQUOTE_CQUOTE() {
+		echo fo\ob\"a\`r\'b\$az 
+		echo "fo\ob\"a\`r\'b\$az" 
+		echo "fo\ob\\"a\\`r"\'"b\\$az" 
+	} 
+	function comsub_QCHAR_OQUOTE_CQUOTE { x=$(
+		echo fo\ob\"a\`r\'b\$az
+		echo "fo\ob\"a\`r\'b\$az"
+		echo 'fo\ob\"a\`r'\''b\$az'
+	); }
+	function comsub_QCHAR_OQUOTE_CQUOTE {
+		x=$(echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\ob\\"a\\`r"\'"b\\$az" ) 
+	} 
+	function reread_QCHAR_OQUOTE_CQUOTE { x=$((
+		echo fo\ob\"a\`r\'b\$az
+		echo "fo\ob\"a\`r\'b\$az"
+		echo 'fo\ob\"a\`r'\''b\$az'
+	)|tr u x); }
+	function reread_QCHAR_OQUOTE_CQUOTE {
+		x=$(( echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\ob\\"a\\`r"\'"b\\$az" ) | tr u x ) 
+	} 
+	inline_OSUBST_CSUBST_OPAT_SPAT_CPAT() {
+		[[ ${foo#blub} = @(bar|baz) ]]
+	}
+	inline_OSUBST_CSUBST_OPAT_SPAT_CPAT() {
+		[[ ${foo#blub} = @(bar|baz) ]] 
+	} 
+	function comsub_OSUBST_CSUBST_OPAT_SPAT_CPAT { x=$(
+		[[ ${foo#blub} = @(bar|baz) ]]
+	); }
+	function comsub_OSUBST_CSUBST_OPAT_SPAT_CPAT {
+		x=$([[ ${foo#blub} = @(bar|baz) ]] ) 
+	} 
+	function reread_OSUBST_CSUBST_OPAT_SPAT_CPAT { x=$((
+		[[ ${foo#blub} = @(bar|baz) ]]
+	)|tr u x); }
+	function reread_OSUBST_CSUBST_OPAT_SPAT_CPAT {
+		x=$(( [[ ${foo#blub} = @(bar|baz) ]] ) | tr u x ) 
+	} 
+---
 name: test-stnze-1
 description:
 	Check that the short form [ $x ] works
@@ -7344,7 +7830,7 @@ name: better-parens-1a
 description:
 	Check support for ((…)) and $((…)) vs (…) and $(…)
 stdin:
-	if ( (echo fubar) | tr u x); then
+	if ( (echo fubar)|tr u x); then
 		echo ja
 	else
 		echo nein
@@ -7357,7 +7843,15 @@ name: better-parens-1b
 description:
 	Check support for ((…)) and $((…)) vs (…) and $(…)
 stdin:
-	echo $( (echo fubar) | tr u x) $?
+	echo $( (echo fubar)|tr u x) $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-1c
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	x=$( (echo fubar)|tr u x); echo $x $?
 expected-stdout:
 	fxbar 0
 ---
@@ -7365,7 +7859,7 @@ name: better-parens-2a
 description:
 	Check support for ((…)) and $((…)) vs (…) and $(…)
 stdin:
-	if ((echo fubar) | tr u x); then
+	if ((echo fubar)|tr u x); then
 		echo ja
 	else
 		echo nein
@@ -7378,7 +7872,15 @@ name: better-parens-2b
 description:
 	Check support for ((…)) and $((…)) vs (…) and $(…)
 stdin:
-	echo $((echo fubar) | tr u x) $?
+	echo $((echo fubar)|tr u x) $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-2c
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	x=$((echo fubar)|tr u x); echo $x $?
 expected-stdout:
 	fxbar 0
 ---
@@ -7386,7 +7888,7 @@ name: better-parens-3a
 description:
 	Check support for ((…)) and $((…)) vs (…) and $(…)
 stdin:
-	if ( (echo fubar) | (tr u x)); then
+	if ( (echo fubar)|(tr u x)); then
 		echo ja
 	else
 		echo nein
@@ -7399,7 +7901,15 @@ name: better-parens-3b
 description:
 	Check support for ((…)) and $((…)) vs (…) and $(…)
 stdin:
-	echo $( (echo fubar) | (tr u x)) $?
+	echo $( (echo fubar)|(tr u x)) $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-3c
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	x=$( (echo fubar)|(tr u x)); echo $x $?
 expected-stdout:
 	fxbar 0
 ---
@@ -7407,7 +7917,7 @@ name: better-parens-4a
 description:
 	Check support for ((…)) and $((…)) vs (…) and $(…)
 stdin:
-	if ((echo fubar) | (tr u x)); then
+	if ((echo fubar)|(tr u x)); then
 		echo ja
 	else
 		echo nein
@@ -7420,7 +7930,15 @@ name: better-parens-4b
 description:
 	Check support for ((…)) and $((…)) vs (…) and $(…)
 stdin:
-	echo $((echo fubar) | (tr u x)) $?
+	echo $((echo fubar)|(tr u x)) $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-4c
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	x=$((echo fubar)|(tr u x)); echo $x $?
 expected-stdout:
 	fxbar 0
 ---
diff --git a/jobs.c b/jobs.c
index a75a47e..d763012 100644
--- a/jobs.c
+++ b/jobs.c
@@ -22,7 +22,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.75 2011/02/18 22:26:09 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.76 2011/03/06 17:08:12 tg Exp $");
 
 #if HAVE_KILLPG
 #define mksh_killpg		killpg
@@ -427,7 +427,7 @@ exchild(struct op *t, int flags,
 		put_job(j, PJ_PAST_STOPPED);
 	}
 
-	snptreef(p->command, sizeof(p->command), "%T", t);
+	vistree(p->command, sizeof(p->command), t);
 
 	/* create child process */
 	forksleep = 1;
@@ -532,7 +532,7 @@ exchild(struct op *t, int flags,
 		if (t->type == TPIPE)
 			unwind(LLEAVE);
 		internal_warningf("%s: %s", "exchild", "execute() returned");
-		fptreef(shl_out, 2, "%s: tried to execute {\n%T\n}\n",
+		fptreef(shl_out, 8, "%s: tried to execute {\n\t%T\n}\n",
 		    "exchild", t);
 		shf_flush(shl_out);
 #endif
diff --git a/lex.c b/lex.c
index 4beb940..55423f4 100644
--- a/lex.c
+++ b/lex.c
@@ -22,7 +22,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.123 2011/03/06 01:25:33 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.124 2011/03/06 17:08:12 tg Exp $");
 
 /*
  * states while lexing word
@@ -614,29 +614,32 @@ yylex(int cf)
 			else if (c == ')') {
 				statep->ls_sasparen.nparen--;
 				if (statep->ls_sasparen.nparen == 1) {
-					POP_STATE();
 					if ((c2 = getsc()) == /*(*/')') {
+						POP_STATE();
 						/* end of EXPRSUB */
 						*wp++ = 0;
 						break;
 					} else {
 						Source *s;
 
+						ungetsc(c2);
 						/*
 						 * mismatched parenthesis -
 						 * assume we were really
 						 * parsing a $(...) expression
 						 */
 						*wp = EOS;
-						wp = Xstring(ws, wp);
+						wp = Xrestpos(ws, wp,
+						    statep->ls_sasparen.start);
+						POP_STATE();
 						/* dp = $((blah))\0 */
 						dp = wdstrip(wp, true, false);
 						s = pushs(SREREAD,
 						    source->areap);
 						s->start = s->str =
 						    (s->u.freeme = dp) + 2;
-						dp[strlen(dp) - 1] = c2;
-						/* s->str = (blah)C\0 */
+						dp[strlen(dp) - 1] = 0;
+						/* s->str = (blah)\0 */
 						s->next = source;
 						source = s;
 						goto subst_command;
diff --git a/sh.h b/sh.h
index 6fd64b9..cc4a4b3 100644
--- a/sh.h
+++ b/sh.h
@@ -154,9 +154,9 @@
 #endif
 
 #ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.437 2011/03/06 01:50:11 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.438 2011/03/06 17:08:13 tg Exp $");
 #endif
-#define MKSH_VERSION "R39 2011/03/05"
+#define MKSH_VERSION "R39 2011/03/06"
 
 #ifndef MKSH_INCLUDES_ONLY
 
@@ -1739,14 +1739,16 @@ struct op *compile(Source *);
 bool parse_usec(const char *, struct timeval *);
 char *yyrecursive(void);
 /* tree.c */
-int fptreef(struct shf *, int, const char *, ...);
+void fptreef(struct shf *, int, const char *, ...);
 char *snptreef(char *, int, const char *, ...);
 struct op *tcopy(struct op *, Area *);
 char *wdcopy(const char *, Area *);
 const char *wdscan(const char *, int);
 char *wdstrip(const char *, bool, bool);
 void tfree(struct op *, Area *);
-int fpFUNCTf(struct shf *, int, bool, const char *, struct op *);
+void vistree(char *, size_t, struct op *)
+    MKSH_A_BOUNDED(string, 1, 2);
+void fpFUNCTf(struct shf *, int, bool, const char *, struct op *);
 /* var.c */
 void newblock(void);
 void popblock(void);
diff --git a/tree.c b/tree.c
index 5241451..6a8d00e 100644
--- a/tree.c
+++ b/tree.c
@@ -22,19 +22,21 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.35 2011/03/06 02:28:59 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.36 2011/03/06 17:08:14 tg Exp $");
 
-#define INDENT	4
+#define INDENT	8
 
 #define tputc(c, shf) shf_putchar(c, shf);
 static void ptree(struct op *, int, struct shf *);
 static void pioact(struct shf *, int, struct ioword *);
-static void tputC(int, struct shf *);
-static void tputS(char *, struct shf *);
+static void tputS(const char *, struct shf *);
 static void vfptreef(struct shf *, int, const char *, va_list);
 static struct ioword **iocopy(struct ioword **, Area *);
 static void iofree(struct ioword **, Area *);
 
+/* "foo& ; bar" and "foo |& ; bar" are invalid */
+static bool prevent_semicolon = false;
+
 /*
  * print a command tree
  */
@@ -44,22 +46,26 @@ ptree(struct op *t, int indent, struct shf *shf)
 	const char **w;
 	struct ioword **ioact;
 	struct op *t1;
+	int i;
 
  Chain:
 	if (t == NULL)
 		return;
 	switch (t->type) {
 	case TCOM:
-		if (t->vars)
-			for (w = (const char **)t->vars; *w != NULL; )
+		if (t->vars) {
+			w = (const char **)t->vars;
+			while (*w)
 				fptreef(shf, indent, "%S ", *w++);
-		else
+		} else
 			shf_puts("#no-vars# ", shf);
-		if (t->args)
-			for (w = t->args; *w != NULL; )
+		if (t->args) {
+			w = t->args;
+			while (*w)
 				fptreef(shf, indent, "%S ", *w++);
-		else
+		} else
 			shf_puts("#no-args# ", shf);
+		prevent_semicolon = false;
 		break;
 	case TEXEC:
 		t = t->left;
@@ -78,30 +84,28 @@ ptree(struct op *t, int indent, struct shf *shf)
 	case TOR:
 	case TAND:
 		fptreef(shf, indent, "%T%s %T",
-		    t->left, (t->type==TOR) ? "||" : "&&", t->right);
+		    t->left, (t->type == TOR) ? "||" : "&&", t->right);
 		break;
 	case TBANG:
 		shf_puts("! ", shf);
+		prevent_semicolon = false;
 		t = t->right;
 		goto Chain;
-	case TDBRACKET: {
-		int i;
-
+	case TDBRACKET:
+		w = t->args;
 		shf_puts("[[", shf);
-		for (i = 0; t->args[i]; i++)
-			fptreef(shf, indent, " %S", t->args[i]);
+		while (*w)
+			fptreef(shf, indent, " %S", *w++);
 		shf_puts(" ]] ", shf);
 		break;
-	}
 	case TSELECT:
-		fptreef(shf, indent, "select %s ", t->str);
-		/* FALLTHROUGH */
 	case TFOR:
-		if (t->type == TFOR)
-			fptreef(shf, indent, "for %s ", t->str);
+		fptreef(shf, indent, "%s %s ",
+		    (t->type == TFOR) ? "for" : "select", t->str);
 		if (t->vars != NULL) {
 			shf_puts("in ", shf);
-			for (w = (const char **)t->vars; *w; )
+			w = (const char **)t->vars;
+			while (*w)
 				fptreef(shf, indent, "%S ", *w++);
 			fptreef(shf, indent, "%;");
 		}
@@ -112,34 +116,42 @@ ptree(struct op *t, int indent, struct shf *shf)
 		fptreef(shf, indent, "case %S in", t->str);
 		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
 			fptreef(shf, indent, "%N(");
-			for (w = (const char **)t1->vars; *w != NULL; w++)
+			w = (const char **)t1->vars;
+			while (*w) {
 				fptreef(shf, indent, "%S%c", *w,
 				    (w[1] != NULL) ? '|' : ')');
+				++w;
+			}
 			fptreef(shf, indent + INDENT, "%N%T%N;;", t1->left);
 		}
 		fptreef(shf, indent, "%Nesac ");
 		break;
-	case TIF:
+#ifndef MKSH_NO_DEPRECATED_WARNING
 	case TELIF:
-		/* 3 == strlen("if ") */
-		fptreef(shf, indent + 3, "if %T", t->left);
-		for (;;) {
+		internal_errorf("TELIF in tree.c:ptree() unexpected");
+		/* FALLTHROUGH */
+#endif
+	case TIF:
+		i = 2;
+		goto process_TIF;
+		do {
+			t = t->right;
+			i = 0;
+			fptreef(shf, indent, "%;");
+ process_TIF:
+			/* 5 == strlen("elif ") */
+			fptreef(shf, indent + 5 - i, "elif %T" + i, t->left);
 			t = t->right;
 			if (t->left != NULL) {
 				fptreef(shf, indent, "%;");
-				fptreef(shf, indent + INDENT, "then%N%T",
-				    t->left);
+				fptreef(shf, indent + INDENT, "%s%N%T",
+				    "then", t->left);
 			}
-			if (t->right == NULL || t->right->type != TELIF)
-				break;
-			t = t->right;
-			fptreef(shf, indent, "%;");
-			/* 5 == strlen("elif ") */
-			fptreef(shf, indent + 5, "elif %T", t->left);
-		}
+		} while (t->right && t->right->type == TELIF);
 		if (t->right != NULL) {
 			fptreef(shf, indent, "%;");
-			fptreef(shf, indent + INDENT, "else%N%T", t->right);
+			fptreef(shf, indent + INDENT, "%s%N%T",
+			    "else", t->right);
 		}
 		fptreef(shf, indent, "%;fi ");
 		break;
@@ -147,10 +159,10 @@ ptree(struct op *t, int indent, struct shf *shf)
 	case TUNTIL:
 		/* 6 == strlen("while"/"until") */
 		fptreef(shf, indent + 6, "%s %T",
-		    (t->type==TWHILE) ? "while" : "until",
+		    (t->type == TWHILE) ? "while" : "until",
 		    t->left);
-		fptreef(shf, indent, "%;do ");
-		fptreef(shf, indent + INDENT, "%T", t->right);
+		fptreef(shf, indent, "%;");
+		fptreef(shf, indent + INDENT, "do%N%T", t->right);
 		fptreef(shf, indent, "%;done ");
 		break;
 	case TBRACE:
@@ -159,9 +171,11 @@ ptree(struct op *t, int indent, struct shf *shf)
 		break;
 	case TCOPROC:
 		fptreef(shf, indent, "%T|& ", t->left);
+		prevent_semicolon = true;
 		break;
 	case TASYNC:
 		fptreef(shf, indent, "%T& ", t->left);
+		prevent_semicolon = true;
 		break;
 	case TFUNCT:
 		fpFUNCTf(shf, indent, t->u.ksh_func, t->str, t->left);
@@ -171,15 +185,17 @@ ptree(struct op *t, int indent, struct shf *shf)
 		break;
 	default:
 		shf_puts("<botch>", shf);
+		prevent_semicolon = false;
 		break;
 	}
 	if ((ioact = t->ioact) != NULL) {
-		int	need_nl = 0;
+		bool need_nl = false;
 
 		while (*ioact != NULL)
 			pioact(shf, indent, *ioact++);
 		/* Print here documents after everything else... */
-		for (ioact = t->ioact; *ioact != NULL; ) {
+		ioact = t->ioact;
+		while (*ioact != NULL) {
 			struct ioword *iop = *ioact++;
 
 			/* heredoc is 0 when tracing (set -x) */
@@ -190,12 +206,13 @@ ptree(struct op *t, int indent, struct shf *shf)
 				shf_puts(iop->heredoc, shf);
 				fptreef(shf, indent, "%s",
 				    evalstr(iop->delim, 0));
-				need_nl = 1;
+				need_nl = true;
 			}
 		}
-		/* Last delimiter must be followed by a newline (this often
-		 * leads to an extra blank line, but its not worth worrying
-		 * about)
+		/*
+		 * Last delimiter must be followed by a newline (this
+		 * often leads to an extra blank line, but it's not
+		 * worth worrying about)
 		 */
 		if (need_nl)
 			tputc('\n', shf);
@@ -218,60 +235,44 @@ pioact(struct shf *shf, int indent, struct ioword *iop)
 
 	switch (type) {
 	case IOREAD:
-		shf_puts("< ", shf);
+		shf_puts("<", shf);
 		break;
 	case IOHERE:
 		shf_puts(flag & IOSKIP ? "<<-" : "<<", shf);
 		break;
 	case IOCAT:
-		shf_puts(">> ", shf);
+		shf_puts(">>", shf);
 		break;
 	case IOWRITE:
-		shf_puts(flag & IOCLOB ? ">| " : "> ", shf);
+		shf_puts(flag & IOCLOB ? ">|" : ">", shf);
 		break;
 	case IORDWR:
-		shf_puts("<> ", shf);
+		shf_puts("<>", shf);
 		break;
 	case IODUP:
 		shf_puts(flag & IORDUP ? "<&" : ">&", shf);
 		break;
 	}
-	/* name/delim are 0 when printing syntax errors */
+	/* name/delim are NULL when printing syntax errors */
 	if (type == IOHERE) {
 		if (iop->delim)
-			fptreef(shf, indent, "%s%S ",
-			    /* here string */ iop->delim[1] == '<' ? "" : " ",
-			    iop->delim);
+			fptreef(shf, indent, "%S ", iop->delim);
 		else
 			tputc(' ', shf);
 	} else if (iop->name)
 		fptreef(shf, indent, (iop->flag & IONAMEXP) ? "%s " : "%S ",
 		    iop->name);
+	prevent_semicolon = false;
 }
 
-
-/*
- * variants of fputc, fputs for ptreef and snptreef
- */
+/* variant of fputs for ptreef */
 static void
-tputC(int c, struct shf *shf)
-{
-	if ((c&0x60) == 0) {		/* C0|C1 */
-		tputc((c&0x80) ? '$' : '^', shf);
-		tputc(((c&0x7F)|0x40), shf);
-	} else if ((c&0x7F) == 0x7F) {	/* DEL */
-		tputc((c&0x80) ? '$' : '^', shf);
-		tputc('?', shf);
-	} else
-		tputc(c, shf);
-}
-
-static void
-tputS(char *wp, struct shf *shf)
+tputS(const char *wp, struct shf *shf)
 {
 	int c, quotelevel = 0;
 
-	/* problems:
+	/*-
+	 * problems:
 	 *	`...` -> $(...)
 	 *	'foo' -> "foo"
 	 * could change encoding to:
@@ -284,25 +285,25 @@ tputS(char *wp, struct shf *shf)
 			return;
 		case ADELIM:
 		case CHAR:
-			tputC(*wp++, shf);
+			tputc(*wp++, shf);
 			break;
 		case QCHAR:
 			c = *wp++;
 			if (!quotelevel || (c == '"' || c == '`' || c == '$'))
 				tputc('\\', shf);
-			tputC(c, shf);
+			tputc(c, shf);
 			break;
 		case COMSUB:
 			shf_puts("$(", shf);
 			while (*wp != 0)
-				tputC(*wp++, shf);
+				tputc(*wp++, shf);
 			tputc(')', shf);
 			wp++;
 			break;
 		case EXPRSUB:
 			shf_puts("$((", shf);
 			while (*wp != 0)
-				tputC(*wp++, shf);
+				tputc(*wp++, shf);
 			shf_puts("))", shf);
 			wp++;
 			break;
@@ -320,7 +321,7 @@ tputS(char *wp, struct shf *shf)
 			if (*wp++ == '{')
 				tputc('{', shf);
 			while ((c = *wp++) != 0)
-				tputC(c, shf);
+				tputc(c, shf);
 			break;
 		case CSUBST:
 			if (*wp++ == '}')
@@ -344,16 +345,14 @@ tputS(char *wp, struct shf *shf)
  * variable args with an ANSI compiler
  */
 /* VARARGS */
-int
+void
 fptreef(struct shf *shf, int indent, const char *fmt, ...)
 {
 	va_list va;
 
 	va_start(va, fmt);
-
 	vfptreef(shf, indent, fmt, va);
 	va_end(va);
-	return (0);
 }
 
 /* VARARGS */
@@ -369,7 +368,8 @@ snptreef(char *s, int n, const char *fmt, ...)
 	vfptreef(&shf, 0, fmt, va);
 	va_end(va);
 
-	return (shf_sclose(&shf)); /* null terminates */
+	/* shf_sclose NUL terminates */
+	return (shf_sclose(&shf));
 }
 
 static void
@@ -381,40 +381,52 @@ vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
 		if (c == '%') {
 			switch ((c = *fmt++)) {
 			case 'c':
+				/* character (octet, probably) */
 				tputc(va_arg(va, int), shf);
 				break;
 			case 's':
+				/* string */
 				shf_puts(va_arg(va, char *), shf);
 				break;
-			case 'S':	/* word */
+			case 'S':
+				/* word */
 				tputS(va_arg(va, char *), shf);
 				break;
-			case 'd':	/* decimal */
+			case 'd':
+				/* signed decimal */
 				shf_fprintf(shf, "%d", va_arg(va, int));
 				break;
-			case 'u':	/* decimal */
+			case 'u':
+				/* unsigned decimal */
 				shf_fprintf(shf, "%u", va_arg(va, unsigned int));
 				break;
-			case 'T':	/* format tree */
+			case 'T':
+				/* format tree */
 				ptree(va_arg(va, struct op *), indent, shf);
-				break;
-			case ';':	/* newline or ; */
-			case 'N':	/* newline or space */
+				goto dont_trash_prevent_semicolon;
+			case ';':
+				/* newline or ; */
+			case 'N':
+				/* newline or space */
 				if (shf->flags & SHF_STRING) {
-					if (c == ';')
+					if (c == ';' && !prevent_semicolon)
 						tputc(';', shf);
 					tputc(' ', shf);
 				} else {
 					int i;
 
 					tputc('\n', shf);
-					for (i = indent; i >= 8; i -= 8)
+					i = indent;
+					while (i >= 8) {
 						tputc('\t', shf);
-					for (; i > 0; --i)
+						i -= 8;
+					}
+					while (i--)
 						tputc(' ', shf);
 				}
 				break;
 			case 'R':
+				/* I/O redirection */
 				pioact(shf, indent, va_arg(va, struct ioword *));
 				break;
 			default:
@@ -423,6 +435,9 @@ vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
 			}
 		} else
 			tputc(c, shf);
+		prevent_semicolon = false;
+ dont_trash_prevent_semicolon:
+		;
 	}
 }
 
@@ -452,11 +467,13 @@ tcopy(struct op *t, Area *ap)
 	if (t->vars == NULL)
 		r->vars = NULL;
 	else {
-		for (tw = (const char **)t->vars; *tw++ != NULL; )
-			;
+		tw = (const char **)t->vars;
+		while (*tw)
+			++tw;
 		rw = r->vars = alloc2(tw - (const char **)t->vars + 1,
 		    sizeof(*tw), ap);
-		for (tw = (const char **)t->vars; *tw != NULL; )
+		tw = (const char **)t->vars;
+		while (*tw)
 			*rw++ = wdcopy(*tw++, ap);
 		*rw = NULL;
 	}
@@ -464,11 +481,13 @@ tcopy(struct op *t, Area *ap)
 	if (t->args == NULL)
 		r->args = NULL;
 	else {
-		for (tw = t->args; *tw++ != NULL; )
-			;
+		tw = t->args;
+		while (*tw)
+			++tw;
 		r->args = (const char **)(rw = alloc2(tw - t->args + 1,
 		    sizeof(*tw), ap));
-		for (tw = t->args; *tw != NULL; )
+		tw = t->args;
+		while (*tw)
 			*rw++ = wdcopy(*tw++, ap);
 		*rw = NULL;
 	}
@@ -485,7 +504,9 @@ tcopy(struct op *t, Area *ap)
 char *
 wdcopy(const char *wp, Area *ap)
 {
-	size_t len = wdscan(wp, EOS) - wp;
+	size_t len;
+
+	len = wdscan(wp, EOS) - wp;
 	return (memcpy(alloc(len, ap), wp, len));
 }
 
@@ -544,9 +565,9 @@ wdscan(const char *wp, int c)
 		}
 }
 
-/* return a copy of wp without any of the mark up characters and
- * with quote characters (" ' \) stripped.
- * (string is allocated from ATEMP)
+/*
+ * return a copy of wp without any of the mark up characters and with
+ * quote characters (" ' \) stripped. (string is allocated from ATEMP)
  */
 char *
 wdstrip(const char *wp, bool keepq, bool make_magic)
@@ -556,7 +577,8 @@ wdstrip(const char *wp, bool keepq, bool make_magic)
 
 	shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
 
-	/* problems:
+	/*-
+	 * problems:
 	 *	`...` -> $(...)
 	 *	x${foo:-"hi"} -> x${foo:-hi}
 	 *	x${foo:-'hi'} -> x${foo:-hi} unless keepq
@@ -564,7 +586,8 @@ wdstrip(const char *wp, bool keepq, bool make_magic)
 	while (1)
 		switch (*wp++) {
 		case EOS:
-			return (shf_sclose(&shf)); /* null terminates */
+			/* shf_sclose NUL terminates */
+			return (shf_sclose(&shf));
 		case ADELIM:
 		case CHAR:
 			c = *wp++;
@@ -634,8 +657,9 @@ iocopy(struct ioword **iow, Area *ap)
 	struct ioword **ior;
 	int i;
 
-	for (ior = iow; *ior++ != NULL; )
-		;
+	ior = iow;
+	while (*ior)
+		++ior;
 	ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap);
 
 	for (i = 0; iow[i] != NULL; i++) {
@@ -678,8 +702,9 @@ tfree(struct op *t, Area *ap)
 	}
 
 	if (t->args != NULL) {
+		/*XXX we assume the caller is right */
 		union mksh_ccphack cw;
-		/* XXX we assume the caller is right */
+
 		cw.ro = t->args;
 		for (w = cw.rw; *w != NULL; w++)
 			afree(*w, ap);
@@ -701,7 +726,8 @@ iofree(struct ioword **iow, Area *ap)
 	struct ioword **iop;
 	struct ioword *p;
 
-	for (iop = iow; (p = *iop++) != NULL; ) {
+	iop = iow;
+	while ((p = *iop++) != NULL) {
 		if (p->name != NULL)
 			afree(p->name, ap);
 		if (p->delim != NULL)
@@ -713,11 +739,38 @@ iofree(struct ioword **iow, Area *ap)
 	afree(iow, ap);
 }
 
-int
+void
 fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
 {
 	if (isksh)
-		return (fptreef(shf, i, "%s %s %T", T_function, k, v));
+		fptreef(shf, i, "%s %s %T", T_function, k, v);
 	else
-		return (fptreef(shf, i, "%s() %T", k, v));
+		fptreef(shf, i, "%s() %T", k, v);
+}
+
+
+/* for jobs.c */
+void
+vistree(char *dst, size_t sz, struct op *t)
+{
+	int c;
+	char *cp, *buf;
+
+	buf = alloc(sz, ATEMP);
+	snptreef(buf, sz, "%T", t);
+	cp = buf;
+	while ((c = *cp++)) {
+		if (((c & 0x60) == 0) || ((c & 0x7F) == 0x7F)) {
+			/* C0 or C1 control character or DEL */
+			if (!--sz)
+				break;
+			*dst++ = (c & 0x80) ? '$' : '^';
+			c = (c & 0x7F) ^ 0x40;
+		}
+		if (!--sz)
+			break;
+		*dst++ = c;
+	}
+	*dst = '\0';
+	afree(buf, ATEMP);
 }