NetBSD Problem Report #48843

From jarmo.jaakkola@roskakori.fi  Wed May 28 23:08:59 2014
Return-Path: <jarmo.jaakkola@roskakori.fi>
Received: from mail.netbsd.org (mail.netbsd.org [149.20.53.66])
	(using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits))
	(Client CN "mail.netbsd.org", Issuer "Postmaster NetBSD.org" (verified OK))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 3C778A6516
	for <gnats-bugs@gnats.NetBSD.org>; Wed, 28 May 2014 23:08:59 +0000 (UTC)
Message-Id: <20140528230853.5CE54530F@roskakori.fi>
Date: Thu, 29 May 2014 02:08:53 +0300 (EEST)
From: Jarmo Jaakkola <jarmo.jaakkola@roskakori.fi>
Reply-To: Jarmo Jaakkola <jarmo.jaakkola@roskakori.fi>
To: gnats-bugs@gnats.NetBSD.org
Subject: sh(1): break/continue/return broken inside dot commands
X-Send-Pr-Version: 3.95

>Number:         48843
>Category:       bin
>Synopsis:       dot commands mess up scope nesting tracking
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed May 28 23:10:00 +0000 2014
>Closed-Date:    Mon Oct 31 05:33:09 +0000 2016
>Last-Modified:  Mon Oct 31 05:33:09 +0000 2016
>Originator:     Jarmo Jaakkola
>Release:        NetBSD 6.1.2_PATCH
>Organization:
>Environment:
System: NetBSD kotoisa.roskakori.fi 6.1.2_PATCH NetBSD 6.1.2_PATCH (KOTOISA) #5: Mon Jan 20 17:01:44 EET 2014 jammuli@kotoisa.roskakori.fi:/usr/src/sys/arch/amd64/compile/KOTOISA amd64
Architecture: x86_64
Machine: amd64
>Description:
Evaluation of commands goes completely haywire if a file containing
a break/continue/return command outside its "intended" scope is sourced
using a dot command inside its "intended" scope.  The main symptom is
not exiting from the sourced file when supposed to, leading to evaluation
of commands that were not supposed to be evaluated.  A secondary symptom
is that these extra commands are not evaluated correctly, as some of them
are skipped.  Some examples are listed in the How-To-Repeat section.

According to the POSIX standard, this is how it should work:
    dot:
        The shell shall execute commands from the file in the current
        environment.
    break:
        The break utility shall exit from the smallest enclosing for, while,
        or until loop, [...]
    continue:
        The continue utility shall return to the top of the smallest
        enclosing for, while, or until loop, [...]
    return:
        The return utility shall cause the shell to stop executing
        the current function or dot script.  If the shell is not currently
        executing a function or dot script, the results are unspecified.

It is clear that return should return from a sourced file, which
it does not do.  Whether break and continue should work from the sourced
file might be debatable.  Because the dot command says "in the current
environment", I'd say yes.  In any case, it should not fail in weird
ways like it does now!

The problems occur with return (a) and break/continue (b) because:
    1)  dotcmd() does not record the function nesting level prior to
        sourcing the file nor does it touch the loopnest variable,
        leading to either
    2   a) returncmd() being unable to detect that it should not set
           evalskip to SKIPFUNC but SKIPFILE, or
        b) breakcmd() setting evalskip to SKIPCONT or SKIPBREAK,
        leading to
    3)  cmdloop() not detecting that it should skip the rest of
        the file, due to only checking for SKIPFILE.
The result is that cmdloop() keeps executing lines from the file
whilst evalskip is set, which is the main symptom.  Because
evalskip is checked in multiple places in eval.c, the secondary
symptom appears.
>How-To-Repeat:
Run the following script:

    printf "break\necho break1; echo break2" >break
    printf "continue\necho continue1; echo continue2" >continue
    printf "return\necho return1; echo return2" >return

    while true; do . ./break; done

    for i in 1 2; do . ./continue; done

    func() {
        . ./return
    }
    func

No output should be produced, but instead this is the result:
    break1
    continue1
    continue1
    return1

The main symptom is evident from the unexpected output and the secondary
one from the fact that there are no lines with '2' in them.
>Fix:
Here is patch to src/bin/sh to fix the above problems.  It keeps
track of the function nesting level at the beginning of a dot command
to enable the return command to work properly.

I also changed the undefined-by-standard functionality of the return
command when it's not in a dot command or function from (indirectly)
exiting the shell to being silently ignored.  This was done because
the previous way has at least one bug: the shell exits without asking
for confirmation when there are stopped jobs.

Because I read the standard to mean that break and continue should have
an effect outside the sourced file, that's how I implemented it.  For what
it's worth, this also seems to be what bash does.  Also laziness, because
this way required no changes to loopnesting tracking.  If this is not
wanted, it might make sense to move the nesting tracking to the inputfile
stack.

The patch also does some clean-up to reduce the amount of global
variables by moving the dotcmd() and the find_dot_file() functions from
main.c to eval.c and making in_function() a proper function.

Index: bin/sh/eval.c
===================================================================
RCS file: /cvsroot/src/bin/sh/eval.c,v
retrieving revision 1.103
diff -u -p -u -r1.103 eval.c
--- bin/sh/eval.c	14 Nov 2011 18:24:45 -0000	1.103
+++ bin/sh/eval.c	28 May 2014 22:51:38 -0000
@@ -88,11 +88,20 @@ __RCSID("$NetBSD: eval.c,v 1.103 2011/11
 #define EV_TESTED 02		/* exit status is checked; ignore -e flag */
 #define EV_BACKCMD 04		/* command executing within back quotes */

-int evalskip;			/* set if we are skipping commands */
+STATIC enum skipstate evalskip;	/* != SKIPNONE if we are skipping commands */
 STATIC int skipcount;		/* number of levels to skip */
-MKINIT int loopnest;		/* current loop nesting level */
-int funcnest;			/* depth of function calls */
+STATIC int loopnest;		/* current loop nesting level */
+STATIC int funcnest;		/* depth of function calls */
 STATIC int builtin_flags;	/* evalcommand flags for builtins */
+/*
+ * Base function nesting level inside a dot command.  Set to 0 initially
+ * and to (funcnest + 1) before every dot command to enable 
+ *   1) detection of being in a file sourced by a dot command and
+ *   2) counting of function nesting in that file for the implementation
+ *      of the return command.
+ * The value is reset to its previous value after the dot command.
+ */
+STATIC int dot_funcnest;


 const char *commandname;
@@ -110,6 +119,7 @@ STATIC void evalpipe(union node *);
 STATIC void evalcommand(union node *, int, struct backcmd *);
 STATIC void prehash(union node *);

+STATIC char *find_dot_file(char *);

 /*
  * Called to reset things after an exception.
@@ -119,9 +129,7 @@ STATIC void prehash(union node *);
 INCLUDE "eval.h"

 RESET {
-	evalskip = 0;
-	loopnest = 0;
-	funcnest = 0;
+	reset_eval();
 }

 SHELLPROC {
@@ -129,6 +137,15 @@ SHELLPROC {
 }
 #endif

+void
+reset_eval(void)
+{
+	evalskip = SKIPNONE;
+	dot_funcnest = 0;
+	loopnest = 0;
+	funcnest = 0;
+}
+
 static int
 sh_pipe(int fds[2])
 {
@@ -326,11 +343,11 @@ evalloop(union node *n, int flags)
 		evaltree(n->nbinary.ch1, EV_TESTED);
 		if (evalskip) {
 skipping:	  if (evalskip == SKIPCONT && --skipcount <= 0) {
-				evalskip = 0;
+				evalskip = SKIPNONE;
 				continue;
 			}
 			if (evalskip == SKIPBREAK && --skipcount <= 0)
-				evalskip = 0;
+				evalskip = SKIPNONE;
 			break;
 		}
 		if (n->type == NWHILE) {
@@ -376,11 +393,11 @@ evalfor(union node *n, int flags)
 		status = exitstatus;
 		if (evalskip) {
 			if (evalskip == SKIPCONT && --skipcount <= 0) {
-				evalskip = 0;
+				evalskip = SKIPNONE;
 				continue;
 			}
 			if (evalskip == SKIPBREAK && --skipcount <= 0)
-				evalskip = 0;
+				evalskip = SKIPNONE;
 			break;
 		}
 	}
@@ -961,7 +978,7 @@ normal_fork:
 		popredir();
 		INTON;
 		if (evalskip == SKIPFUNC) {
-			evalskip = 0;
+			evalskip = SKIPNONE;
 			skipcount = 0;
 		}
 		if (flags & EV_EXIT)
@@ -1102,7 +1119,24 @@ prehash(union node *n)
 				     pathval());
 }

+STATIC int
+in_function(void)
+{
+	return funcnest;
+}
+
+STATIC enum skipstate
+current_skipstate(void)
+{
+	return evalskip;
+}

+STATIC void
+stop_skipping(void)
+{
+	evalskip = SKIPNONE;
+	skipcount = 0;
+}

 /*
  * Builtin commands.  Builtin commands whose functions are closely
@@ -1149,9 +1183,84 @@ breakcmd(int argc, char **argv)
 	return 0;
 }

+int
+dotcmd(int argc, char **argv)
+{
+	exitstatus = 0;
+
+	if (argc >= 2) {		/* That's what SVR2 does */
+		char *fullname;
+		/*
+		 * dot_funcnest needs to be 0 when not in a dotcmd, so it
+		 * cannot be restored with (funcnest + 1).
+		 */
+		int dot_funcnest_old;
+		struct stackmark smark;
+
+		setstackmark(&smark);
+		fullname = find_dot_file(argv[1]);
+		setinputfile(fullname, 1);
+		commandname = fullname;
+		dot_funcnest_old = dot_funcnest;
+		dot_funcnest = funcnest + 1;
+		cmdloop(0);
+		dot_funcnest = dot_funcnest_old;
+		popfile();
+		popstackmark(&smark);
+	}
+	return exitstatus;
+}
+
+/*
+ * Take commands from a file.  To be compatible we should do a path
+ * search for the file, which is necessary to find sub-commands.
+ */
+
+STATIC char *
+find_dot_file(char *basename)
+{
+	char *fullname;
+	const char *path = pathval();
+	struct stat statb;
+
+	/* don't try this for absolute or relative paths */
+	if (strchr(basename, '/'))
+		return basename;
+
+	while ((fullname = padvance(&path, basename)) != NULL) {
+		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
+			/*
+			 * Don't bother freeing here, since it will
+			 * be freed by the caller.
+			 */
+			return fullname;
+		}
+		stunalloc(fullname);
+	}
+
+	/* not found in the PATH */
+	error("%s: not found", basename);
+	/* NOTREACHED */
+}
+
+

 /*
  * The return command.
+ *
+ * Quoth the POSIX standard:
+ *   The return utility shall cause the shell to stop executing the current
+ *   function or dot script. If the shell is not currently executing
+ *   a function or dot script, the results are unspecified.
+ *
+ * As for the unspecified part, there seems to be no de-facto standard: bash
+ * ignores the return with a warning, zsh ignores the return in interactive
+ * mode but seems to liken it to exit in a script.  (checked May 2014)
+ *
+ * We choose to silently ignore the return.  Older versions of this shell
+ * set evalskip to SKIPFILE causing the shell to (indirectly) exit.  This
+ * had at least the problem of circumventing the check for stopped jobs,
+ * which would occur for exit or ^D.
  */

 int
@@ -1159,17 +1268,19 @@ returncmd(int argc, char **argv)
 {
 	int ret = argc > 1 ? number(argv[1]) : exitstatus;

-	if (funcnest) {
+	if ((dot_funcnest == 0 && funcnest)
+	    || (dot_funcnest > 0 && funcnest - (dot_funcnest - 1) > 0)) {
 		evalskip = SKIPFUNC;
 		skipcount = 1;
-		return ret;
-	}
-	else {
-		/* Do what ksh does; skip the rest of the file */
+	} else if (dot_funcnest > 0) {
 		evalskip = SKIPFILE;
 		skipcount = 1;
-		return ret;
+	} else {
+		/* XXX: should a warning be issued? */
+		ret = 0;
 	}
+
+	return ret;
 }


Index: bin/sh/eval.h
===================================================================
RCS file: /cvsroot/src/bin/sh/eval.h,v
retrieving revision 1.15
diff -u -p -u -r1.15 eval.h
--- bin/sh/eval.h	15 Feb 2008 17:26:06 -0000	1.15
+++ bin/sh/eval.h	28 May 2014 22:51:38 -0000
@@ -53,12 +53,21 @@ void evaltree(union node *, int);
 void evalbackcmd(union node *, struct backcmd *);

 /* in_function returns nonzero if we are currently evaluating a function */
-#define in_function()	funcnest
-extern int funcnest;
-extern int evalskip;
+int in_function(void);		/* return non-zero, if evaluating a function */

 /* reasons for skipping commands (see comment on breakcmd routine) */
-#define SKIPBREAK	1
-#define SKIPCONT	2
-#define SKIPFUNC	3
-#define SKIPFILE	4
+enum skipstate {
+      SKIPNONE  = 0,	/* not skipping */
+      SKIPBREAK,	/* break */
+      SKIPCONT,		/* continue */
+      SKIPFUNC,		/* return in a function */
+      SKIPFILE		/* return in a dot command */
+};
+
+enum skipstate current_skipstate(void);
+void stop_skipping(void);	/* reset internal skipping state to SKIPNONE */
+
+/*
+ * Only for use by reset() in init.c!
+ */
+void reset_eval(void);
Index: bin/sh/main.c
===================================================================
RCS file: /cvsroot/src/bin/sh/main.c,v
retrieving revision 1.57
diff -u -p -u -r1.57 main.c
--- bin/sh/main.c	18 Jun 2011 21:18:46 -0000	1.57
+++ bin/sh/main.c	28 May 2014 22:51:38 -0000
@@ -89,7 +89,6 @@ extern int etext();
 #endif

 STATIC void read_profile(const char *);
-STATIC char *find_dot_file(char *);
 int main(int, char **);

 /*
@@ -239,6 +238,7 @@ cmdloop(int top)
 	struct stackmark smark;
 	int inter;
 	int numeof = 0;
+	enum skipstate skip;

 	TRACE(("cmdloop(%d) called\n", top));
 	setstackmark(&smark);
@@ -270,8 +270,18 @@ cmdloop(int top)
 		}
 		popstackmark(&smark);
 		setstackmark(&smark);
-		if (evalskip == SKIPFILE) {
-			evalskip = 0;
+
+		/*
+		 * Any SKIP* can occur here!  SKIP(FUNC|BREAK|CONT) occur when
+		 * a dotcmd is in a loop or a function body and appropriate
+		 * built-ins occurs in file scope in the sourced file.  Values
+		 * other than SKIPFILE are reset by the appropriate eval*()
+		 * that contained the dotcmd() call.
+		 */
+		skip = current_skipstate();
+		if (skip != SKIPNONE) {
+			if (skip == SKIPFILE)
+				stop_skipping();
 			break;
 		}
 	}
@@ -337,60 +347,6 @@ readcmdfile(char *name)



