aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2016-08-19 16:12:00 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2016-08-19 16:12:00 +0300
commitf1c724be131b8f89ef46a13d6181b0e037420bf1 (patch)
tree04cdbefefb82d3f91dbb04382f79ca063beccf7b
parentaf713489cb216985e6fee1afab36a35791d4c38e (diff)
downloadrush-f1c724be131b8f89ef46a13d6181b0e037420bf1.tar.gz
rush-f1c724be131b8f89ef46a13d6181b0e037420bf1.tar.bz2
Finish the testsuite.
* doc/rush.rc.5: Fix typos. * doc/rush.texi: Likewise. * src/dump.c (dump_argv): Optionally sort the array. * src/map.c (meta_expand_string): Fix $N evaluation. (mapdef): Allow to use ${^} as a synonym to ${program}. * src/rush.c (env_setup): Fix handling of NAME+= and NAME=+ * tests/Makefile.am: Add new files. * tests/atlocal.in (RUSHDIR): New variable. (myfilter): New function. * tests/testsuite.at (AT_RUSH_TEST): Handle optional environment and interactive settings. Include New tests. * tests/chdir.at: New file. * tests/delete.at: New file. * tests/env.at: New file. * tests/error.at: New file. * tests/fallthrough.at: New file. * tests/gid.at: New file. * tests/interactive.at: New file. * tests/map.at: New file. * tests/newgrp.at: New file. * tests/set.at: New file. * tests/transform.at: New file. * tests/uid.at: New file. * tests/umask.at: New file.
-rw-r--r--doc/rush.rc.512
-rw-r--r--doc/rush.texi2
-rw-r--r--src/dump.c54
-rw-r--r--src/map.c3
-rw-r--r--src/rush.c107
-rw-r--r--tests/Makefile.am15
-rw-r--r--tests/atlocal.in10
-rw-r--r--tests/chdir.at34
-rw-r--r--tests/delete.at87
-rw-r--r--tests/env.at191
-rw-r--r--tests/error.at42
-rw-r--r--tests/fallthrough.at65
-rw-r--r--tests/gid.at50
-rw-r--r--tests/interactive.at39
-rw-r--r--tests/map.at79
-rw-r--r--tests/newgrp.at34
-rw-r--r--tests/set.at111
-rw-r--r--tests/testsuite.at36
-rw-r--r--tests/transform.at106
-rw-r--r--tests/uid.at50
-rw-r--r--tests/umask.at34
21 files changed, 1101 insertions, 60 deletions
diff --git a/doc/rush.rc.5 b/doc/rush.rc.5
index 0d44d21..4f7968b 100644
--- a/doc/rush.rc.5
+++ b/doc/rush.rc.5
@@ -13,7 +13,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
-.TH RUSH.RC 1 "August 17, 2016" "RUSH.RC" "Rush User Reference"
+.TH RUSH.RC 1 "August 19, 2016" "RUSH.RC" "Rush User Reference"
.SH NAME
rush.rc \- configuration rules for rush.
.SH DESCRIPTION
@@ -324,7 +324,7 @@ command prefixed with a dash:
.RS
.RS
.EX
-transform[0] ${^} s,.*/,-
+transform[0] ${^} s,.*/,-,
.EE
.RE
.RE
@@ -437,19 +437,19 @@ specifiers are allowed:
Clear the environment. If used, this must be the very first argument.
.TP
\fI\-NAME\fR
-Unset the environment variable \INAME\fR.
+Unset the environment variable \fINAME\fR.
.TP
\fI\-NAME\fB=\fIVAL\fR
-Unset the environment variable \INAME\fR only if its value is \fIVAL\fR.
+Unset the environment variable \fINAME\fR only if its value is \fIVAL\fR.
.TP
\fINAME\fR
Retain the environment variable \fINAME\fR.
.TP
\fINAME\fB=\fIVALUE\fR
-Set the environment variable \INAME\fB to the given \fIVALUE\fR.
+Set the environment variable \fINAME\fB to the given \fIVALUE\fR.
.TP
\fINAME\fB+=\fIVALUE\fR
-Retain the variable \INAME\fB and append \fIVALUE\fR to its value. If
+Retain the variable \fINAME\fB and append \fIVALUE\fR to its value. If
no such variable is present in the environment, it is created and
\fIVALUE\fR is assigned to it. However, if \fIVALUE\fR starts with a
punctuation character, this character is removed from it before the
diff --git a/doc/rush.texi b/doc/rush.texi
index 7bf691c..1eb09a1 100644
--- a/doc/rush.texi
+++ b/doc/rush.texi
@@ -814,7 +814,7 @@ The example below replaces the 0th argument with the base name of the
command, prefixed by a dash:
@smallexample
-transform[0] $@{^@} s,.*/,-
+transform[0] $@{^@} s,.*/,-,
@end smallexample
For instance, if the command name is @samp{/bin/bash}, @samp{argv[0]} will
diff --git a/src/dump.c b/src/dump.c
index ab90fe5..d658e4c 100644
--- a/src/dump.c
+++ b/src/dump.c
@@ -107,25 +107,63 @@ dump_string(char const *string, char const *id, struct json_dumper *dmp)
}
static void
-dump_argv(char **argv, char const *id, struct json_dumper *dmp)
+dump_raw_argv(char **argv, struct json_dumper *dmp)
+{
+ size_t i;
+
+ for (i = 0; argv[i]; i++) {
+ if (i)
+ dump_delim(dmp);
+ dump_string_data(argv[i], dmp);
+ }
+}
+
+static int
+cmp_ptr(const void *a, const void *b)
+{
+ char ** const aptr = (char ** const) a;
+ char ** const bptr = (char ** const) b;
+ return strcmp (*aptr, *bptr);
+}
+
+static void
+dump_argv(char **argv, char const *id, int sort, struct json_dumper *dmp)
{
size_t i;
struct json_dumper nest_dmp;
dump_id(id, dmp);
+ if (!argv) {
+ fputs("null", dmp->fp);
+ return;
+ }
fputc('[', dmp->fp);
+ if (!argv[0]) {
+ fputc(']', dmp->fp);
+ return;
+ }
+
if (dmp->indent)
fputc('\n', dmp->fp);
dumper_copy(&nest_dmp, dmp);
dump_indent(&nest_dmp);
- for (i = 0; argv[i]; i++) {
- if (i)
- dump_delim(&nest_dmp);
- dump_string_data(argv[i], &nest_dmp);
- }
+ if (sort) {
+ char **newargv;
+ for (i = 0; argv[i]; i++)
+ ;
+ newargv = xcalloc(i+1, sizeof(newargv[0]));
+ for (i = 0; (newargv[i] = argv[i]) != NULL; i++)
+ ;
+ qsort(newargv, i, sizeof(newargv[0]), cmp_ptr);
+ dump_raw_argv(newargv, &nest_dmp);
+ free(newargv);
+ } else {
+ dump_raw_argv(argv, &nest_dmp);
+ }
+
dump_separator(dmp);
fputc(']', dmp->fp);
}
@@ -203,7 +241,7 @@ dump_request(struct rush_request *req, FILE *fp)
if (strcmp(ws.ws_wordv[i], "cmdline") == 0)
dump_string(req->cmdline, "cmdline", &dmp);
else if (strcmp(ws.ws_wordv[i], "argv") == 0)
- dump_argv(req->argv, "argv", &dmp);
+ dump_argv(req->argv, "argv", 0, &dmp);
else if (strcmp(ws.ws_wordv[i], "prog") == 0)
dump_string(req->prog, "prog", &dmp);
else if (strcmp(ws.ws_wordv[i], "interactive") == 0)
@@ -239,7 +277,7 @@ dump_request(struct rush_request *req, FILE *fp)
else if (strcmp(ws.ws_wordv[i], "locale") == 0)
dump_string(req->i18n.locale, "locale", &dmp);
else if (strcmp(ws.ws_wordv[i], "environ") == 0)
- dump_argv(environ, "environ", &dmp);
+ dump_argv(environ, "environ", 1, &dmp);
else
logmsg(LOG_ERR, _("unknown keyword: %s"), ws.ws_wordv[i]);
}
diff --git a/src/map.c b/src/map.c
index f873667..91f9687 100644
--- a/src/map.c
+++ b/src/map.c
@@ -112,7 +112,7 @@ meta_expand_string(const char *string, struct metadef *def,
len = 1;
if (c_isdigit(*p)) {
unsigned n = *p - '0';
- if (n >= req->argc) {
+ if (n < req->argc) {
s = req->argv[n];
len = strlen(req->argv[n]);
}
@@ -191,6 +191,7 @@ static struct metadef mapdef[] = {
{ "home", NULL, meta_home },
{ "gecos", NULL, meta_gecos },
{ "program", NULL, meta_program },
+ { "^", NULL, meta_program },
{ "command", NULL, meta_command },
{ NULL }
};
diff --git a/src/rush.c b/src/rush.c
index 037474d..72b1cd2 100644
--- a/src/rush.c
+++ b/src/rush.c
@@ -349,18 +349,32 @@ expand_tilde(const char *dir, const char *home)
return res;
}
-static char *
-find_env(char *name, int val)
+static int
+find_env_pos(char **env, char *name, int *idx, int *valoff)
{
int nlen = strcspn(name, "?+=");
int i;
- for (i = 0; environ[i]; i++) {
- size_t elen = strcspn(environ[i], "=");
- if (elen == nlen && memcmp(name, environ[i], nlen) == 0)
- return val ? environ[i] + elen + 1 : environ[i];
+ for (i = 0; env[i]; i++) {
+ size_t elen = strcspn(env[i], "=");
+ if (elen == nlen && memcmp(name, env[i], nlen) == 0) {
+ if (idx)
+ *idx = i;
+ if (valoff)
+ *valoff = elen + 1;
+ return 0;
+ }
}
- return NULL;
+ return -1;
+}
+
+static char *
+find_env_ptr(char **env, char *name, int val)
+{
+ int i, j;
+ if (find_env_pos(env, name, &i, &j))
+ return NULL;
+ return val ? env[i] + j : env[i];
}
static int
@@ -459,23 +473,35 @@ env_setup(char **env)
} if ((p = strchr(env[i], '='))) {
if (p == env[i])
continue; /* Ignore erroneous entry */
- if (p[-1] == '+')
- new_env[n++] = env_concat(env[i],
- p - env[i] - 1,
- find_env(env[i], 1),
- p + 1);
- else if (p[1] == '+')
- new_env[n++] = env_concat(env[i],
- p - env[i],
- p + 2,
- find_env(env[i], 1));
- else if (p[-1] == '?') {
- if (!find_env(env[i], 0))
+ if (p[-1] == '+') {
+ int j;
+
+ if (find_env_pos(new_env, env[i], &j, NULL))
+ j = n++;
+ new_env[j] = env_concat(env[i],
+ p - env[i] - 1,
+ find_env_ptr(environ,
+ env[i],
+ 1),
+ p + 1);
+ } else if (p[1] == '+') {
+ int j;
+
+ if (find_env_pos(new_env, env[i], &j, NULL))
+ j = n++;
+ new_env[j] = env_concat(env[i],
+ p - env[i],
+ p + 2,
+ find_env_ptr(environ,
+ env[i],
+ 1));
+ } else if (p[-1] == '?') {
+ if (find_env_pos(environ, env[i], NULL, NULL))
new_env[n++] = p + 1;
} else
new_env[n++] = env[i];
} else {
- p = find_env(env[i], 0);
+ p = find_env_ptr(environ, env[i], 0);
if (p)
new_env[n++] = p;
}
@@ -536,6 +562,7 @@ run_transforms(struct rush_rule *rule, struct rush_request *req)
int i, arg_no, arg_end;
int args_transformed = 0;
char *val, **target;
+ char *mem = NULL;
#define GET_TGT_VAL() \
if (node->progmod) { \
@@ -551,8 +578,17 @@ run_transforms(struct rush_rule *rule, struct rush_request *req)
debug(2, _("Modifying argv[%d]"), arg_no); \
} \
if (node->pattern) { \
- val = rush_expand_string(node->pattern, req); \
+ val = mem = rush_expand_string(node->pattern, req); \
+ } else { \
+ mem = NULL; \
}
+#define FREE_VAL() do { \
+ if (mem) { \
+ free(mem); \
+ mem = NULL; \
+ } \
+ } while (0)
+
for (node = rule->transform_head; node; node = node->next) {
switch (node->type) {
@@ -562,7 +598,13 @@ run_transforms(struct rush_rule *rule, struct rush_request *req)
args_transformed = 0;
}
debug(2, "%s", _("Transforming command line"));
- p = transform_string(node->v.trans, req->cmdline);
+ if (node->pattern) {
+ val = rush_expand_string(node->pattern, req);
+ p = transform_string(node->v.trans, val);
+ free(val);
+ } else
+ p = transform_string(node->v.trans,
+ req->cmdline);
free(req->cmdline);
req->cmdline = p;
debug(2, _("Command line: %s"), req->cmdline);
@@ -580,12 +622,14 @@ run_transforms(struct rush_rule *rule, struct rush_request *req)
req->cmdline = xstrdup(val);
debug(2, _("Command line: %s"), req->cmdline);
reparse_cmdline(req);
+ FREE_VAL();
break;
case transform_arg:
GET_TGT_VAL();
p = transform_string(node->v.trans, val);
assign_string(target, p);
+ FREE_VAL();
break;
case transform_map:
@@ -603,6 +647,7 @@ run_transforms(struct rush_rule *rule, struct rush_request *req)
assign_string(target, p);
else
args_transformed = 0;
+ FREE_VAL();
break;
case transform_delarg:
@@ -631,7 +676,8 @@ run_transforms(struct rush_rule *rule, struct rush_request *req)
case transform_setarg:
GET_TGT_VAL();
- assign_string(target, val);
+ assign_string(target, xstrdup(val));
+ FREE_VAL();
break;
}
}
@@ -913,21 +959,6 @@ run_rule(struct rush_rule *rule, struct rush_request *req)
req->home_dir, strerror(errno));
}
- if (req->interactive && !req->prog) {
- char *p;
-
- req->prog = req->argv[0];
- p = strrchr(req->prog, '/');
- if (p)
- ++p;
- else
- p = req->prog;
- req->argv[0] = xmalloc(strlen(p) + 2);
- req->argv[0][0] = '-';
- strcpy(req->argv[0] + 1, p);
- rebuild_cmdline(req);
- }
-
debug(2, _("Executing %s, %s"), PROGFILE(req), req->cmdline);
if (lint_option) {
if (dump_option)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 431157d..18b3d83 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -40,10 +40,23 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
TESTSUITE_AT = \
argc.at\
+ chdir.at\
command.at\
+ delete.at\
+ env.at\
+ error.at\
+ fallthrough.at\
+ interactive.at\
+ gid.at\
+ map.at\
match.at\
matchprog.at\
- testsuite.at
+ newgrp.at\
+ set.at\
+ testsuite.at\
+ transform.at\
+ uid.at\
+ umask.at
TESTSUITE = $(srcdir)/testsuite
M4=m4
diff --git a/tests/atlocal.in b/tests/atlocal.in
index 64a0d46..272b48e 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -3,6 +3,7 @@
# Copyright (C) 2016 Sergey Poznyakoff
PATH=@abs_builddir@:@abs_top_builddir@/src:$top_srcdir:$srcdir:$PATH
+RUSHDIR=@abs_top_builddir@/src
myvars() {
set -- $(myid)
MY_USER=$1
@@ -10,3 +11,12 @@ myvars() {
MY_GROUP=$3
MY_GID=$4
}
+myfilter() {
+ sed -e "s^$PWD^PWD^g" \
+ -e "s/$MY_USER/MY_USER/g" \
+ -e "s/$MY_UID/MY_UID/g" \
+ -e "s/$MY_GROUP/MY_GROUP/g" \
+ -e "s/$MY_GID/MY_GID/g" \
+ $@
+}
+
diff --git a/tests/chdir.at b/tests/chdir.at
new file mode 100644
index 0000000..e0e6c96
--- /dev/null
+++ b/tests/chdir.at
@@ -0,0 +1,34 @@
+# This file is part of GNU Rush.
+# Copyright (C) 2016 Sergey Poznyakoff
+#
+# GNU Rush is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Rush is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([chdir])
+AT_KEYWORDS([chdir])
+
+AT_RUSH_TEST([
+rule
+ chdir $PWD
+],
+[home_dir],
+[command],
+[0],
+[{
+ "home_dir":"PWD"
+}
+],
+[])
+
+AT_CLEANUP
+
diff --git a/tests/delete.at b/tests/delete.at
new file mode 100644
index 0000000..d90edb0
--- /dev/null
+++ b/tests/delete.at
@@ -0,0 +1,87 @@
+# This file is part of GNU Rush.
+# Copyright (C) 2016 Sergey Poznyakoff
+#
+# GNU Rush is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Rush is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([delete])
+AT_KEYWORDS([delete])
+
+AT_RUSH_TEST([
+rule
+ delete[1]
+],
+[cmdline,argv,prog],
+[echo foo bar],
+[0],
+[{
+ "cmdline":"echo bar",
+ "argv":[[
+ "echo",
+ "bar"
+ ]],
+ "prog":null
+}
+],
+[])
+
+AT_RUSH_TEST([
+rule
+ delete[$]
+],
+[cmdline,argv,prog],
+[echo foo bar],
+[0],
+[{
+ "cmdline":"echo foo",
+ "argv":[[
+ "echo",
+ "foo"
+ ]],
+ "prog":null
+}
+],
+[])
+
+AT_RUSH_TEST([
+rule
+ delete 2 4
+],
+[cmdline,argv,prog],
+[echo foo bar baz quux wum],
+[0],
+[{
+ "cmdline":"echo foo wum",
+ "argv":[[
+ "echo",
+ "foo",
+ "wum"
+ ]],
+ "prog":null
+}
+],
+[])
+
+AT_RUSH_TEST([
+rule
+ delete[0]
+],
+[cmdline,argv,prog],
+[echo foo bar],
+[1],
+[],
+[rush: Error: Deleting argv[[0]] is prohibited
+])
+
+AT_CLEANUP
+
diff --git a/tests/env.at b/tests/env.at
new file mode 100644
index 0000000..9ae91ad
--- /dev/null
+++ b/tests/env.at
@@ -0,0 +1,191 @@
+# This file is part of GNU Rush.
+# Copyright (C) 2016 Sergey Poznyakoff
+#
+# GNU Rush is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Rush is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([env])
+AT_KEYWORDS([env])
+
+AT_RUSH_TEST([
+rule
+ env -
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[]]
+}
+],
+[])
+
+m4_pushdef([RUSH_ENVIRON],[-i HOME=$PWD USER=$MY_USER LOGIN=$MY_USER])
+AT_RUSH_TEST([
+rule
+ env -LOGIN
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "HOME=PWD",
+ "USER=MY_USER"
+ ]]
+}
+],
+[])
+
+AT_RUSH_TEST([
+rule
+ env -LOGIN=$MY_USER
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "HOME=PWD",
+ "USER=MY_USER"
+ ]]
+}
+],
+[])
+m4_popdef([RUSH_ENVIRON])
+
+m4_pushdef([RUSH_ENVIRON],[-i HOME=$PWD USER=$MY_USER LOGIN=NO_$MY_USER])
+AT_RUSH_TEST([
+rule
+ env -LOGIN=$MY_USER
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "HOME=PWD",
+ "LOGIN=NO_MY_USER",
+ "USER=MY_USER"
+ ]]
+}
+],
+[])
+m4_popdef([RUSH_ENVIRON])
+
+m4_pushdef([RUSH_ENVIRON],[-i HOME=$PWD USER=$MY_USER LOGIN=NO_$MY_USER PATH=$PATH])
+AT_RUSH_TEST([
+rule
+ env - HOME USER
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "HOME=PWD",
+ "USER=MY_USER"
+ ]]
+}
+],
+[])
+m4_popdef([RUSH_ENVIRON])
+
+m4_pushdef([RUSH_ENVIRON],[-i HOME=$PWD USER=$MY_USER])
+AT_RUSH_TEST([
+rule
+ env NEWVAR=foo
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "HOME=PWD",
+ "NEWVAR=foo",
+ "USER=MY_USER"
+ ]]
+}
+],
+[])
+m4_popdef([RUSH_ENVIRON])
+
+m4_pushdef([RUSH_ENVIRON],[-i])
+AT_RUSH_TEST([
+rule
+ env MYPATH+=:/usr/local/bin
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "MYPATH=/usr/local/bin"
+ ]]
+}
+],
+[])
+m4_popdef([RUSH_ENVIRON])
+
+m4_pushdef([RUSH_ENVIRON],[-i MYPATH=/bin:/usr/bin])
+AT_RUSH_TEST([
+rule
+ env MYPATH+=:/usr/local/bin
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "MYPATH=/bin:/usr/bin:/usr/local/bin"
+ ]]
+}
+],
+[])
+m4_popdef([RUSH_ENVIRON])
+
+m4_pushdef([RUSH_ENVIRON],[-i])
+AT_RUSH_TEST([
+rule
+ env MYPATH=+/usr/local/bin:
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "MYPATH=/usr/local/bin"
+ ]]
+}
+],
+[])
+m4_popdef([RUSH_ENVIRON])
+
+m4_pushdef([RUSH_ENVIRON],[-i MYPATH=/bin:/usr/bin])
+AT_RUSH_TEST([
+rule
+ env MYPATH=+/usr/local/bin:
+],
+[environ],
+[command],
+[0],
+[{
+ "environ":[[
+ "MYPATH=/usr/local/bin:/bin:/usr/bin"
+ ]]
+}
+],
+[])
+m4_popdef([RUSH_ENVIRON])
+
+AT_CLEANUP
diff --git a/tests/error.at b/tests/error.at
new file mode 100644
index 0000000..893cc2f
--- /dev/null
+++ b/tests/error.at
@@ -0,0 +1,42 @@
+# This file is part of GNU Rush.
+# Copyright (C) 2016 Sergey Poznyakoff
+#
+# GNU Rush is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Rush is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([error])
+AT_KEYWORDS([error])
+
+AT_RUSH_TEST([
+rule
+ exit "Forced error"
+],
+[cmdline],
+[command],
+[1],
+[],
+["Forced error"
+])
+
+AT_RUSH_TEST([
+rule
+ exit 1 "Forced error"
+],
+[cmdline],
+[command],
+[1],
+["Forced error"
+],
+[])
+
+AT_CLEANUP
diff --git a/tests/fallthrough.at b/tests/fallthrough.at
new file mode 100644
index 0000000..03c69a9
--- /dev/null
+++ b/tests/fallthrough.at
@@ -0,0 +1,65 @@
+# This file is part of GNU Rush.
+# Copyright (C) 2016 Sergey Poznyakoff
+#
+# GNU Rush is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Rush is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([fall-through])
+AT_KEYWORDS([fallthrough fall-through])
+
+AT_RUSH_TEST([
+rule
+ env - USER=$MY_USER STATUS='Fall-through working'
+ fall-through
+
+rule
+ set[0] newcommand
+],
+[cmdline,environ],
+[command],
+[0],
+[{
+ "cmdline":"newcommand",
+ "environ":[[
+ "STATUS=Fall-through working",
+ "USER=MY_USER"
+ ]]
+}
+],
+[])
+
+AT_RUSH_TEST([
+rule
+ chdir /tmp
+ fall-through
+
+rule
+ set[0] newcommand
+ interactive
+],
+[cmdline,interactive,home_dir],
+[interactive],
+[0],
+[{
+ "cmdline":"newcommand",
+ "interactive":1,
+ "home_dir":null
+}
+],
+[])
+
+AT_CLEANUP
+
+
+
+
diff --git a/tests/gid.at b/tests/gid.at
new file mode 100644
index 0000000..af90ea7
--- /dev/null
+++ b/tests/gid.at
@@ -0,0 +1,50 @@
+# This file is part of GNU Rush.
+# Copyright (C) 2008-2016 Sergey Poznyakoff
+#
+# GNU Rush is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Rush is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([gid])
+AT_KEYWORDS([gid])
+
+AT_RUSH_TEST([
+rule
+ gid $MY_GROUP
+],
+[cmdline,argv,prog],
+[ls /],
+[0],
+[{
+ "cmdline":"ls /",
+ "argv":[[
+ "ls",
+ "/"
+ ]],
+ "prog":null
+}
+],
+[])
+
+AT_RUSH_TEST([
+rule
+ gid != $MY_GROUP
+],
+[cmdline,argv,prog],
+[ls /],
+[1],
+[],
+[rush: Error: no matching rule for "ls /", user MY_USER
+])
+
+AT_CLEANUP
+
diff --git a/tests/interactive.at b/tests/interactive.at
new file mode 100644
index 0000000..9b3a69f
--- /dev/null
+++ b/tests/interactive.at
@@ -0,0 +1,39 @@
+# This file is part of GNU Rush.
+# Copyright (C) 2016 Sergey Poznyakoff
+#
+# GNU Rush is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Rush is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([interactive])
+AT_KEYWORDS([interactive])
+
+AT_RUSH_TEST([
+rule
+ interactive
+],
+[cmdline,argv,prog,interactive],
+[interactive],
+[0],
+[{
+ "cmdline":"/bin/sh",
+ "argv":[[
+ "/bin/sh"
+ ]],
+ "prog":null,
+ "interactive":1
+}
+],
+[])
+
+AT_CLEANUP
+
diff --git a/tests/map.at b/tests/map.at
new file mode 100644
index 0000000..14096fe
--- /dev/null
+++ b/tests/map.at
@@ -0,0 +1,79 @@
+# This file is part of GNU Rush.
+# Copyright (C) 2016 Sergey Poznyakoff
+#
+# GNU Rush is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Rush is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([map])
+AT_KEYWORDS([map])
+
+AT_CHECK([
+myvars
+
+cat > passwd.rush <<EOT
+root:x:0:0::/root:/bin/bash
+$MY_USER:x:$MY_UID:$MY_GID:Me:/:/rush_special_shell
+EOT
+
+WD=`pwd`
+cat > test.conf <<EOT
+rule
+ map[[0]] $WD/passwd.rush : \${user} 1 7 /nologin
+EOT
+
+set -e
+echo Matching map
+rush -C none -Dcmdline,argv,prog -c 'command arg' test.conf
+
+echo No match, return default
+> passwd.rush
+rush -C none -Dcmdline,argv,prog -c 'command arg' test.conf
+
+echo No match, no default
+cat > test.conf <<EOT
+rule
+ map[[0]] $WD/passwd.rush : \${user} 1 7
+EOT
+rush -C none -Dcmdline,argv,prog -c 'command arg' test.conf
+],
+[0],
+[Matching map
+{
+ "cmdline":"/rush_special_shell arg",
+ "argv":[[
+ "/rush_special_shell",
+ "arg"
+ ]],
+ "prog":null
+}
+No match, return default
+{
+ "cmdline":"/nologin arg",
+ "argv":[[
+ "/nologin",
+ "arg"
+ ]],
+ "prog":null
+}
+No match, no default
+{
+ "cmdline":"command arg",
+ "argv":[[
+ "command",
+ "arg"
+ ]],
+ "prog":null
+}
+])
+
+AT_CLEANUP
diff --git a/tests/newgrp.at b/tests/newgrp.at
new file mode 100644
index 0000000..2b04848
--- /dev/null
+++ b/tests/newgrp.at
@@ -0,0 +1,34 @@
+# This file is part of GNU Rush.
+# Copyright (C) 2016 Sergey Poznyakoff
+#
+# GNU Rush is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Rush is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([newgrp])
+AT_KEYWORDS([newg