-/*
- * Take commands from a file.  To be compatible we should do a path
- * search for the file, which is necessary to find sub-commands.
- */
-
-
-STATIC char *
-find_dot_file(char *basename)
-{
-	char *fullname;
-	const char *path = pathval();
-	struct stat statb;
-
-	/* don't try this for absolute or relative paths */
-	if (strchr(basename, '/'))
-		return basename;
-
-	while ((fullname = padvance(&path, basename)) != NULL) {
-		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
-			/*
-			 * Don't bother freeing here, since it will
-			 * be freed by the caller.
-			 */
-			return fullname;
-		}
-		stunalloc(fullname);
-	}
-
-	/* not found in the PATH */
-	error("%s: not found", basename);
-	/* NOTREACHED */
-}
-
-int
-dotcmd(int argc, char **argv)
-{
-	exitstatus = 0;
-
-	if (argc >= 2) {		/* That's what SVR2 does */
-		char *fullname;
-		struct stackmark smark;
-
-		setstackmark(&smark);
-		fullname = find_dot_file(argv[1]);
-		setinputfile(fullname, 1);
-		commandname = fullname;
-		cmdloop(0);
-		popfile();
-		popstackmark(&smark);
-	}
-	return exitstatus;
-}
-
-
 int
 exitcmd(int argc, char **argv)
 {
Index: bin/sh/sh.1
===================================================================
RCS file: /cvsroot/src/bin/sh/sh.1,v
retrieving revision 1.106
diff -u -p -u -r1.106 sh.1
--- bin/sh/sh.1	5 Oct 2011 13:15:30 -0000	1.106
+++ bin/sh/sh.1	28 May 2014 22:51:38 -0000
@@ -1179,10 +1179,23 @@ be built in for efficiency (e.g.
 .Xr test 1 ,
 etc).
 .Bl -tag -width 5n
-.It :
+.It : [ Ar arg ... ]
 A null command that returns a 0 (true) exit value.
+Any arguments are ignored.
 .It \&. file
-The commands in the specified file are read and executed by the shell.
+The dot command reads and executes the commands from the specified
+.Ar file
+in the current shell environment.
+The file does not need to be executable and is looked up from the directories
+listed in the
+.Ev PATH
+variable if it does not contain a directory separator
+.Pq Sq / .
+The return command can be used for a premature return from the sourced file.
+.Pp
+A non-obvious consequence of the file executing in the current environment
+is that loop control keywords (continue and break) can be used in the file
+to control loops surrounding the dot command.
 .It alias Op Ar name Ns Op Ar "=string ..."
 If
 .Ar name=string
@@ -1620,6 +1633,19 @@ With the
 .Fl p
 option specified the output will be formatted suitably for non-interactive use.
 .Pp
+.It return [ Ar n ]
+Stop executing the current function or a dot command with return value of
+.Ar n
+or the value of the last executed command, if not specified.
+For portability,
+.Ar n
+should be in the range from 0 to 255.
+.Pp
+The effects of using a return command outside a function or a dot command
+are not standardized.
+This implementation (currently) treats such a return as a no-op with
+a return value of 0 (success, true).
+Use the exit command if you want to return from a script or exit your shell.
 .It set Oo { Fl options | Cm +options | Cm \-- } Oc Ar arg ...
 The
 .Ic set

>Release-Note:

>Audit-Trail:
From: Jarmo Jaakkola <jarmo.jaakkola@roskakori.fi>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot
	commands
Date: Thu, 29 May 2014 03:03:40 +0300

 Because of this bug, the package devel/git is broken: PR pkg/48844.

 -- 
 Jarmo Jaakkola

From: Jarmo Jaakkola <jarmo.jaakkola@roskakori.fi>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot
	commands
Date: Thu, 29 May 2014 03:26:45 +0300

 Here's a bunch test cases.

 The test cases are named with the pattern
     dotscope_cmd_cmdscope,
 where dotscope is the scope in which the dot command is and cmdscope
 is the scope where cmd is in the sourced file.

 These cases fail for the current shell and are fixed by the original
 patch:
     t_dotcmd:for_break_case, t_dotcmd:for_break_file,
     t_dotcmd:for_break_func, t_dotcmd:for_continue_case,
     t_dotcmd:for_continue_file, t_dotcmd:for_continue_func,
     t_dotcmd:func_return_case, t_dotcmd:func_return_compound,
     t_dotcmd:func_return_file, t_dotcmd:func_return_for,
     t_dotcmd:func_return_until, t_dotcmd:func_return_while,
     t_dotcmd:until_break_case, t_dotcmd:until_break_file,
     t_dotcmd:until_break_func, t_dotcmd:until_continue_case,
     t_dotcmd:until_continue_file, t_dotcmd:until_continue_func,
     t_dotcmd:while_break_case, t_dotcmd:while_break_file,
     t_dotcmd:while_break_func, t_dotcmd:while_continue_case,
     t_dotcmd:while_continue_file, t_dotcmd:while_continue_func

 Index: etc/mtree/NetBSD.dist.tests
 ===================================================================
 RCS file: /cvsroot/src/etc/mtree/NetBSD.dist.tests,v
 retrieving revision 1.60
 diff -u -p -u -r1.60 NetBSD.dist.tests
 --- etc/mtree/NetBSD.dist.tests	13 Feb 2012 21:03:06 -0000	1.60
 +++ etc/mtree/NetBSD.dist.tests	28 May 2014 23:59:58 -0000
 @@ -128,6 +128,10 @@
  ./usr/tests/atf/atf-run
  ./usr/tests/atf/atf-sh
  ./usr/tests/atf/test-programs
 +./usr/tests/bin
 +./usr/tests/bin/sh
 +./usr/tests/bin/sh/dotcmd
 +./usr/tests/bin/sh/dotcmd/out
  ./usr/tests/crypto
  ./usr/tests/crypto/libcrypto
  ./usr/tests/dev
 diff -urN nonexistant/Makefile tests/bin/Makefile
 --- nonexistant/Makefile	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/Makefile	2014-05-27 14:45:36.000000000 +0300
 @@ -0,0 +1,10 @@
 +# $NetBSD$
 +#
 +
 +.include <bsd.own.mk>
 +
 +TESTSDIR=	${TESTSBASE}/bin
 +
 +TESTS_SUBDIRS+=	sh
 +
 +.include <bsd.test.mk>
 Binary files nonexistant/sh/.Makefile.swp and tests/bin/sh/.Makefile.swp differ
 diff -urN nonexistant/sh/Makefile tests/bin/sh/Makefile
 --- nonexistant/sh/Makefile	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/Makefile	2014-05-27 23:51:40.000000000 +0300
 @@ -0,0 +1,10 @@
 +# $NetBSD$
 +#
 +
 +.include <bsd.own.mk>
 +
 +TESTSDIR = ${TESTSBASE}/bin/sh
 +
 +TESTS_SUBDIRS += dotcmd
 +
 +.include <bsd.test.mk>
 Binary files nonexistant/sh/dotcmd/.Makefile.swp and tests/bin/sh/dotcmd/.Makefile.swp differ
 Binary files nonexistant/sh/dotcmd/.t_dotcmd.sh.swp and tests/bin/sh/dotcmd/.t_dotcmd.sh.swp differ
 diff -urN nonexistant/sh/dotcmd/Makefile tests/bin/sh/dotcmd/Makefile
 --- nonexistant/sh/dotcmd/Makefile	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/Makefile	2014-05-28 13:29:14.000000000 +0300
 @@ -0,0 +1,40 @@
 +# $NetBSD$
 +#
 +
 +.include <bsd.own.mk>
 +
 +TESTSDIR = ${TESTSBASE}/bin/sh/dotcmd
 +
 +TESTS_SH = t_dotcmd
 +
 +FILESDIR = ${TESTSDIR}/out
 +
 +# Testing scripts: dotcmd in various scopes includes a file with
 +# return / break / continue in various scopes.
 +#
 +.for cmd_scope in case compound file for func subshell until while
 +. for cmd in return break continue
 +FILES += ${cmd}_${cmd_scope}
 +FILESDIR_${cmd}_${cmd_scope} = ${TESTSDIR}
 +FILESBUILD_${cmd}_${cmd_scope} = yes
 +
 +${cmd}_${cmd_scope}: scoped_command
 +	${.CURDIR}/scoped_command '${cmd_scope}' '${cmd}' '${cmd}' \
 +		>'${.TARGET}'
 +
 +.  for dot_scope in case compound file for func subshell until while
 +FILES += \
 +	${dot_scope}_${cmd}_${cmd_scope} \
 +	out/${dot_scope}_${cmd}_${cmd_scope}.out
 +FILESDIR_${dot_scope}_${cmd}_${cmd_scope} = ${TESTSDIR}
 +FILESBUILD_${dot_scope}_${cmd}_${cmd_scope} = yes
 +FILESMODE_${dot_scope}_${cmd}_${cmd_scope} = ${BINMODE}
 +
 +${dot_scope}_${cmd}_${cmd_scope}: scoped_command
 +	${.CURDIR}/scoped_command '${dot_scope}' \
 +		'. "${cmd}_${cmd_scope}"' 'dotcmd' 'dotcmd' >'${.TARGET}'
 +.  endfor
 +. endfor
 +.endfor
 +
 +.include <bsd.test.mk>
 diff -urN nonexistant/sh/dotcmd/out/case_break_case.out tests/bin/sh/dotcmd/out/case_break_case.out
 --- nonexistant/sh/dotcmd/out/case_break_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_break_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +before case
 +before break
 +after break, return value: 0
 +after case
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_break_compound.out tests/bin/sh/dotcmd/out/case_break_compound.out
 --- nonexistant/sh/dotcmd/out/case_break_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_break_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +compound start
 +before break
 +after break, return value: 0
 +compound end
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_break_file.out tests/bin/sh/dotcmd/out/case_break_file.out
 --- nonexistant/sh/dotcmd/out/case_break_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_break_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before case
 +before dotcmd
 +before break
 +after break, return value: 0
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_break_for.out tests/bin/sh/dotcmd/out/case_break_for.out
 --- nonexistant/sh/dotcmd/out/case_break_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_break_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,7 @@
 +before case
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_break_func.out tests/bin/sh/dotcmd/out/case_break_func.out
 --- nonexistant/sh/dotcmd/out/case_break_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_break_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +before function
 +before break
 +after break
 +after function
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_break_subshell.out tests/bin/sh/dotcmd/out/case_break_subshell.out
 --- nonexistant/sh/dotcmd/out/case_break_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_break_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +subshell start
 +before break
 +after break, return value: 0
 +subshell end
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_break_until.out tests/bin/sh/dotcmd/out/case_break_until.out
 --- nonexistant/sh/dotcmd/out/case_break_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_break_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,7 @@
 +before case
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_break_while.out tests/bin/sh/dotcmd/out/case_break_while.out
 --- nonexistant/sh/dotcmd/out/case_break_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_break_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,7 @@
 +before case
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_continue_case.out tests/bin/sh/dotcmd/out/case_continue_case.out
 --- nonexistant/sh/dotcmd/out/case_continue_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_continue_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +before case
 +before continue
 +after continue, return value: 0
 +after case
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_continue_compound.out tests/bin/sh/dotcmd/out/case_continue_compound.out
 --- nonexistant/sh/dotcmd/out/case_continue_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_continue_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +compound start
 +before continue
 +after continue, return value: 0
 +compound end
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_continue_file.out tests/bin/sh/dotcmd/out/case_continue_file.out
 --- nonexistant/sh/dotcmd/out/case_continue_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_continue_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before case
 +before dotcmd
 +before continue
 +after continue, return value: 0
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_continue_for.out tests/bin/sh/dotcmd/out/case_continue_for.out
 --- nonexistant/sh/dotcmd/out/case_continue_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_continue_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_continue_func.out tests/bin/sh/dotcmd/out/case_continue_func.out
 --- nonexistant/sh/dotcmd/out/case_continue_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_continue_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +before function
 +before continue
 +after continue
 +after function
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_continue_subshell.out tests/bin/sh/dotcmd/out/case_continue_subshell.out
 --- nonexistant/sh/dotcmd/out/case_continue_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_continue_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +subshell start
 +before continue
 +after continue, return value: 0
 +subshell end
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_continue_until.out tests/bin/sh/dotcmd/out/case_continue_until.out
 --- nonexistant/sh/dotcmd/out/case_continue_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_continue_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_continue_while.out tests/bin/sh/dotcmd/out/case_continue_while.out
 --- nonexistant/sh/dotcmd/out/case_continue_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_continue_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before case
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_return_case.out tests/bin/sh/dotcmd/out/case_return_case.out
 --- nonexistant/sh/dotcmd/out/case_return_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_return_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before case
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_return_compound.out tests/bin/sh/dotcmd/out/case_return_compound.out
 --- nonexistant/sh/dotcmd/out/case_return_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_return_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before case
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_return_file.out tests/bin/sh/dotcmd/out/case_return_file.out
 --- nonexistant/sh/dotcmd/out/case_return_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_return_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,5 @@
 +before case
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_return_for.out tests/bin/sh/dotcmd/out/case_return_for.out
 --- nonexistant/sh/dotcmd/out/case_return_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_return_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before case
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_return_func.out tests/bin/sh/dotcmd/out/case_return_func.out
 --- nonexistant/sh/dotcmd/out/case_return_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_return_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,7 @@
 +before case
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_return_subshell.out tests/bin/sh/dotcmd/out/case_return_subshell.out
 --- nonexistant/sh/dotcmd/out/case_return_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_return_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before case
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_return_until.out tests/bin/sh/dotcmd/out/case_return_until.out
 --- nonexistant/sh/dotcmd/out/case_return_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_return_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before case
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/case_return_while.out tests/bin/sh/dotcmd/out/case_return_while.out
 --- nonexistant/sh/dotcmd/out/case_return_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/case_return_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before case
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 +after case
 diff -urN nonexistant/sh/dotcmd/out/compound_break_case.out tests/bin/sh/dotcmd/out/compound_break_case.out
 --- nonexistant/sh/dotcmd/out/compound_break_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_break_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +before case
 +before break
 +after break, return value: 0
 +after case
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_break_compound.out tests/bin/sh/dotcmd/out/compound_break_compound.out
 --- nonexistant/sh/dotcmd/out/compound_break_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_break_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +compound start
 +before break
 +after break, return value: 0
 +compound end
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_break_file.out tests/bin/sh/dotcmd/out/compound_break_file.out
 --- nonexistant/sh/dotcmd/out/compound_break_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_break_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +compound start
 +before dotcmd
 +before break
 +after break, return value: 0
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_break_for.out tests/bin/sh/dotcmd/out/compound_break_for.out
 --- nonexistant/sh/dotcmd/out/compound_break_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_break_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,7 @@
 +compound start
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_break_func.out tests/bin/sh/dotcmd/out/compound_break_func.out
 --- nonexistant/sh/dotcmd/out/compound_break_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_break_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +before function
 +before break
 +after break
 +after function
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_break_subshell.out tests/bin/sh/dotcmd/out/compound_break_subshell.out
 --- nonexistant/sh/dotcmd/out/compound_break_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_break_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +subshell start
 +before break
 +after break, return value: 0
 +subshell end
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_break_until.out tests/bin/sh/dotcmd/out/compound_break_until.out
 --- nonexistant/sh/dotcmd/out/compound_break_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_break_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,7 @@
 +compound start
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_break_while.out tests/bin/sh/dotcmd/out/compound_break_while.out
 --- nonexistant/sh/dotcmd/out/compound_break_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_break_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,7 @@
 +compound start
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_continue_case.out tests/bin/sh/dotcmd/out/compound_continue_case.out
 --- nonexistant/sh/dotcmd/out/compound_continue_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_continue_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +before case
 +before continue
 +after continue, return value: 0
 +after case
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_continue_compound.out tests/bin/sh/dotcmd/out/compound_continue_compound.out
 --- nonexistant/sh/dotcmd/out/compound_continue_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_continue_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +compound start
 +before continue
 +after continue, return value: 0
 +compound end
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_continue_file.out tests/bin/sh/dotcmd/out/compound_continue_file.out
 --- nonexistant/sh/dotcmd/out/compound_continue_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_continue_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +compound start
 +before dotcmd
 +before continue
 +after continue, return value: 0
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_continue_for.out tests/bin/sh/dotcmd/out/compound_continue_for.out
 --- nonexistant/sh/dotcmd/out/compound_continue_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_continue_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_continue_func.out tests/bin/sh/dotcmd/out/compound_continue_func.out
 --- nonexistant/sh/dotcmd/out/compound_continue_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_continue_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +before function
 +before continue
 +after continue
 +after function
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_continue_subshell.out tests/bin/sh/dotcmd/out/compound_continue_subshell.out
 --- nonexistant/sh/dotcmd/out/compound_continue_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_continue_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +subshell start
 +before continue
 +after continue, return value: 0
 +subshell end
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_continue_until.out tests/bin/sh/dotcmd/out/compound_continue_until.out
 --- nonexistant/sh/dotcmd/out/compound_continue_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_continue_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_continue_while.out tests/bin/sh/dotcmd/out/compound_continue_while.out
 --- nonexistant/sh/dotcmd/out/compound_continue_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_continue_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +compound start
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_return_case.out tests/bin/sh/dotcmd/out/compound_return_case.out
 --- nonexistant/sh/dotcmd/out/compound_return_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_return_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +compound start
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_return_compound.out tests/bin/sh/dotcmd/out/compound_return_compound.out
 --- nonexistant/sh/dotcmd/out/compound_return_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_return_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +compound start
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_return_file.out tests/bin/sh/dotcmd/out/compound_return_file.out
 --- nonexistant/sh/dotcmd/out/compound_return_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_return_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,5 @@
 +compound start
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_return_for.out tests/bin/sh/dotcmd/out/compound_return_for.out
 --- nonexistant/sh/dotcmd/out/compound_return_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_return_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +compound start
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_return_func.out tests/bin/sh/dotcmd/out/compound_return_func.out
 --- nonexistant/sh/dotcmd/out/compound_return_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_return_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,7 @@
 +compound start
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_return_subshell.out tests/bin/sh/dotcmd/out/compound_return_subshell.out
 --- nonexistant/sh/dotcmd/out/compound_return_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_return_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +compound start
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_return_until.out tests/bin/sh/dotcmd/out/compound_return_until.out
 --- nonexistant/sh/dotcmd/out/compound_return_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_return_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +compound start
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/compound_return_while.out tests/bin/sh/dotcmd/out/compound_return_while.out
 --- nonexistant/sh/dotcmd/out/compound_return_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/compound_return_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +compound start
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 +compound end
 diff -urN nonexistant/sh/dotcmd/out/file_break_case.out tests/bin/sh/dotcmd/out/file_break_case.out
 --- nonexistant/sh/dotcmd/out/file_break_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_break_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +before case
 +before break
 +after break, return value: 0
 +after case
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_break_compound.out tests/bin/sh/dotcmd/out/file_break_compound.out
 --- nonexistant/sh/dotcmd/out/file_break_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_break_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +compound start
 +before break
 +after break, return value: 0
 +compound end
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_break_file.out tests/bin/sh/dotcmd/out/file_break_file.out
 --- nonexistant/sh/dotcmd/out/file_break_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_break_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,4 @@
 +before dotcmd
 +before break
 +after break, return value: 0
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_break_for.out tests/bin/sh/dotcmd/out/file_break_for.out
 --- nonexistant/sh/dotcmd/out/file_break_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_break_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,5 @@
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_break_func.out tests/bin/sh/dotcmd/out/file_break_func.out
 --- nonexistant/sh/dotcmd/out/file_break_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_break_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +before function
 +before break
 +after break
 +after function
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_break_subshell.out tests/bin/sh/dotcmd/out/file_break_subshell.out
 --- nonexistant/sh/dotcmd/out/file_break_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_break_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +subshell start
 +before break
 +after break, return value: 0
 +subshell end
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_break_until.out tests/bin/sh/dotcmd/out/file_break_until.out
 --- nonexistant/sh/dotcmd/out/file_break_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_break_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,5 @@
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_break_while.out tests/bin/sh/dotcmd/out/file_break_while.out
 --- nonexistant/sh/dotcmd/out/file_break_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_break_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,5 @@
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_continue_case.out tests/bin/sh/dotcmd/out/file_continue_case.out
 --- nonexistant/sh/dotcmd/out/file_continue_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_continue_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +before case
 +before continue
 +after continue, return value: 0
 +after case
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_continue_compound.out tests/bin/sh/dotcmd/out/file_continue_compound.out
 --- nonexistant/sh/dotcmd/out/file_continue_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_continue_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +compound start
 +before continue
 +after continue, return value: 0
 +compound end
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_continue_file.out tests/bin/sh/dotcmd/out/file_continue_file.out
 --- nonexistant/sh/dotcmd/out/file_continue_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_continue_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,4 @@
 +before dotcmd
 +before continue
 +after continue, return value: 0
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_continue_for.out tests/bin/sh/dotcmd/out/file_continue_for.out
 --- nonexistant/sh/dotcmd/out/file_continue_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_continue_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_continue_func.out tests/bin/sh/dotcmd/out/file_continue_func.out
 --- nonexistant/sh/dotcmd/out/file_continue_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_continue_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +before function
 +before continue
 +after continue
 +after function
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_continue_subshell.out tests/bin/sh/dotcmd/out/file_continue_subshell.out
 --- nonexistant/sh/dotcmd/out/file_continue_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_continue_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +subshell start
 +before continue
 +after continue, return value: 0
 +subshell end
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_continue_until.out tests/bin/sh/dotcmd/out/file_continue_until.out
 --- nonexistant/sh/dotcmd/out/file_continue_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_continue_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_continue_while.out tests/bin/sh/dotcmd/out/file_continue_while.out
 --- nonexistant/sh/dotcmd/out/file_continue_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_continue_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_return_case.out tests/bin/sh/dotcmd/out/file_return_case.out
 --- nonexistant/sh/dotcmd/out/file_return_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_return_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,4 @@
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_return_compound.out tests/bin/sh/dotcmd/out/file_return_compound.out
 --- nonexistant/sh/dotcmd/out/file_return_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_return_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,4 @@
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_return_file.out tests/bin/sh/dotcmd/out/file_return_file.out
 --- nonexistant/sh/dotcmd/out/file_return_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_return_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,3 @@
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_return_for.out tests/bin/sh/dotcmd/out/file_return_for.out
 --- nonexistant/sh/dotcmd/out/file_return_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_return_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,4 @@
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_return_func.out tests/bin/sh/dotcmd/out/file_return_func.out
 --- nonexistant/sh/dotcmd/out/file_return_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_return_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,5 @@
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_return_subshell.out tests/bin/sh/dotcmd/out/file_return_subshell.out
 --- nonexistant/sh/dotcmd/out/file_return_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_return_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,4 @@
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_return_until.out tests/bin/sh/dotcmd/out/file_return_until.out
 --- nonexistant/sh/dotcmd/out/file_return_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_return_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,4 @@
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/file_return_while.out tests/bin/sh/dotcmd/out/file_return_while.out
 --- nonexistant/sh/dotcmd/out/file_return_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/file_return_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,4 @@
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 diff -urN nonexistant/sh/dotcmd/out/for_break_case.out tests/bin/sh/dotcmd/out/for_break_case.out
 --- nonexistant/sh/dotcmd/out/for_break_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_break_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,5 @@
 +before for
 +before dotcmd
 +before case
 +before break
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_break_compound.out tests/bin/sh/dotcmd/out/for_break_compound.out
 --- nonexistant/sh/dotcmd/out/for_break_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_break_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,5 @@
 +before for
 +before dotcmd
 +compound start
 +before break
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_break_file.out tests/bin/sh/dotcmd/out/for_break_file.out
 --- nonexistant/sh/dotcmd/out/for_break_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_break_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,4 @@
 +before for
 +before dotcmd
 +before break
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_break_for.out tests/bin/sh/dotcmd/out/for_break_for.out
 --- nonexistant/sh/dotcmd/out/for_break_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_break_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,12 @@
 +before for
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_break_func.out tests/bin/sh/dotcmd/out/for_break_func.out
 --- nonexistant/sh/dotcmd/out/for_break_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_break_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,5 @@
 +before for
 +before dotcmd
 +before function
 +before break
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_break_subshell.out tests/bin/sh/dotcmd/out/for_break_subshell.out
 --- nonexistant/sh/dotcmd/out/for_break_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_break_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,10 @@
 +before for
 +before dotcmd
 +subshell start
 +before break
 +after dotcmd, return value: 0
 +before dotcmd
 +subshell start
 +before break
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_break_until.out tests/bin/sh/dotcmd/out/for_break_until.out
 --- nonexistant/sh/dotcmd/out/for_break_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_break_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,12 @@
 +before for
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_break_while.out tests/bin/sh/dotcmd/out/for_break_while.out
 --- nonexistant/sh/dotcmd/out/for_break_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_break_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,12 @@
 +before for
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_continue_case.out tests/bin/sh/dotcmd/out/for_continue_case.out
 --- nonexistant/sh/dotcmd/out/for_continue_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_continue_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before for
 +before dotcmd
 +before case
 +before continue
 +before dotcmd
 +before case
 +before continue
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_continue_compound.out tests/bin/sh/dotcmd/out/for_continue_compound.out
 --- nonexistant/sh/dotcmd/out/for_continue_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_continue_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before for
 +before dotcmd
 +compound start
 +before continue
 +before dotcmd
 +compound start
 +before continue
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_continue_file.out tests/bin/sh/dotcmd/out/for_continue_file.out
 --- nonexistant/sh/dotcmd/out/for_continue_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_continue_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before for
 +before dotcmd
 +before continue
 +before dotcmd
 +before continue
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_continue_for.out tests/bin/sh/dotcmd/out/for_continue_for.out
 --- nonexistant/sh/dotcmd/out/for_continue_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_continue_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,14 @@
 +before for
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_continue_func.out tests/bin/sh/dotcmd/out/for_continue_func.out
 --- nonexistant/sh/dotcmd/out/for_continue_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_continue_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before for
 +before dotcmd
 +before function
 +before continue
 +before dotcmd
 +before function
 +before continue
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_continue_subshell.out tests/bin/sh/dotcmd/out/for_continue_subshell.out
 --- nonexistant/sh/dotcmd/out/for_continue_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_continue_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,10 @@
 +before for
 +before dotcmd
 +subshell start
 +before continue
 +after dotcmd, return value: 0
 +before dotcmd
 +subshell start
 +before continue
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_continue_until.out tests/bin/sh/dotcmd/out/for_continue_until.out
 --- nonexistant/sh/dotcmd/out/for_continue_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_continue_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,14 @@
 +before for
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_continue_while.out tests/bin/sh/dotcmd/out/for_continue_while.out
 --- nonexistant/sh/dotcmd/out/for_continue_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_continue_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,14 @@
 +before for
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_return_case.out tests/bin/sh/dotcmd/out/for_return_case.out
 --- nonexistant/sh/dotcmd/out/for_return_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_return_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,10 @@
 +before for
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_return_compound.out tests/bin/sh/dotcmd/out/for_return_compound.out
 --- nonexistant/sh/dotcmd/out/for_return_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_return_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,10 @@
 +before for
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_return_file.out tests/bin/sh/dotcmd/out/for_return_file.out
 --- nonexistant/sh/dotcmd/out/for_return_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_return_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before for
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_return_for.out tests/bin/sh/dotcmd/out/for_return_for.out
 --- nonexistant/sh/dotcmd/out/for_return_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_return_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,10 @@
 +before for
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_return_func.out tests/bin/sh/dotcmd/out/for_return_func.out
 --- nonexistant/sh/dotcmd/out/for_return_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_return_func.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,12 @@
 +before for
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_return_subshell.out tests/bin/sh/dotcmd/out/for_return_subshell.out
 --- nonexistant/sh/dotcmd/out/for_return_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_return_subshell.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,10 @@
 +before for
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_return_until.out tests/bin/sh/dotcmd/out/for_return_until.out
 --- nonexistant/sh/dotcmd/out/for_return_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_return_until.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,10 @@
 +before for
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/for_return_while.out tests/bin/sh/dotcmd/out/for_return_while.out
 --- nonexistant/sh/dotcmd/out/for_return_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/for_return_while.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,10 @@
 +before for
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 +after for
 diff -urN nonexistant/sh/dotcmd/out/func_break_case.out tests/bin/sh/dotcmd/out/func_break_case.out
 --- nonexistant/sh/dotcmd/out/func_break_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_break_case.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +before case
 +before break
 +after break, return value: 0
 +after case
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_break_compound.out tests/bin/sh/dotcmd/out/func_break_compound.out
 --- nonexistant/sh/dotcmd/out/func_break_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_break_compound.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +compound start
 +before break
 +after break, return value: 0
 +compound end
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_break_file.out tests/bin/sh/dotcmd/out/func_break_file.out
 --- nonexistant/sh/dotcmd/out/func_break_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_break_file.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,6 @@
 +before function
 +before dotcmd
 +before break
 +after break, return value: 0
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_break_for.out tests/bin/sh/dotcmd/out/func_break_for.out
 --- nonexistant/sh/dotcmd/out/func_break_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_break_for.out	2014-05-28 16:45:07.000000000 +0300
 @@ -0,0 +1,7 @@
 +before function
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_break_func.out tests/bin/sh/dotcmd/out/func_break_func.out
 --- nonexistant/sh/dotcmd/out/func_break_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_break_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +before function
 +before break
 +after break
 +after function
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_break_subshell.out tests/bin/sh/dotcmd/out/func_break_subshell.out
 --- nonexistant/sh/dotcmd/out/func_break_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_break_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +subshell start
 +before break
 +after break, return value: 0
 +subshell end
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_break_until.out tests/bin/sh/dotcmd/out/func_break_until.out
 --- nonexistant/sh/dotcmd/out/func_break_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_break_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,7 @@
 +before function
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_break_while.out tests/bin/sh/dotcmd/out/func_break_while.out
 --- nonexistant/sh/dotcmd/out/func_break_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_break_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,7 @@
 +before function
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_continue_case.out tests/bin/sh/dotcmd/out/func_continue_case.out
 --- nonexistant/sh/dotcmd/out/func_continue_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_continue_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +before case
 +before continue
 +after continue, return value: 0
 +after case
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_continue_compound.out tests/bin/sh/dotcmd/out/func_continue_compound.out
 --- nonexistant/sh/dotcmd/out/func_continue_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_continue_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +compound start
 +before continue
 +after continue, return value: 0
 +compound end
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_continue_file.out tests/bin/sh/dotcmd/out/func_continue_file.out
 --- nonexistant/sh/dotcmd/out/func_continue_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_continue_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +before function
 +before dotcmd
 +before continue
 +after continue, return value: 0
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_continue_for.out tests/bin/sh/dotcmd/out/func_continue_for.out
 --- nonexistant/sh/dotcmd/out/func_continue_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_continue_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_continue_func.out tests/bin/sh/dotcmd/out/func_continue_func.out
 --- nonexistant/sh/dotcmd/out/func_continue_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_continue_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +before function
 +before continue
 +after continue
 +after function
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_continue_subshell.out tests/bin/sh/dotcmd/out/func_continue_subshell.out
 --- nonexistant/sh/dotcmd/out/func_continue_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_continue_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +subshell start
 +before continue
 +after continue, return value: 0
 +subshell end
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_continue_until.out tests/bin/sh/dotcmd/out/func_continue_until.out
 --- nonexistant/sh/dotcmd/out/func_continue_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_continue_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_continue_while.out tests/bin/sh/dotcmd/out/func_continue_while.out
 --- nonexistant/sh/dotcmd/out/func_continue_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_continue_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before function
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_return_case.out tests/bin/sh/dotcmd/out/func_return_case.out
 --- nonexistant/sh/dotcmd/out/func_return_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_return_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +before function
 +before dotcmd
 +before case
 +before return
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_return_compound.out tests/bin/sh/dotcmd/out/func_return_compound.out
 --- nonexistant/sh/dotcmd/out/func_return_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_return_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +before function
 +before dotcmd
 +compound start
 +before return
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_return_file.out tests/bin/sh/dotcmd/out/func_return_file.out
 --- nonexistant/sh/dotcmd/out/func_return_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_return_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,5 @@
 +before function
 +before dotcmd
 +before return
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_return_for.out tests/bin/sh/dotcmd/out/func_return_for.out
 --- nonexistant/sh/dotcmd/out/func_return_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_return_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +before function
 +before dotcmd
 +before for
 +before return
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_return_func.out tests/bin/sh/dotcmd/out/func_return_func.out
 --- nonexistant/sh/dotcmd/out/func_return_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_return_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,7 @@
 +before function
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_return_subshell.out tests/bin/sh/dotcmd/out/func_return_subshell.out
 --- nonexistant/sh/dotcmd/out/func_return_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_return_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +before function
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_return_until.out tests/bin/sh/dotcmd/out/func_return_until.out
 --- nonexistant/sh/dotcmd/out/func_return_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_return_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +before function
 +before dotcmd
 +before until
 +before return
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/func_return_while.out tests/bin/sh/dotcmd/out/func_return_while.out
 --- nonexistant/sh/dotcmd/out/func_return_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/func_return_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +before function
 +before dotcmd
 +before while
 +before return
 +after dotcmd
 +after function
 diff -urN nonexistant/sh/dotcmd/out/subshell_break_case.out tests/bin/sh/dotcmd/out/subshell_break_case.out
 --- nonexistant/sh/dotcmd/out/subshell_break_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_break_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +before case
 +before break
 +after break, return value: 0
 +after case
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_break_compound.out tests/bin/sh/dotcmd/out/subshell_break_compound.out
 --- nonexistant/sh/dotcmd/out/subshell_break_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_break_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +compound start
 +before break
 +after break, return value: 0
 +compound end
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_break_file.out tests/bin/sh/dotcmd/out/subshell_break_file.out
 --- nonexistant/sh/dotcmd/out/subshell_break_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_break_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +subshell start
 +before dotcmd
 +before break
 +after break, return value: 0
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_break_for.out tests/bin/sh/dotcmd/out/subshell_break_for.out
 --- nonexistant/sh/dotcmd/out/subshell_break_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_break_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,7 @@
 +subshell start
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_break_func.out tests/bin/sh/dotcmd/out/subshell_break_func.out
 --- nonexistant/sh/dotcmd/out/subshell_break_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_break_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +before function
 +before break
 +after break
 +after function
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_break_subshell.out tests/bin/sh/dotcmd/out/subshell_break_subshell.out
 --- nonexistant/sh/dotcmd/out/subshell_break_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_break_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +subshell start
 +before break
 +after break, return value: 0
 +subshell end
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_break_until.out tests/bin/sh/dotcmd/out/subshell_break_until.out
 --- nonexistant/sh/dotcmd/out/subshell_break_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_break_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,7 @@
 +subshell start
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_break_while.out tests/bin/sh/dotcmd/out/subshell_break_while.out
 --- nonexistant/sh/dotcmd/out/subshell_break_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_break_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,7 @@
 +subshell start
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_continue_case.out tests/bin/sh/dotcmd/out/subshell_continue_case.out
 --- nonexistant/sh/dotcmd/out/subshell_continue_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_continue_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +before case
 +before continue
 +after continue, return value: 0
 +after case
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_continue_compound.out tests/bin/sh/dotcmd/out/subshell_continue_compound.out
 --- nonexistant/sh/dotcmd/out/subshell_continue_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_continue_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +compound start
 +before continue
 +after continue, return value: 0
 +compound end
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_continue_file.out tests/bin/sh/dotcmd/out/subshell_continue_file.out
 --- nonexistant/sh/dotcmd/out/subshell_continue_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_continue_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +subshell start
 +before dotcmd
 +before continue
 +after continue, return value: 0
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_continue_for.out tests/bin/sh/dotcmd/out/subshell_continue_for.out
 --- nonexistant/sh/dotcmd/out/subshell_continue_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_continue_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_continue_func.out tests/bin/sh/dotcmd/out/subshell_continue_func.out
 --- nonexistant/sh/dotcmd/out/subshell_continue_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_continue_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +before function
 +before continue
 +after continue
 +after function
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_continue_subshell.out tests/bin/sh/dotcmd/out/subshell_continue_subshell.out
 --- nonexistant/sh/dotcmd/out/subshell_continue_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_continue_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +subshell start
 +before continue
 +after continue, return value: 0
 +subshell end
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_continue_until.out tests/bin/sh/dotcmd/out/subshell_continue_until.out
 --- nonexistant/sh/dotcmd/out/subshell_continue_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_continue_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_continue_while.out tests/bin/sh/dotcmd/out/subshell_continue_while.out
 --- nonexistant/sh/dotcmd/out/subshell_continue_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_continue_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +subshell start
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_return_case.out tests/bin/sh/dotcmd/out/subshell_return_case.out
 --- nonexistant/sh/dotcmd/out/subshell_return_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_return_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +subshell start
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_return_compound.out tests/bin/sh/dotcmd/out/subshell_return_compound.out
 --- nonexistant/sh/dotcmd/out/subshell_return_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_return_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +subshell start
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_return_file.out tests/bin/sh/dotcmd/out/subshell_return_file.out
 --- nonexistant/sh/dotcmd/out/subshell_return_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_return_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,5 @@
 +subshell start
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_return_for.out tests/bin/sh/dotcmd/out/subshell_return_for.out
 --- nonexistant/sh/dotcmd/out/subshell_return_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_return_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +subshell start
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_return_func.out tests/bin/sh/dotcmd/out/subshell_return_func.out
 --- nonexistant/sh/dotcmd/out/subshell_return_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_return_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,7 @@
 +subshell start
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_return_subshell.out tests/bin/sh/dotcmd/out/subshell_return_subshell.out
 --- nonexistant/sh/dotcmd/out/subshell_return_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_return_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +subshell start
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_return_until.out tests/bin/sh/dotcmd/out/subshell_return_until.out
 --- nonexistant/sh/dotcmd/out/subshell_return_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_return_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +subshell start
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/subshell_return_while.out tests/bin/sh/dotcmd/out/subshell_return_while.out
 --- nonexistant/sh/dotcmd/out/subshell_return_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/subshell_return_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +subshell start
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 +subshell end
 diff -urN nonexistant/sh/dotcmd/out/until_break_case.out tests/bin/sh/dotcmd/out/until_break_case.out
 --- nonexistant/sh/dotcmd/out/until_break_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_break_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,5 @@
 +before until
 +before dotcmd
 +before case
 +before break
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_break_compound.out tests/bin/sh/dotcmd/out/until_break_compound.out
 --- nonexistant/sh/dotcmd/out/until_break_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_break_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,5 @@
 +before until
 +before dotcmd
 +compound start
 +before break
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_break_file.out tests/bin/sh/dotcmd/out/until_break_file.out
 --- nonexistant/sh/dotcmd/out/until_break_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_break_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,4 @@
 +before until
 +before dotcmd
 +before break
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_break_for.out tests/bin/sh/dotcmd/out/until_break_for.out
 --- nonexistant/sh/dotcmd/out/until_break_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_break_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,12 @@
 +before until
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_break_func.out tests/bin/sh/dotcmd/out/until_break_func.out
 --- nonexistant/sh/dotcmd/out/until_break_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_break_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,5 @@
 +before until
 +before dotcmd
 +before function
 +before break
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_break_subshell.out tests/bin/sh/dotcmd/out/until_break_subshell.out
 --- nonexistant/sh/dotcmd/out/until_break_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_break_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before until
 +before dotcmd
 +subshell start
 +before break
 +after dotcmd, return value: 0
 +before dotcmd
 +subshell start
 +before break
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_break_until.out tests/bin/sh/dotcmd/out/until_break_until.out
 --- nonexistant/sh/dotcmd/out/until_break_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_break_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,12 @@
 +before until
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_break_while.out tests/bin/sh/dotcmd/out/until_break_while.out
 --- nonexistant/sh/dotcmd/out/until_break_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_break_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,12 @@
 +before until
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_continue_case.out tests/bin/sh/dotcmd/out/until_continue_case.out
 --- nonexistant/sh/dotcmd/out/until_continue_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_continue_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before until
 +before dotcmd
 +before case
 +before continue
 +before dotcmd
 +before case
 +before continue
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_continue_compound.out tests/bin/sh/dotcmd/out/until_continue_compound.out
 --- nonexistant/sh/dotcmd/out/until_continue_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_continue_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before until
 +before dotcmd
 +compound start
 +before continue
 +before dotcmd
 +compound start
 +before continue
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_continue_file.out tests/bin/sh/dotcmd/out/until_continue_file.out
 --- nonexistant/sh/dotcmd/out/until_continue_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_continue_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +before until
 +before dotcmd
 +before continue
 +before dotcmd
 +before continue
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_continue_for.out tests/bin/sh/dotcmd/out/until_continue_for.out
 --- nonexistant/sh/dotcmd/out/until_continue_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_continue_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,14 @@
 +before until
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_continue_func.out tests/bin/sh/dotcmd/out/until_continue_func.out
 --- nonexistant/sh/dotcmd/out/until_continue_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_continue_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before until
 +before dotcmd
 +before function
 +before continue
 +before dotcmd
 +before function
 +before continue
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_continue_subshell.out tests/bin/sh/dotcmd/out/until_continue_subshell.out
 --- nonexistant/sh/dotcmd/out/until_continue_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_continue_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before until
 +before dotcmd
 +subshell start
 +before continue
 +after dotcmd, return value: 0
 +before dotcmd
 +subshell start
 +before continue
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_continue_until.out tests/bin/sh/dotcmd/out/until_continue_until.out
 --- nonexistant/sh/dotcmd/out/until_continue_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_continue_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,14 @@
 +before until
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_continue_while.out tests/bin/sh/dotcmd/out/until_continue_while.out
 --- nonexistant/sh/dotcmd/out/until_continue_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_continue_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,14 @@
 +before until
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_return_case.out tests/bin/sh/dotcmd/out/until_return_case.out
 --- nonexistant/sh/dotcmd/out/until_return_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_return_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before until
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_return_compound.out tests/bin/sh/dotcmd/out/until_return_compound.out
 --- nonexistant/sh/dotcmd/out/until_return_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_return_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before until
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_return_file.out tests/bin/sh/dotcmd/out/until_return_file.out
 --- nonexistant/sh/dotcmd/out/until_return_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_return_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before until
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_return_for.out tests/bin/sh/dotcmd/out/until_return_for.out
 --- nonexistant/sh/dotcmd/out/until_return_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_return_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before until
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_return_func.out tests/bin/sh/dotcmd/out/until_return_func.out
 --- nonexistant/sh/dotcmd/out/until_return_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_return_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,12 @@
 +before until
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_return_subshell.out tests/bin/sh/dotcmd/out/until_return_subshell.out
 --- nonexistant/sh/dotcmd/out/until_return_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_return_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before until
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_return_until.out tests/bin/sh/dotcmd/out/until_return_until.out
 --- nonexistant/sh/dotcmd/out/until_return_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_return_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before until
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/until_return_while.out tests/bin/sh/dotcmd/out/until_return_while.out
 --- nonexistant/sh/dotcmd/out/until_return_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/until_return_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before until
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 +after until
 diff -urN nonexistant/sh/dotcmd/out/while_break_case.out tests/bin/sh/dotcmd/out/while_break_case.out
 --- nonexistant/sh/dotcmd/out/while_break_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_break_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,5 @@
 +before while
 +before dotcmd
 +before case
 +before break
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_break_compound.out tests/bin/sh/dotcmd/out/while_break_compound.out
 --- nonexistant/sh/dotcmd/out/while_break_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_break_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,5 @@
 +before while
 +before dotcmd
 +compound start
 +before break
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_break_file.out tests/bin/sh/dotcmd/out/while_break_file.out
 --- nonexistant/sh/dotcmd/out/while_break_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_break_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,4 @@
 +before while
 +before dotcmd
 +before break
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_break_for.out tests/bin/sh/dotcmd/out/while_break_for.out
 --- nonexistant/sh/dotcmd/out/while_break_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_break_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,12 @@
 +before while
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 +before dotcmd
 +before for
 +before break
 +after for
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_break_func.out tests/bin/sh/dotcmd/out/while_break_func.out
 --- nonexistant/sh/dotcmd/out/while_break_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_break_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,5 @@
 +before while
 +before dotcmd
 +before function
 +before break
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_break_subshell.out tests/bin/sh/dotcmd/out/while_break_subshell.out
 --- nonexistant/sh/dotcmd/out/while_break_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_break_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before while
 +before dotcmd
 +subshell start
 +before break
 +after dotcmd, return value: 0
 +before dotcmd
 +subshell start
 +before break
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_break_until.out tests/bin/sh/dotcmd/out/while_break_until.out
 --- nonexistant/sh/dotcmd/out/while_break_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_break_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,12 @@
 +before while
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 +before dotcmd
 +before until
 +before break
 +after until
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_break_while.out tests/bin/sh/dotcmd/out/while_break_while.out
 --- nonexistant/sh/dotcmd/out/while_break_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_break_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,12 @@
 +before while
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 +before dotcmd
 +before while
 +before break
 +after while
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_continue_case.out tests/bin/sh/dotcmd/out/while_continue_case.out
 --- nonexistant/sh/dotcmd/out/while_continue_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_continue_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before while
 +before dotcmd
 +before case
 +before continue
 +before dotcmd
 +before case
 +before continue
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_continue_compound.out tests/bin/sh/dotcmd/out/while_continue_compound.out
 --- nonexistant/sh/dotcmd/out/while_continue_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_continue_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before while
 +before dotcmd
 +compound start
 +before continue
 +before dotcmd
 +compound start
 +before continue
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_continue_file.out tests/bin/sh/dotcmd/out/while_continue_file.out
 --- nonexistant/sh/dotcmd/out/while_continue_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_continue_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,6 @@
 +before while
 +before dotcmd
 +before continue
 +before dotcmd
 +before continue
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_continue_for.out tests/bin/sh/dotcmd/out/while_continue_for.out
 --- nonexistant/sh/dotcmd/out/while_continue_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_continue_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,14 @@
 +before while
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 +before dotcmd
 +before for
 +before continue
 +before continue
 +after for
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_continue_func.out tests/bin/sh/dotcmd/out/while_continue_func.out
 --- nonexistant/sh/dotcmd/out/while_continue_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_continue_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before while
 +before dotcmd
 +before function
 +before continue
 +before dotcmd
 +before function
 +before continue
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_continue_subshell.out tests/bin/sh/dotcmd/out/while_continue_subshell.out
 --- nonexistant/sh/dotcmd/out/while_continue_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_continue_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before while
 +before dotcmd
 +subshell start
 +before continue
 +after dotcmd, return value: 0
 +before dotcmd
 +subshell start
 +before continue
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_continue_until.out tests/bin/sh/dotcmd/out/while_continue_until.out
 --- nonexistant/sh/dotcmd/out/while_continue_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_continue_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,14 @@
 +before while
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 +before dotcmd
 +before until
 +before continue
 +before continue
 +after until
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_continue_while.out tests/bin/sh/dotcmd/out/while_continue_while.out
 --- nonexistant/sh/dotcmd/out/while_continue_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_continue_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,14 @@
 +before while
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 +before dotcmd
 +before while
 +before continue
 +before continue
 +after while
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_return_case.out tests/bin/sh/dotcmd/out/while_return_case.out
 --- nonexistant/sh/dotcmd/out/while_return_case.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_return_case.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before while
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before case
 +before return
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_return_compound.out tests/bin/sh/dotcmd/out/while_return_compound.out
 --- nonexistant/sh/dotcmd/out/while_return_compound.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_return_compound.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before while
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +compound start
 +before return
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_return_file.out tests/bin/sh/dotcmd/out/while_return_file.out
 --- nonexistant/sh/dotcmd/out/while_return_file.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_return_file.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,8 @@
 +before while
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before return
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_return_for.out tests/bin/sh/dotcmd/out/while_return_for.out
 --- nonexistant/sh/dotcmd/out/while_return_for.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_return_for.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before while
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before for
 +before return
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_return_func.out tests/bin/sh/dotcmd/out/while_return_func.out
 --- nonexistant/sh/dotcmd/out/while_return_func.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_return_func.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,12 @@
 +before while
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 +before dotcmd
 +before function
 +before return
 +after function
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_return_subshell.out tests/bin/sh/dotcmd/out/while_return_subshell.out
 --- nonexistant/sh/dotcmd/out/while_return_subshell.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_return_subshell.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before while
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +subshell start
 +before return
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_return_until.out tests/bin/sh/dotcmd/out/while_return_until.out
 --- nonexistant/sh/dotcmd/out/while_return_until.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_return_until.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before while
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before until
 +before return
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/out/while_return_while.out tests/bin/sh/dotcmd/out/while_return_while.out
 --- nonexistant/sh/dotcmd/out/while_return_while.out	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/out/while_return_while.out	2014-05-28 16:45:08.000000000 +0300
 @@ -0,0 +1,10 @@
 +before while
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 +before dotcmd
 +before while
 +before return
 +after dotcmd, return value: 0
 +after while
 diff -urN nonexistant/sh/dotcmd/scoped_command tests/bin/sh/dotcmd/scoped_command
 --- nonexistant/sh/dotcmd/scoped_command	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/scoped_command	2014-05-28 13:44:29.000000000 +0300
 @@ -0,0 +1,129 @@
 +#!/bin/sh
 +#
 +# $NetBSD$
 +#
 +# Copyright (c) 2014 The NetBSD Foundation, Inc.
 +# All rights reserved.
 +#
 +# This code is derived from software contributed to The NetBSD Foundation
 +# by Jarmo Jaakkola.
 +#
 +# Redistribution and use in source and binary forms, with or without
 +# modification, are permitted provided that the following conditions
 +# are met:
 +# 1. Redistributions of source code must retain the above copyright
 +#    notice, this list of conditions and the following disclaimer.
 +# 2. Redistributions in binary form must reproduce the above copyright
 +#    notice, this list of conditions and the following disclaimer in the
 +#    documentation and/or other materials provided with the distribution.
 +#
 +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 +# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 +# POSSIBILITY OF SUCH DAMAGE.
 +#
 +
 +set -e
 +
 +# USAGE:
 +#   scoped_command scope cmd msg var_suffix
 +#
 +# Write to stdout a piece of Bourne Shell script with _cmd_ in specific
 +# _scope_.  The execution of _cmd_ is bracketed by prints of "before _msg_"
 +# and "after _msg_, return value ${?}".  If the generated script uses
 +# variables, __var_suffix_ is appended to their names to allow nesting of
 +# scripts generated this way.
 +#
 +# _scope_ should be one of: case, compound, file, for, func, subshell,
 +# until, while.
 +# _cmd_ is the command line to execute.  Remember proper quoting!
 +# _msg_ is text that will be used inside single quotes.
 +# _var_suffix_ is a syntactically valid identifier name.
 +
 +# don't rely on command lists (';')
 +cmd="echo 'before ${3}'
 +${2}
 +echo 'after ${3}, return value:' ${?}"
 +
 +echo "#!/bin/sh"
 +
 +[ 'func' = "${1}" ] && cat <<EOF
 +func()
 +{
 +    echo 'before ${3}'
 +    \${1}
 +    echo 'after ${3}'
 +}
 +
 +echo 'before function'
 +func "${2}" "${3}"  # don't rely on 'shift'
 +echo 'after function'
 +EOF
 +
 +[ 'case' = "${1}" ] && cat <<EOF
 +echo 'before case'
 +case 'a' in
 +    a)  ${cmd};;
 +esac
 +echo 'after case'
 +EOF
 +
 +[ 'file' = "${1}" ] && cat <<EOF
 +${cmd}
 +EOF
 +
 +[ 'while' = "${1}" ] && cat <<EOF
 +echo 'before while'
 +cond_${4}='true true false'
 +while \${cond_${4}}
 +do
 +    cond_${4}="\${cond_${4}#* }"
 +    ${cmd}
 +done
 +echo 'after while'
 +EOF
 +
 +[ 'until' = "${1}" ] && cat <<EOF
 +echo 'before until'
 +cond_${4}='false false true'
 +until \${cond_${4}}
 +do
 +    cond_${4}="\${cond_${4}#* }"
 +    ${cmd}
 +done
 +echo 'after until'
 +EOF
 +
 +[ 'for' = "${1}" ] && cat <<EOF
 +echo 'before for'
 +for i_${4} in 1 2
 +do
 +    ${cmd}
 +done
 +echo 'after for'
 +EOF
 +
 +[ 'subshell' = "${1}" ] && cat <<EOF
 +(
 +    echo 'subshell start'
 +    ${cmd}
 +    echo 'subshell end'
 +)
 +EOF
 +
 +[ 'compound' = "${1}" ] && cat <<EOF
 +{
 +    echo 'compound start'
 +    ${cmd};
 +    echo 'compound end'
 +}
 +EOF
 +
 +exit 0
 diff -urN nonexistant/sh/dotcmd/t_dotcmd.sh tests/bin/sh/dotcmd/t_dotcmd.sh
 --- nonexistant/sh/dotcmd/t_dotcmd.sh	1970-01-01 02:00:00.000000000 +0200
 +++ tests/bin/sh/dotcmd/t_dotcmd.sh	2014-05-28 13:23:27.000000000 +0300
 @@ -0,0 +1,76 @@
 +# $NetBSD$
 +#
 +# Copyright (c) 2014 The NetBSD Foundation, Inc.
 +# All rights reserved.
 +#
 +# This code is derived from software contributed to The NetBSD Foundation
 +# by Jarmo Jaakkola.
 +#
 +# Redistribution and use in source and binary forms, with or without
 +# modification, are permitted provided that the following conditions
 +# are met:
 +# 1. Redistributions of source code must retain the above copyright
 +#    notice, this list of conditions and the following disclaimer.
 +# 2. Redistributions in binary form must reproduce the above copyright
 +#    notice, this list of conditions and the following disclaimer in the
 +#    documentation and/or other materials provided with the distribution.
 +#
 +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 +# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 +# POSSIBILITY OF SUCH DAMAGE.
 +#
 +
 +# Test loop and function flow control statements in various scopes in a file
 +# sourced by a dotcmd in various scopes.  Basically, dotcmd is like #include
 +# in C/C++ so, for example, if the dotcmd is in a loop's body, a break in
 +# the sourced file can be used to break out of that loop.
 +
 +cmds='return break continue'
 +scopes='case compound file for func subshell until while'
 +
 +case_ids=''
 +
 +for dot_scope in ${scopes}
 +do
 +    for cmd in ${cmds}
 +    do
 +        for cmd_scope in ${scopes}
 +        do
 +            case_id="${dot_scope}_${cmd}_${cmd_scope}"
 +	    case_ids="${case_ids} ${case_id}"
 +            atf_test_case "${case_id}"
 +            eval "
 +${case_id}_head()
 +{
 +    atf_set 'descr' \\
 +        'dotcmd in ${dot_scope}, file contains ${cmd} in ${cmd_scope}'
 +}
 +
 +${case_id}_body()
 +{
 +    srcdir=\$(atf_get_srcdir)
 +    # for dotcmd to find the sourced files
 +    PATH=\"\${PATH}:\${srcdir}\"
 +    atf_check -o file:\"\${srcdir}/out/${case_id}.out\" \\
 +            \"\${srcdir}/${case_id}\"
 +}
 +" # end eval
 +        done
 +    done
 +done
 +
 +atf_init_test_cases()
 +{
 +    for case_id in ${case_ids}
 +    do
 +        atf_add_test_case "${case_id}"
 +    done
 +}

From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/48843 CVS commit: src/tests/bin/sh
Date: Sat, 31 May 2014 10:29:07 -0400

 Module Name:	src
 Committed By:	christos
 Date:		Sat May 31 14:29:06 UTC 2014

 Modified Files:
 	src/tests/bin/sh: Makefile
 Added Files:
 	src/tests/bin/sh/dotcmd: Makefile scoped_command t_dotcmd.sh
 	src/tests/bin/sh/dotcmd/out: case_break_case.out
 	    case_break_compound.out case_break_file.out case_break_for.out
 	    case_break_func.out case_break_subshell.out case_break_until.out
 	    case_break_while.out case_continue_case.out
 	    case_continue_compound.out case_continue_file.out
 	    case_continue_for.out case_continue_func.out
 	    case_continue_subshell.out case_continue_until.out
 	    case_continue_while.out case_return_case.out
 	    case_return_compound.out case_return_file.out case_return_for.out
 	    case_return_func.out case_return_subshell.out case_return_until.out
 	    case_return_while.out compound_break_case.out
 	    compound_break_compound.out compound_break_file.out
 	    compound_break_for.out compound_break_func.out
 	    compound_break_subshell.out compound_break_until.out
 	    compound_break_while.out compound_continue_case.out
 	    compound_continue_compound.out compound_continue_file.out
 	    compound_continue_for.out compound_continue_func.out
 	    compound_continue_subshell.out compound_continue_until.out
 	    compound_continue_while.out compound_return_case.out
 	    compound_return_compound.out compound_return_file.out
 	    compound_return_for.out compound_return_func.out
 	    compound_return_subshell.out compound_return_until.out
 	    compound_return_while.out file_break_case.out
 	    file_break_compound.out file_break_file.out file_break_for.out
 	    file_break_func.out file_break_subshell.out file_break_until.out
 	    file_break_while.out file_continue_case.out
 	    file_continue_compound.out file_continue_file.out
 	    file_continue_for.out file_continue_func.out
 	    file_continue_subshell.out file_continue_until.out
 	    file_continue_while.out file_return_case.out
 	    file_return_compound.out file_return_file.out file_return_for.out
 	    file_return_func.out file_return_subshell.out file_return_until.out
 	    file_return_while.out for_break_case.out for_break_compound.out
 	    for_break_file.out for_break_for.out for_break_func.out
 	    for_break_subshell.out for_break_until.out for_break_while.out
 	    for_continue_case.out for_continue_compound.out
 	    for_continue_file.out for_continue_for.out for_continue_func.out
 	    for_continue_subshell.out for_continue_until.out
 	    for_continue_while.out for_return_case.out for_return_compound.out
 	    for_return_file.out for_return_for.out for_return_func.out
 	    for_return_subshell.out for_return_until.out for_return_while.out
 	    func_break_case.out func_break_compound.out func_break_file.out
 	    func_break_for.out func_break_func.out func_break_subshell.out
 	    func_break_until.out func_break_while.out func_continue_case.out
 	    func_continue_compound.out func_continue_file.out
 	    func_continue_for.out func_continue_func.out
 	    func_continue_subshell.out func_continue_until.out
 	    func_continue_while.out func_return_case.out
 	    func_return_compound.out func_return_file.out func_return_for.out
 	    func_return_func.out func_return_subshell.out func_return_until.out
 	    func_return_while.out subshell_break_case.out
 	    subshell_break_compound.out subshell_break_file.out
 	    subshell_break_for.out subshell_break_func.out
 	    subshell_break_subshell.out subshell_break_until.out
 	    subshell_break_while.out subshell_continue_case.out
 	    subshell_continue_compound.out subshell_continue_file.out
 	    subshell_continue_for.out subshell_continue_func.out
 	    subshell_continue_subshell.out subshell_continue_until.out
 	    subshell_continue_while.out subshell_return_case.out
 	    subshell_return_compound.out subshell_return_file.out
 	    subshell_return_for.out subshell_return_func.out
 	    subshell_return_subshell.out subshell_return_until.out
 	    subshell_return_while.out until_break_case.out
 	    until_break_compound.out until_break_file.out until_break_for.out
 	    until_break_func.out until_break_subshell.out until_break_until.out
 	    until_break_while.out until_continue_case.out
 	    until_continue_compound.out until_continue_file.out
 	    until_continue_for.out until_continue_func.out
 	    until_continue_subshell.out until_continue_until.out
 	    until_continue_while.out until_return_case.out
 	    until_return_compound.out until_return_file.out
 	    until_return_for.out until_return_func.out
 	    until_return_subshell.out until_return_until.out
 	    until_return_while.out while_break_case.out
 	    while_break_compound.out while_break_file.out while_break_for.out
 	    while_break_func.out while_break_subshell.out while_break_until.out
 	    while_break_while.out while_continue_case.out
 	    while_continue_compound.out while_continue_file.out
 	    while_continue_for.out while_continue_func.out
 	    while_continue_subshell.out while_continue_until.out
 	    while_continue_while.out while_return_case.out
 	    while_return_compound.out while_return_file.out
 	    while_return_for.out while_return_func.out
 	    while_return_subshell.out while_return_until.out
 	    while_return_while.out

 Log Message:
 PR/48843: Jarmo Jaakkola: Test cses for break/continue/return broken
 inside dot commands:

  The test cases are named with the pattern
      dotscope_cmd_cmdscope,
  where dotscope is the scope in which the dot command is and cmdscope
  is the scope where cmd is in the sourced file.


 To generate a diff of this commit:
 cvs rdiff -u -r1.2 -r1.3 src/tests/bin/sh/Makefile
 cvs rdiff -u -r0 -r1.1 src/tests/bin/sh/dotcmd/Makefile \
     src/tests/bin/sh/dotcmd/scoped_command \
     src/tests/bin/sh/dotcmd/t_dotcmd.sh
 cvs rdiff -u -r0 -r1.1 src/tests/bin/sh/dotcmd/out/case_break_case.out \
     src/tests/bin/sh/dotcmd/out/case_break_compound.out \
     src/tests/bin/sh/dotcmd/out/case_break_file.out \
     src/tests/bin/sh/dotcmd/out/case_break_for.out \
     src/tests/bin/sh/dotcmd/out/case_break_func.out \
     src/tests/bin/sh/dotcmd/out/case_break_subshell.out \
     src/tests/bin/sh/dotcmd/out/case_break_until.out \
     src/tests/bin/sh/dotcmd/out/case_break_while.out \
     src/tests/bin/sh/dotcmd/out/case_continue_case.out \
     src/tests/bin/sh/dotcmd/out/case_continue_compound.out \
     src/tests/bin/sh/dotcmd/out/case_continue_file.out \
     src/tests/bin/sh/dotcmd/out/case_continue_for.out \
     src/tests/bin/sh/dotcmd/out/case_continue_func.out \
     src/tests/bin/sh/dotcmd/out/case_continue_subshell.out \
     src/tests/bin/sh/dotcmd/out/case_continue_until.out \
     src/tests/bin/sh/dotcmd/out/case_continue_while.out \
     src/tests/bin/sh/dotcmd/out/case_return_case.out \
     src/tests/bin/sh/dotcmd/out/case_return_compound.out \
     src/tests/bin/sh/dotcmd/out/case_return_file.out \
     src/tests/bin/sh/dotcmd/out/case_return_for.out \
     src/tests/bin/sh/dotcmd/out/case_return_func.out \
     src/tests/bin/sh/dotcmd/out/case_return_subshell.out \
     src/tests/bin/sh/dotcmd/out/case_return_until.out \
     src/tests/bin/sh/dotcmd/out/case_return_while.out \
     src/tests/bin/sh/dotcmd/out/compound_break_case.out \
     src/tests/bin/sh/dotcmd/out/compound_break_compound.out \
     src/tests/bin/sh/dotcmd/out/compound_break_file.out \
     src/tests/bin/sh/dotcmd/out/compound_break_for.out \
     src/tests/bin/sh/dotcmd/out/compound_break_func.out \
     src/tests/bin/sh/dotcmd/out/compound_break_subshell.out \
     src/tests/bin/sh/dotcmd/out/compound_break_until.out \
     src/tests/bin/sh/dotcmd/out/compound_break_while.out \
     src/tests/bin/sh/dotcmd/out/compound_continue_case.out \
     src/tests/bin/sh/dotcmd/out/compound_continue_compound.out \
     src/tests/bin/sh/dotcmd/out/compound_continue_file.out \
     src/tests/bin/sh/dotcmd/out/compound_continue_for.out \
     src/tests/bin/sh/dotcmd/out/compound_continue_func.out \
     src/tests/bin/sh/dotcmd/out/compound_continue_subshell.out \
     src/tests/bin/sh/dotcmd/out/compound_continue_until.out \
     src/tests/bin/sh/dotcmd/out/compound_continue_while.out \
     src/tests/bin/sh/dotcmd/out/compound_return_case.out \
     src/tests/bin/sh/dotcmd/out/compound_return_compound.out \
     src/tests/bin/sh/dotcmd/out/compound_return_file.out \
     src/tests/bin/sh/dotcmd/out/compound_return_for.out \
     src/tests/bin/sh/dotcmd/out/compound_return_func.out \
     src/tests/bin/sh/dotcmd/out/compound_return_subshell.out \
     src/tests/bin/sh/dotcmd/out/compound_return_until.out \
     src/tests/bin/sh/dotcmd/out/compound_return_while.out \
     src/tests/bin/sh/dotcmd/out/file_break_case.out \
     src/tests/bin/sh/dotcmd/out/file_break_compound.out \
     src/tests/bin/sh/dotcmd/out/file_break_file.out \
     src/tests/bin/sh/dotcmd/out/file_break_for.out \
     src/tests/bin/sh/dotcmd/out/file_break_func.out \
     src/tests/bin/sh/dotcmd/out/file_break_subshell.out \
     src/tests/bin/sh/dotcmd/out/file_break_until.out \
     src/tests/bin/sh/dotcmd/out/file_break_while.out \
     src/tests/bin/sh/dotcmd/out/file_continue_case.out \
     src/tests/bin/sh/dotcmd/out/file_continue_compound.out \
     src/tests/bin/sh/dotcmd/out/file_continue_file.out \
     src/tests/bin/sh/dotcmd/out/file_continue_for.out \
     src/tests/bin/sh/dotcmd/out/file_continue_func.out \
     src/tests/bin/sh/dotcmd/out/file_continue_subshell.out \
     src/tests/bin/sh/dotcmd/out/file_continue_until.out \
     src/tests/bin/sh/dotcmd/out/file_continue_while.out \
     src/tests/bin/sh/dotcmd/out/file_return_case.out \
     src/tests/bin/sh/dotcmd/out/file_return_compound.out \
     src/tests/bin/sh/dotcmd/out/file_return_file.out \
     src/tests/bin/sh/dotcmd/out/file_return_for.out \
     src/tests/bin/sh/dotcmd/out/file_return_func.out \
     src/tests/bin/sh/dotcmd/out/file_return_subshell.out \
     src/tests/bin/sh/dotcmd/out/file_return_until.out \
     src/tests/bin/sh/dotcmd/out/file_return_while.out \
     src/tests/bin/sh/dotcmd/out/for_break_case.out \
     src/tests/bin/sh/dotcmd/out/for_break_compound.out \
     src/tests/bin/sh/dotcmd/out/for_break_file.out \
     src/tests/bin/sh/dotcmd/out/for_break_for.out \
     src/tests/bin/sh/dotcmd/out/for_break_func.out \
     src/tests/bin/sh/dotcmd/out/for_break_subshell.out \
     src/tests/bin/sh/dotcmd/out/for_break_until.out \
     src/tests/bin/sh/dotcmd/out/for_break_while.out \
     src/tests/bin/sh/dotcmd/out/for_continue_case.out \
     src/tests/bin/sh/dotcmd/out/for_continue_compound.out \
     src/tests/bin/sh/dotcmd/out/for_continue_file.out \
     src/tests/bin/sh/dotcmd/out/for_continue_for.out \
     src/tests/bin/sh/dotcmd/out/for_continue_func.out \
     src/tests/bin/sh/dotcmd/out/for_continue_subshell.out \
     src/tests/bin/sh/dotcmd/out/for_continue_until.out \
     src/tests/bin/sh/dotcmd/out/for_continue_while.out \
     src/tests/bin/sh/dotcmd/out/for_return_case.out \
     src/tests/bin/sh/dotcmd/out/for_return_compound.out \
     src/tests/bin/sh/dotcmd/out/for_return_file.out \
     src/tests/bin/sh/dotcmd/out/for_return_for.out \
     src/tests/bin/sh/dotcmd/out/for_return_func.out \
     src/tests/bin/sh/dotcmd/out/for_return_subshell.out \
     src/tests/bin/sh/dotcmd/out/for_return_until.out \
     src/tests/bin/sh/dotcmd/out/for_return_while.out \
     src/tests/bin/sh/dotcmd/out/func_break_case.out \
     src/tests/bin/sh/dotcmd/out/func_break_compound.out \
     src/tests/bin/sh/dotcmd/out/func_break_file.out \
     src/tests/bin/sh/dotcmd/out/func_break_for.out \
     src/tests/bin/sh/dotcmd/out/func_break_func.out \
     src/tests/bin/sh/dotcmd/out/func_break_subshell.out \
     src/tests/bin/sh/dotcmd/out/func_break_until.out \
     src/tests/bin/sh/dotcmd/out/func_break_while.out \
     src/tests/bin/sh/dotcmd/out/func_continue_case.out \
     src/tests/bin/sh/dotcmd/out/func_continue_compound.out \
     src/tests/bin/sh/dotcmd/out/func_continue_file.out \
     src/tests/bin/sh/dotcmd/out/func_continue_for.out \
     src/tests/bin/sh/dotcmd/out/func_continue_func.out \
     src/tests/bin/sh/dotcmd/out/func_continue_subshell.out \
     src/tests/bin/sh/dotcmd/out/func_continue_until.out \
     src/tests/bin/sh/dotcmd/out/func_continue_while.out \
     src/tests/bin/sh/dotcmd/out/func_return_case.out \
     src/tests/bin/sh/dotcmd/out/func_return_compound.out \
     src/tests/bin/sh/dotcmd/out/func_return_file.out \
     src/tests/bin/sh/dotcmd/out/func_return_for.out \
     src/tests/bin/sh/dotcmd/out/func_return_func.out \
     src/tests/bin/sh/dotcmd/out/func_return_subshell.out \
     src/tests/bin/sh/dotcmd/out/func_return_until.out \
     src/tests/bin/sh/dotcmd/out/func_return_while.out \
     src/tests/bin/sh/dotcmd/out/subshell_break_case.out \
     src/tests/bin/sh/dotcmd/out/subshell_break_compound.out \
     src/tests/bin/sh/dotcmd/out/subshell_break_file.out \
     src/tests/bin/sh/dotcmd/out/subshell_break_for.out \
     src/tests/bin/sh/dotcmd/out/subshell_break_func.out \
     src/tests/bin/sh/dotcmd/out/subshell_break_subshell.out \
     src/tests/bin/sh/dotcmd/out/subshell_break_until.out \
     src/tests/bin/sh/dotcmd/out/subshell_break_while.out \
     src/tests/bin/sh/dotcmd/out/subshell_continue_case.out \
     src/tests/bin/sh/dotcmd/out/subshell_continue_compound.out \
     src/tests/bin/sh/dotcmd/out/subshell_continue_file.out \
     src/tests/bin/sh/dotcmd/out/subshell_continue_for.out \
     src/tests/bin/sh/dotcmd/out/subshell_continue_func.out \
     src/tests/bin/sh/dotcmd/out/subshell_continue_subshell.out \
     src/tests/bin/sh/dotcmd/out/subshell_continue_until.out \
     src/tests/bin/sh/dotcmd/out/subshell_continue_while.out \
     src/tests/bin/sh/dotcmd/out/subshell_return_case.out \
     src/tests/bin/sh/dotcmd/out/subshell_return_compound.out \
     src/tests/bin/sh/dotcmd/out/subshell_return_file.out \
     src/tests/bin/sh/dotcmd/out/subshell_return_for.out \
     src/tests/bin/sh/dotcmd/out/subshell_return_func.out \
     src/tests/bin/sh/dotcmd/out/subshell_return_subshell.out \
     src/tests/bin/sh/dotcmd/out/subshell_return_until.out \
     src/tests/bin/sh/dotcmd/out/subshell_return_while.out \
     src/tests/bin/sh/dotcmd/out/until_break_case.out \
     src/tests/bin/sh/dotcmd/out/until_break_compound.out \
     src/tests/bin/sh/dotcmd/out/until_break_file.out \
     src/tests/bin/sh/dotcmd/out/until_break_for.out \
     src/tests/bin/sh/dotcmd/out/until_break_func.out \
     src/tests/bin/sh/dotcmd/out/until_break_subshell.out \
     src/tests/bin/sh/dotcmd/out/until_break_until.out \
     src/tests/bin/sh/dotcmd/out/until_break_while.out \
     src/tests/bin/sh/dotcmd/out/until_continue_case.out \
     src/tests/bin/sh/dotcmd/out/until_continue_compound.out \
     src/tests/bin/sh/dotcmd/out/until_continue_file.out \
     src/tests/bin/sh/dotcmd/out/until_continue_for.out \
     src/tests/bin/sh/dotcmd/out/until_continue_func.out \
     src/tests/bin/sh/dotcmd/out/until_continue_subshell.out \
     src/tests/bin/sh/dotcmd/out/until_continue_until.out \
     src/tests/bin/sh/dotcmd/out/until_continue_while.out \
     src/tests/bin/sh/dotcmd/out/until_return_case.out \
     src/tests/bin/sh/dotcmd/out/until_return_compound.out \
     src/tests/bin/sh/dotcmd/out/until_return_file.out \
     src/tests/bin/sh/dotcmd/out/until_return_for.out \
     src/tests/bin/sh/dotcmd/out/until_return_func.out \
     src/tests/bin/sh/dotcmd/out/until_return_subshell.out \
     src/tests/bin/sh/dotcmd/out/until_return_until.out \
     src/tests/bin/sh/dotcmd/out/until_return_while.out \
     src/tests/bin/sh/dotcmd/out/while_break_case.out \
     src/tests/bin/sh/dotcmd/out/while_break_compound.out \
     src/tests/bin/sh/dotcmd/out/while_break_file.out \
     src/tests/bin/sh/dotcmd/out/while_break_for.out \
     src/tests/bin/sh/dotcmd/out/while_break_func.out \
     src/tests/bin/sh/dotcmd/out/while_break_subshell.out \
     src/tests/bin/sh/dotcmd/out/while_break_until.out \
     src/tests/bin/sh/dotcmd/out/while_break_while.out \
     src/tests/bin/sh/dotcmd/out/while_continue_case.out \
     src/tests/bin/sh/dotcmd/out/while_continue_compound.out \
     src/tests/bin/sh/dotcmd/out/while_continue_file.out \
     src/tests/bin/sh/dotcmd/out/while_continue_for.out \
     src/tests/bin/sh/dotcmd/out/while_continue_func.out \
     src/tests/bin/sh/dotcmd/out/while_continue_subshell.out \
     src/tests/bin/sh/dotcmd/out/while_continue_until.out \
     src/tests/bin/sh/dotcmd/out/while_continue_while.out \
     src/tests/bin/sh/dotcmd/out/while_return_case.out \
     src/tests/bin/sh/dotcmd/out/while_return_compound.out \
     src/tests/bin/sh/dotcmd/out/while_return_file.out \
     src/tests/bin/sh/dotcmd/out/while_return_for.out \
     src/tests/bin/sh/dotcmd/out/while_return_func.out \
     src/tests/bin/sh/dotcmd/out/while_return_subshell.out \
     src/tests/bin/sh/dotcmd/out/while_return_until.out \
     src/tests/bin/sh/dotcmd/out/while_return_while.out

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/48843 CVS commit: src/bin/sh
Date: Sat, 31 May 2014 10:42:19 -0400

 Module Name:	src
 Committed By:	christos
 Date:		Sat May 31 14:42:18 UTC 2014

 Modified Files:
 	src/bin/sh: eval.c eval.h main.c sh.1

 Log Message:
 PR/48843: Jarmo Jaakkola: dot commands mess up scope nesting tracking

 Evaluation of commands goes completely haywire if a file containing
 a break/continue/return command outside its "intended" scope is sourced
 using a dot command inside its "intended" scope.  The main symptom is
 not exiting from the sourced file when supposed to, leading to evaluation
 of commands that were not supposed to be evaluated.  A secondary symptom
 is that these extra commands are not evaluated correctly, as some of them
 are skipped.  Some examples are listed in the How-To-Repeat section.

 According to the POSIX standard, this is how it should work:
     dot:
         The shell shall execute commands from the file in the current
         environment.
     break:
         The break utility shall exit from the smallest enclosing for, while,
         or until loop, [...]
     continue:
         The continue utility shall return to the top of the smallest
         enclosing for, while, or until loop, [...]
     return:
         The return utility shall cause the shell to stop executing
         the current function or dot script.  If the shell is not currently
         executing a function or dot script, the results are unspecified.

 It is clear that return should return from a sourced file, which
 it does not do.  Whether break and continue should work from the sourced
 file might be debatable.  Because the dot command says "in the current
 environment", I'd say yes.  In any case, it should not fail in weird
 ways like it does now!

 The problems occur with return (a) and break/continue (b) because:
     1)  dotcmd() does not record the function nesting level prior to
         sourcing the file nor does it touch the loopnest variable,
         leading to either
     2   a) returncmd() being unable to detect that it should not set
            evalskip to SKIPFUNC but SKIPFILE, or
         b) breakcmd() setting evalskip to SKIPCONT or SKIPBREAK,
         leading to
     3)  cmdloop() not detecting that it should skip the rest of
         the file, due to only checking for SKIPFILE.
 The result is that cmdloop() keeps executing lines from the file
 whilst evalskip is set, which is the main symptom.  Because
 evalskip is checked in multiple places in eval.c, the secondary
 symptom appears.
 >How-To-Repeat:
 Run the following script:

     printf "break\necho break1; echo break2" >break
     printf "continue\necho continue1; echo continue2" >continue
     printf "return\necho return1; echo return2" >return

     while true; do . ./break; done

     for i in 1 2; do . ./continue; done

     func() {
         . ./return
     }
     func

 No output should be produced, but instead this is the result:
     break1
     continue1
     continue1
     return1

 The main symptom is evident from the unexpected output and the secondary
 one from the fact that there are no lines with '2' in them.
 >Fix:
 Here is patch to src/bin/sh to fix the above problems.  It keeps
 track of the function nesting level at the beginning of a dot command
 to enable the return command to work properly.

 I also changed the undefined-by-standard functionality of the return
 command when it's not in a dot command or function from (indirectly)
 exiting the shell to being silently ignored.  This was done because
 the previous way has at least one bug: the shell exits without asking
 for confirmation when there are stopped jobs.

 Because I read the standard to mean that break and continue should have
 an effect outside the sourced file, that's how I implemented it.  For what
 it's worth, this also seems to be what bash does.  Also laziness, because
 this way required no changes to loopnesting tracking.  If this is not
 wanted, it might make sense to move the nesting tracking to the inputfile
 stack.

 The patch also does some clean-up to reduce the amount of global
 variables by moving the dotcmd() and the find_dot_file() functions from
 main.c to eval.c and making in_function() a proper function.


 To generate a diff of this commit:
 cvs rdiff -u -r1.108 -r1.109 src/bin/sh/eval.c
 cvs rdiff -u -r1.15 -r1.16 src/bin/sh/eval.h
 cvs rdiff -u -r1.57 -r1.58 src/bin/sh/main.c
 cvs rdiff -u -r1.112 -r1.113 src/bin/sh/sh.1

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: Richard Hansen <rhansen@bbn.com>
To: jarmo.jaakkola@roskakori.fi
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@netbsd.org, 
 tech-userlevel@NetBSD.org
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot commands
Date: Sat, 31 May 2014 17:14:26 -0400

 > According to the POSIX standard, this is how it should work:
 >     dot:
 >         The shell shall execute commands from the file in the current
 >         environment.
 >     break:
 >         The break utility shall exit from the smallest enclosing for, while,
 >         or until loop, [...]
 >     continue:
 >         The continue utility shall return to the top of the smallest
 >         enclosing for, while, or until loop, [...]
 >     return:
 >         The return utility shall cause the shell to stop executing
 >         the current function or dot script.  If the shell is not currently
 >         executing a function or dot script, the results are unspecified.
 > 
 > It is clear that return should return from a sourced file,

 Yes.

 > Whether break and continue should work from the sourced
 > file might be debatable.  Because the dot command says "in the current
 > environment", I'd say yes.

 Not necessarily.  POSIX does not define "enclosing loop", so it could be
 interpreted as syntactic enclosure (a break/continue command must be a
 command in the compound list associated with the loop for the loop to
 qualify as enclosing the command) or logical enclosure as experienced
 during execution.  I can see pros and cons to either behavior.

 I will bring this up during the next Austin Group teleconference.  We
 should be able to get some improved wording in before POSIX Issue 7 TC2
 is published (even if that wording is simply "unspecified" or
 "implementation defined").  Any input from the NetBSD community would be
 appreciated.

 The intended behavior of break/continue outside of a loop is also
 unclear.  I'll bring that up as well.

 > Because I read the standard to mean that break and continue should have
 > an effect outside the sourced file, that's how I implemented it.  For what
 > it's worth, this also seems to be what bash does.

 The behavior of existing implementations will strongly influence the
 direction the Austin Group takes when revising the text.  With that
 said, what behavior would you like POSIX to specify?

 Sourcing a file with the dot command and running a function are very
 similar; how do your changes affect how the shell behaves when calling a
 function that has break/continue in the body?

 > +A non-obvious consequence of the file executing in the current environment
 > +is that loop control keywords (continue and break) can be used in the file
 > +to control loops surrounding the dot command.

 Specifying this new behavior in the man page implies commitment to it.
 In case you want to change the behavior in the future (perhaps because
 the Austin Group standardizes a different behavior), I would recommend
 softening the language like you did with 'return' (e.g., "the POSIX
 standard is unclear, so this implementation (currently) does...").

 > +The effects of using a return command outside a function or a dot command
 > +are not standardized.

 One can argue that the effects are standardized as "unspecified".  :)

 How about: "The POSIX standard says that the results of running 'return'
 outside a function or dot script are unspecified.  This implementation..."

 Thanks,
 Richard

From: Jarmo Jaakkola <jarmo.jaakkola@roskakori.fi>
To: Richard Hansen <rhansen@bbn.com>
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@netbsd.org,
	tech-userlevel@NetBSD.org
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot
	commands
Date: Sun, 1 Jun 2014 02:09:41 +0300

 On Sat, May 31, 2014 at 05:14:26PM -0400, Richard Hansen wrote:
 > I will bring this up during the next Austin Group teleconference.  We
 > should be able to get some improved wording in before POSIX Issue 7 TC2
 > is published (even if that wording is simply "unspecified" or
 > "implementation defined").  Any input from the NetBSD community would be
 > appreciated.
 > 
 > The intended behavior of break/continue outside of a loop is also
 > unclear.  I'll bring that up as well.

 That would be great, these things really should be clarified.  This
 probably belongs to "shell execution environment"?  That is, whether
 function and loop nesting levels are part of it or not.  How about local
 variables, or were they just a common extension that is not specified
 in POSIX?

 While we're on the subject of clarity, I'm not sure if the value $0
 inside a dot command has been specified.  It could be implied by
 "current execution environment" but perhaps it wouldn't hurt to be
 explicit.

 > > Because I read the standard to mean that break and continue should have
 > > an effect outside the sourced file, that's how I implemented it.  For what
 > > it's worth, this also seems to be what bash does.
 > 
 > The behavior of existing implementations will strongly influence the
 > direction the Austin Group takes when revising the text.  With that
 > said, what behavior would you like POSIX to specify?

 I have no opinion one way or another.  I just took the easiest way to fix
 existing breakage.  I quickly tested with bash, ksh and zsh with a trivial
 case: dot command in a while loop contains a break. bash does as my change
 does: the loop is broken.  ksh complains and seems to ignore the break.
 zsh's behaviour is probably best described as "undefined", because it
 complains, stops sourcing the file and yet does not break the loop.

 > Sourcing a file with the dot command and running a function are very
 > similar; how do your changes affect how the shell behaves when calling a
 > function that has break/continue in the body?

 My changes retain the previous behavior, which was to allow a break or
 a continue in function to affect a loop that contains the function call.
 Quick check for other shells: bash and zsh work the same, ksh complains and
 does not break the loop nor return from the function.

 How the function call worked was one more reason I chose to implement
 the dot command fix as I did: one could think this implementation of
 a dot command as a function call that has no parameters (except "set --")
 and that the function's body is stored in a file.

 > > +A non-obvious consequence of the file executing in the current environment
 > > +is that loop control keywords (continue and break) can be used in the file
 > > +to control loops surrounding the dot command.
 > 
 > Specifying this new behavior in the man page implies commitment to it.
 > In case you want to change the behavior in the future (perhaps because
 > the Austin Group standardizes a different behavior), I would recommend
 > softening the language like you did with 'return' (e.g., "the POSIX
 > standard is unclear, so this implementation (currently) does...").

 I agree, that would be better.  Perhaps:

     The POSIX standard is unclear on how loop control keywords (break
     and continue) behave across a dot command boundary.  This
     implementation (currently) allows them to control loops surrounding
     the dot command.

 > > +The effects of using a return command outside a function or a dot command
 > > +are not standardized.
 > 
 > One can argue that the effects are standardized as "unspecified".  :)
 > 
 > How about: "The POSIX standard says that the results of running 'return'
 > outside a function or dot script are unspecified.  This implementation..."

 Yes, of course, my bad.  Your formulation would be better.

 christos, you made the previous commits, could you do those language
 tweaks?  Or should I prepare another patch?

 -- 
 Jarmo Jaakkola

From: David Holland <dholland-tech@netbsd.org>
To: Richard Hansen <rhansen@bbn.com>
Cc: jarmo.jaakkola@roskakori.fi, gnats-bugs@NetBSD.org,
	tech-userlevel@NetBSD.org
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot
 commands
Date: Sat, 31 May 2014 23:51:35 +0000

 On Sat, May 31, 2014 at 05:14:26PM -0400, Richard Hansen wrote:
  > > Whether break and continue should work from the sourced
  > > file might be debatable.  Because the dot command says "in the current
  > > environment", I'd say yes.
  > 
  > Not necessarily.  POSIX does not define "enclosing loop", so it could be
  > interpreted as syntactic enclosure (a break/continue command must be a
  > command in the compound list associated with the loop for the loop to
  > qualify as enclosing the command) or logical enclosure as experienced
  > during execution.  I can see pros and cons to either behavior.

 Offhand, I would say that continues and breaks should be statically
 scoped; dynamic scoping is almost always a mistake. So you certainly
 shouldn't be able to break from a loop by calling a function that
 contains a break outside a loop. (Although netbsd's sh, bash, and zsh
 all seem to allow this, I would call it a bug. ksh rejects it.)

 How this applies to a sourced file isn't so clear though, at least
 offhand, as the point of sourcing a file is to read and evaluate it
 within the current context. My inclination would be that sourcing a
 file is not the same as calling a function; however, I'm far from an
 expert on sh.

 It seems that the behavior of sourcing with respect to $0 and $@
 varies among implementations, which doesn't make me happy.

  > I will bring this up during the next Austin Group teleconference.  We
  > should be able to get some improved wording in before POSIX Issue 7 TC2
  > is published (even if that wording is simply "unspecified" or
  > "implementation defined").  Any input from the NetBSD community would be
  > appreciated.
  > 
  > The intended behavior of break/continue outside of a loop is also
  > unclear.  I'll bring that up as well.

 netbsd's sh seems to accept it silently; ksh, bash, and zsh all reject
 it. I would consider our sh broken.

  > > Because I read the standard to mean that break and continue should have
  > > an effect outside the sourced file, that's how I implemented it.  For what
  > > it's worth, this also seems to be what bash does.
  > 
  > The behavior of existing implementations will strongly influence the
  > direction the Austin Group takes when revising the text.  With that
  > said, what behavior would you like POSIX to specify?

 With stuff like this, I'd rather fix our implementation (or have it be
 noncompliant until fixed) than standardize unprincipled behavior. FWIW.

 -- 
 David A. Holland
 dholland@netbsd.org

From: David Holland <dholland-tech@netbsd.org>
To: Richard Hansen <rhansen@bbn.com>, gnats-bugs@NetBSD.org,
	tech-userlevel@NetBSD.org
Cc: 
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot
 commands
Date: Sat, 31 May 2014 23:55:48 +0000

 On Sun, Jun 01, 2014 at 02:09:41AM +0300, Jarmo Jaakkola wrote:
  > How the function call worked was one more reason I chose to implement
  > the dot command fix as I did: one could think this implementation of
  > a dot command as a function call that has no parameters (except "set --")
  > and that the function's body is stored in a file.

 Well... implementations other than netbsd's sh seem to allow passing
 parameters to a dot command:

 valkyrie% cat y2
 . ./y a b c d e
 valkyrie% cat y
 echo '$0 is '"$0"'; $@ is '"$@"
 valkyrie% sh ./y2
 $0 is ./y2; $@ is 
 valkyrie% ksh ./y2
 $0 is ./y2; $@ is a b c d e
 valkyrie% bash ./y2
 $0 is ./y2; $@ is a b c d e
 valkyrie% zsh ./y2
 $0 is ./y; $@ is a b c d e
 valkyrie% 

 although apparently only zsh changes $0.

 -- 
 David A. Holland
 dholland@netbsd.org

From: Jarmo Jaakkola <jarmo.jaakkola@roskakori.fi>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot
	commands
Date: Sun, 1 Jun 2014 19:25:21 +0300

 Soften the language in the manual page, making less promises about
 behavior not explicitly stated in the standard.


 diff -u orig/sh.1 new/sh.1
 --- orig/sh.1
 +++ new/sh.1
 @@ -1193,9 +1193,10 @@ variable if it does not contain a directory separator
  .Pq Sq / .
  The return command can be used for a premature return from the sourced file.
  .Pp
 -A non-obvious consequence of the file executing in the current environment
 -is that loop control keywords (continue and break) can be used in the file
 -to control loops surrounding the dot command.
 +The POSIX standard is unclear on how loop control keywords (break
 +and continue) behave across a dot command boundary.
 +This implementation allows them to control loops surrounding the dot command,
 +but obviously such behavior should not be relied on.
  .It alias Op Ar name Ns Op Ar "=string ..."
  If
  .Ar name=string
 @@ -1641,11 +1642,13 @@ For portability,
  .Ar n
  should be in the range from 0 to 255.
  .Pp
 -The effects of using a return command outside a function or a dot command
 -are not standardized.
 -This implementation (currently) treats such a return as a no-op with
 -a return value of 0 (success, true).
 -Use the exit command if you want to return from a script or exit your shell.
 +The POSIX standard says that the results of
 +.Sq return
 +outside a function or a dot command are unspecified.
 +This implementation treats such a return as a no-op with a return value of 0
 +(success, true).
 +Use the exit command instead, if you want to return from a script or exit
 +your shell.
  .It set Oo { Fl options | Cm +options | Cm \-- } Oc Ar arg ...
  The
  .Ic set


 -- 
 Jarmo Jaakkola

From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/48843 CVS commit: src/bin/sh
Date: Sun, 1 Jun 2014 13:46:06 -0400

 Module Name:	src
 Committed By:	christos
 Date:		Sun Jun  1 17:46:06 UTC 2014

 Modified Files:
 	src/bin/sh: sh.1

 Log Message:
 PR/48843: Jarmo Jaakkola: Soften the language in the manual page,
 making less promises about behavior not explicitly stated in the standard.


 To generate a diff of this commit:
 cvs rdiff -u -r1.113 -r1.114 src/bin/sh/sh.1

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: Rhialto <rhialto@falu.nl>
To: David Holland <dholland-tech@netbsd.org>
Cc: Richard Hansen <rhansen@bbn.com>, jarmo.jaakkola@roskakori.fi,
        gnats-bugs@NetBSD.org, tech-userlevel@NetBSD.org
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot
 commands
Date: Sun, 1 Jun 2014 23:19:17 +0200

 --nmemrqcdn5VTmUEE
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: inline
 Content-Transfer-Encoding: quoted-printable

 On Sat 31 May 2014 at 23:51:35 +0000, David Holland wrote:
 > On Sat, May 31, 2014 at 05:14:26PM -0400, Richard Hansen wrote:
 >  > Not necessarily.  POSIX does not define "enclosing loop", so it could =
 be
 >  > interpreted as syntactic enclosure (a break/continue command must be a
 >  > command in the compound list associated with the loop for the loop to
 >  > qualify as enclosing the command) or logical enclosure as experienced
 >  > during execution.  I can see pros and cons to either behavior.
 >=20
 > Offhand, I would say that continues and breaks should be statically
 > scoped; dynamic scoping is almost always a mistake. So you certainly
 > shouldn't be able to break from a loop by calling a function that
 > contains a break outside a loop. (Although netbsd's sh, bash, and zsh
 > all seem to allow this, I would call it a bug. ksh rejects it.)

 I agree. The other way is clearly unworkable. A function with a break or
 continue outside a loop would be incorrect to call except inside a loop.
 That is ludicrous.

 > How this applies to a sourced file isn't so clear though, at least
 > offhand, as the point of sourcing a file is to read and evaluate it
 > within the current context. My inclination would be that sourcing a
 > file is not the same as calling a function; however, I'm far from an
 > expert on sh.

 I would say here too that static checking should be the norm. Except
 that there is an inherent non-staticness here: the file could have been
 generated dynamically, or the name of the file could be dynamic, or
 perhaps even more annoying non-static things.

 -Olaf.
 --=20
 ___ Olaf 'Rhialto' Seibert  -- The Doctor: No, 'eureka' is Greek for
 \X/ rhialto/at/xs4all.nl    -- 'this bath is too hot.'

 --nmemrqcdn5VTmUEE
 Content-Type: application/pgp-signature

 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1

 iQEcBAEBAgAGBQJTi5jUAAoJEJmJxkVhw/vTnnoH/05oc9bPyvEB1mN1UeCxaLZj
 n9/fE+KfnguYZq1bZC9kL8nFwvddXdq/NVEDpP67Ie7dvyfSsblK1VYYpP0/+ETX
 ITUZFt/iiCsg+wLlejmSD08pWzNaNHBPA4YMe6wX7N/XaL8JWaFZt+0XUW/cRAPE
 lRN8H/fzOri5rins4++/NEY30/3Bubw1xUhx7I+mjy1Rjrv3BBpQxA0eEb8n+/Hy
 /W2zzXX79AB1uKSLGxJD8k5oAIFy0Zi4QIfc2qZ7HziJ1AWn/hCuhIrOyhQmHHEp
 cwItWX4O3vYqmcDyfUtnsGdJn12y2x8dzmSHtoSkCaymPyuh2IQAGpOlJxIWxOc=
 =StD5
 -----END PGP SIGNATURE-----

 --nmemrqcdn5VTmUEE--

From: Richard Hansen <rhansen@bbn.com>
To: gnats-bugs@NetBSD.org, netbsd-bugs@netbsd.org, 
 tech-userlevel@NetBSD.org
Cc: 
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot commands
Date: Mon, 02 Jun 2014 13:21:17 -0400

 On 2014-05-31 19:09, Jarmo Jaakkola wrote:
 > On Sat, May 31, 2014 at 05:14:26PM -0400, Richard Hansen wrote:
 >> I will bring this up during the next Austin Group teleconference.  We
 >> should be able to get some improved wording in before POSIX Issue 7 TC2
 >> is published (even if that wording is simply "unspecified" or
 >> "implementation defined").  Any input from the NetBSD community would be
 >> appreciated.
 >>
 >> The intended behavior of break/continue outside of a loop is also
 >> unclear.  I'll bring that up as well.

 I filed a bug report that will be the basis for discussion during the
 Thursday teleconference (assuming we have time to address this bug):
 http://austingroupbugs.net/view.php?id=842

 > 
 > That would be great, these things really should be clarified.  This
 > probably belongs to "shell execution environment"?  That is, whether
 > function and loop nesting levels are part of it or not.

 I'll think about it more when I write up the proposed changes, but I
 don't think that the Shell Execution Environment section is the
 appropriate place.  That section is all about how the shell forks (or
 the equivalent) to run commands.  The purpose of the "in the current
 environment" phrase in the dot command's description is to let you know
 that the script isn't run in a subshell or as a separate process, so it
 can modify variable assignments, etc.

 If dot was meant to act like C's #include and read a separate file's
 lines as if they were typed where the dot command was typed, then I
 think it would have been specified that way.

 > How about local
 > variables, or were they just a common extension that is not specified
 > in POSIX?

 Local variables are a common extension.  Proposing text to standardize
 local variables is on my todo list -- they would help solve some really
 problematic parts of the current standard.  See [1] and the discussion
 at [2].

 I should note that anyone is welcome to submit bug reports or proposed
 wording changes about any topic.  You can even join the next POSIX
 telecon if you'd like; see [3].

 [1] http://austingroupbugs.net/view.php?id=654
 [2] http://thread.gmane.org/gmane.comp.standards.posix.austin.general/6728
 [3] http://article.gmane.org/gmane.comp.standards.posix.austin.general/9454

 > 
 > While we're on the subject of clarity, I'm not sure if the value $0
 > inside a dot command has been specified.  It could be implied by
 > "current execution environment" but perhaps it wouldn't hurt to be
 > explicit.

 The value of $0 in a dot script is specified -- it is the same as it was
 before the script was sourced.  This is not explicit -- the only way to
 arrive at this conclusion is to note that the description of the dot
 command does not specify any change to $0.  It would be better if this
 was made explicit, as it is with functions.  I'll add that to my todo
 list, or you can file a bug report if you'd like.

 >> The behavior of existing implementations will strongly influence the
 >> direction the Austin Group takes when revising the text.  With that
 >> said, what behavior would you like POSIX to specify?
 > 
 > I have no opinion one way or another.  I just took the easiest way to fix
 > existing breakage.

 OK

 > I quickly tested with bash, ksh and zsh with a trivial
 > case: dot command in a while loop contains a break. bash does as my change
 > does: the loop is broken.  ksh complains and seems to ignore the break.
 > zsh's behaviour is probably best described as "undefined", because it
 > complains, stops sourcing the file and yet does not break the loop.

 Curious -- I had different results when testing 'break' from a dot
 script in other shells (I documented my results in the POSIX bug report).

 > 
 >> Sourcing a file with the dot command and running a function are very
 >> similar; how do your changes affect how the shell behaves when calling a
 >> function that has break/continue in the body?
 > 
 > My changes retain the previous behavior, which was to allow a break or
 > a continue in function to affect a loop that contains the function call.

 So the behavior from a function and the behavior from a dot script are
 now the same?  I like that.

 > Quick check for other shells: bash and zsh work the same, ksh complains and
 > does not break the loop nor return from the function.

 Hmm, my test results differed from yours in the function case as well as
 the dot script case.  (Not that it matters much.)

 > 
 > How the function call worked was one more reason I chose to implement
 > the dot command fix as I did: one could think this implementation of
 > a dot command as a function call that has no parameters (except "set --")
 > and that the function's body is stored in a file.

 Exactly -- I think it's good for those two cases to be consistent.

 -Richard

From: Richard Hansen <rhansen@bbn.com>
To: David Holland <dholland-tech@netbsd.org>
Cc: jarmo.jaakkola@roskakori.fi, gnats-bugs@NetBSD.org, 
 tech-userlevel@NetBSD.org
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot commands
Date: Mon, 02 Jun 2014 13:54:04 -0400

 On 2014-05-31 19:51, David Holland wrote:
 > On Sat, May 31, 2014 at 05:14:26PM -0400, Richard Hansen wrote:
 >>> Whether break and continue should work from the sourced
 >>> file might be debatable.  Because the dot command says "in the current
 >>> environment", I'd say yes.
 >> 
 >> Not necessarily.  POSIX does not define "enclosing loop", so it could be
 >> interpreted as syntactic enclosure (a break/continue command must be a
 >> command in the compound list associated with the loop for the loop to
 >> qualify as enclosing the command) or logical enclosure as experienced
 >> during execution.  I can see pros and cons to either behavior.
 > 
 > Offhand, I would say that continues and breaks should be statically
 > scoped; dynamic scoping is almost always a mistake. So you certainly
 > shouldn't be able to break from a loop by calling a function that
 > contains a break outside a loop. (Although netbsd's sh, bash, and zsh
 > all seem to allow this, I would call it a bug. ksh rejects it.)

 I think I also prefer lexical scoping, but I can see some valid (though
 unusual) uses for dynamic scoping.

 > 
 > How this applies to a sourced file isn't so clear though, at least
 > offhand, as the point of sourcing a file is to read and evaluate it
 > within the current context.

 Yes and no -- there are subtle differences between the dot command and
 C's #include preprocessor directive (exit status of the dot command,
 in-line variable assignments before the dot command, redirection).

 > My inclination would be that sourcing a
 > file is not the same as calling a function; however, I'm far from an
 > expert on sh.

 I don't really know the history, but my impression is that the dot
 command was intended to make it possible to split common complex tasks
 out into modular, reusable scripts.  That sounds more like a function
 and less like a preprocessor include to me, though the differences
 aren't very significant.

 > 
 > It seems that the behavior of sourcing with respect to $0 and $@
 > varies among implementations, which doesn't make me happy.

 They're all consistent if you don't specify any additional arguments to
 the dot command.  Zsh does something different with $0, but it's an
 incompatible scripting language in many ways unless run in sh emulation
 mode (e.g., it doesn't do word splitting by default -- a drastic
 departure from POSIX shell).

 > 
 >> I will bring this up during the next Austin Group teleconference.  We
 >> should be able to get some improved wording in before POSIX Issue 7 TC2
 >> is published (even if that wording is simply "unspecified" or
 >> "implementation defined").  Any input from the NetBSD community would be
 >> appreciated.
 >> 
 >> The intended behavior of break/continue outside of a loop is also
 >> unclear.  I'll bring that up as well.
 > 
 > netbsd's sh seems to accept it silently; ksh, bash, and zsh all reject
 > it. I would consider our sh broken.

 Yes, although not necessarily non-conformant.  I'd say that the behavior
 of break and continue outside of a loop should be specified as
 undefined, which would allow implementations to do whatever they want
 (error out, silently ignore it, give it a special meaning as a fancy
 extension, etc.).

 > 
 >>> Because I read the standard to mean that break and continue should have
 >>> an effect outside the sourced file, that's how I implemented it.  For what
 >>> it's worth, this also seems to be what bash does.
 >> 
 >> The behavior of existing implementations will strongly influence the
 >> direction the Austin Group takes when revising the text.  With that
 >> said, what behavior would you like POSIX to specify?
 > 
 > With stuff like this, I'd rather fix our implementation (or have it be
 > noncompliant until fixed) than standardize unprincipled behavior. FWIW.

 OK, so that's one one vote for static/lexical scoping.

 Note that unlike variable binding, the behavior of dynamic scoping of
 break/continue is a superset of static/lexical scoping (assuming no
 closures):  If the shell does dynamic scoping of break/continue but
 scripts are always written assuming lexical scoping then those scripts
 will still work as expected in all cases.  If POSIX were to specify
 static/lexical scoping then a shell that performs dynamic scoping would
 be conformant but with an extension to the standard.

 -Richard

From: Richard Hansen <rhansen@bbn.com>
To: David Holland <dholland-tech@netbsd.org>
Cc: jarmo.jaakkola@roskakori.fi, gnats-bugs@NetBSD.org, 
 tech-userlevel@NetBSD.org
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot commands
Date: Tue, 03 Jun 2014 12:53:56 -0400

 On 2014-06-02 13:54, Richard Hansen wrote:
 > Note that unlike variable binding, the behavior of dynamic scoping of
 > break/continue is a superset of static/lexical scoping (assuming no
 > closures):  If the shell does dynamic scoping of break/continue but
 > scripts are always written assuming lexical scoping then those scripts
 > will still work as expected in all cases.  If POSIX were to specify
 > static/lexical scoping then a shell that performs dynamic scoping would
 > be conformant but with an extension to the standard.

 Oops -- as pointed out in [1] this is incorrect when the argument to
 break/continue is greater than the number of lexically enclosing loops.
  An author could write 'break 1000' to make it obvious to other readers
 that all loops should be broken.  A script like this might behave
 differently on a shell that does dynamic scoping vs. a shell that does
 lexical scoping.

 So it looks like POSIX will probably have to choose either lexical
 (static) or dynamic for the next major version of the standard (Issue 8).

 David voiced his preference for lexical [2]; does anyone else have an
 opinion?

 I think my preference is for dynamic, although I would question the
 design decisions of a script author that takes advantage of dynamic
 scoping.  Why dynamic over lexical?  Because functionality is lost by
 making it lexical:  If a script author wants dynamic but the shell does
 lexical, the author is out of luck.  If the shell does dynamic but the
 author wants lexical, the author can easily count the number of
 lexically enclosing loops and limit the argument of break/continue to
 that value.  Dynamic might also be easier to implement.

 Thanks,
 Richard

 [1] http://article.gmane.org/gmane.comp.standards.posix.austin.general/9467
 [2] http://article.gmane.org/gmane.os.netbsd.devel.userlevel/17452

From: Aleksej Saushev <asau@inbox.ru>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot commands
Date: Thu, 05 Jun 2014 02:32:01 +0400

 Richard Hansen <rhansen@bbn.com> writes:

 > The following reply was made to PR bin/48843; it has been noted by GNATS.
 >
 > From: Richard Hansen <rhansen@bbn.com>
 > To: David Holland <dholland-tech@netbsd.org>
 > Cc: jarmo.jaakkola@roskakori.fi, gnats-bugs@NetBSD.org, 
 >  tech-userlevel@NetBSD.org
 > Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot commands
 > Date: Tue, 03 Jun 2014 12:53:56 -0400
 >
 >  On 2014-06-02 13:54, Richard Hansen wrote:
 >  > Note that unlike variable binding, the behavior of dynamic scoping of
 >  > break/continue is a superset of static/lexical scoping (assuming no
 >  > closures):  If the shell does dynamic scoping of break/continue but
 >  > scripts are always written assuming lexical scoping then those scripts
 >  > will still work as expected in all cases.  If POSIX were to specify
 >  > static/lexical scoping then a shell that performs dynamic scoping would
 >  > be conformant but with an extension to the standard.
 >  
 >  Oops -- as pointed out in [1] this is incorrect when the argument to
 >  break/continue is greater than the number of lexically enclosing loops.
 >   An author could write 'break 1000' to make it obvious to other readers
 >  that all loops should be broken.  A script like this might behave
 >  differently on a shell that does dynamic scoping vs. a shell that does
 >  lexical scoping.
 >  
 >  So it looks like POSIX will probably have to choose either lexical
 >  (static) or dynamic for the next major version of the standard (Issue 8).
 >  
 >  David voiced his preference for lexical [2]; does anyone else have an
 >  opinion?
 >  
 >  I think my preference is for dynamic, although I would question the
 >  design decisions of a script author that takes advantage of dynamic
 >  scoping.  Why dynamic over lexical?  Because functionality is lost by
 >  making it lexical:  If a script author wants dynamic but the shell does
 >  lexical, the author is out of luck.  If the shell does dynamic but the
 >  author wants lexical, the author can easily count the number of
 >  lexically enclosing loops and limit the argument of break/continue to
 >  that value.  Dynamic might also be easier to implement.

 No, this is wrong. Simulating dynamic scoping with lexical one is clear
 and almost trivial. It also has benefit that you have control over which
 variables have dynamic scope. The opposite is practically impossible.

 Besides, the general consensus in programming languages community is
 that dynamic scoping is bad design. It breaks abstractions and thus
 makes writing modular and correct code a lot harder. Choosing dynamic
 scoping over lexical has nearly disastrous consequences on the code
 maintainability.

From: Richard Hansen <rhansen@bbn.com>
To: gnats-bugs@NetBSD.org, netbsd-bugs@netbsd.org, 
 tech-userlevel@NetBSD.org
Cc: 
Subject: Re: bin/48843: sh(1): break/continue/return broken inside dot commands
Date: Thu, 05 Jun 2014 14:56:00 -0400

 On 2014-06-02 13:21, Richard Hansen wrote:
 >> On Sat, May 31, 2014 at 05:14:26PM -0400, Richard Hansen wrote:
 >>> I will bring this up during the next Austin Group teleconference.  We
 >>> should be able to get some improved wording in before POSIX Issue 7 TC2
 >>> is published (even if that wording is simply "unspecified" or
 >>> "implementation defined").  Any input from the NetBSD community would be
 >>> appreciated.
 >>>
 >>> The intended behavior of break/continue outside of a loop is also
 >>> unclear.  I'll bring that up as well.
 > 
 > I filed a bug report that will be the basis for discussion during the
 > Thursday teleconference (assuming we have time to address this bug):
 > http://austingroupbugs.net/view.php?id=842

 That bug has been resolved, with the revised text (which will be in
 Issue 7 TC2) available here:
 http://austingroupbugs.net/view.php?id=842#c2257

 We decided to not do anything for Issue 8 at this time, so the behavior
 will continue to be unspecified if n is greater than the number of
 lexically enclosing loops.  The argument is that the standard has been
 this way for 20+ years without any major complaints, so why force
 implementations to change their code to support a case that few care about.

 -Richard

State-Changed-From-To: open->closed
State-Changed-By: dholland@NetBSD.org
State-Changed-When: Mon, 31 Oct 2016 05:33:09 +0000
State-Changed-Why:
the patches got committed and the standards discussion did not lead to
needing to do further work.


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2014 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.