diff --git a/ChangeLog b/ChangeLog
index 75dd9c1631..a27c01c5a0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -440,4 +440,5 @@
* fseek() needs to discard bytes buffered by ungetc().
* Corrected ftell() return value.
* Added fsetpos() and fgetpos().
+ * NSH now supports 'test' and '[' commands
diff --git a/Documentation/NuttX.html b/Documentation/NuttX.html
index 0b33ac548b..c96db7b607 100644
--- a/Documentation/NuttX.html
+++ b/Documentation/NuttX.html
@@ -456,7 +456,7 @@
- The 2tth release of NuttX (nuttx-0.3.13) is available for download
+ The 25th release of NuttX (nuttx-0.3.13) is available for download
from the SourceForge
website.
The change log associated with the release is available here.
@@ -1067,6 +1067,7 @@ nuttx-0.3.14 2008-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr>
* fseek() needs to discard bytes buffered by ungetc().
* Corrected ftell() return value.
* Added fsetpos() and fgetpos().
+ * NSH now supports 'test' and '[' commands
pascal-0.1.3 2008-xx-xx Gregory Nutt <spudmonkey@racsa.co.cr>
diff --git a/examples/README.txt b/examples/README.txt
index 82055e3978..4847d3d25e 100644
--- a/examples/README.txt
+++ b/examples/README.txt
@@ -37,6 +37,7 @@ examples/nsh
Command Depends on Configuration
---------- --------------------------
+ [ !CONFIG_EXAMPLES_NSH_DISABLESCRIPT
cat CONFIG_NFILE_DESCRIPTORS > 0
cd !CONFIG_DISABLE_ENVIRON && CONFIG_NFILE_DESCRIPTORS > 0
cp CONFIG_NFILE_DESCRIPTORS > 0
@@ -54,15 +55,38 @@ examples/nsh
mount !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_FAT
ps --
pwd !CONFIG_DISABLE_ENVIRON && CONFIG_NFILE_DESCRIPTORS > 0
- set !CONFIG_DISABLE_ENVIRON
- sleep !CONFIG_DISABLE_SIGNALS
- sh CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0
rm !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0
rmdir !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0
+ set !CONFIG_DISABLE_ENVIRON
+ sh CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0 && !CONFIG_EXAMPLES_NSH_DISABLESCRIPT
+ sleep !CONFIG_DISABLE_SIGNALS
+ test !CONFIG_EXAMPLES_NSH_DISABLESCRIPT
umount !CONFIG_DISABLE_MOUNTPOINT && CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_FS_FAT
unset !CONFIG_DISABLE_ENVIRON
usleep !CONFIG_DISABLE_SIGNALS
+ Test syntax:
+
+ expression = simple-expression | !expression |
+ expression -o expression | expression -a expression
+
+ simple-expression = unary-expression | binary-expression
+
+ unary-expression = string-unary | file-unary
+
+ string-unary = -n string | -z string
+
+ file-unary = -b file | -c file | -d file | -e file | -f file |
+ -r file | -s file | -w file
+
+ binary-expression = string-binary | numeric-binary
+
+ string-binary = string = string | string == string | string != string
+
+ numeric-binary = integer -eq integer | integer -ge integer |
+ integer -gt integer | integer -le integer |
+ integer -lt integer | integer -ne integer
+
Other behavior of NSH can be modified with the following settings in
the configs//defconfig file:
@@ -87,6 +111,12 @@ examples/nsh
The maximum number of nested if-then[-else]-fi sequences that
are permissable. Default: 3
+ * CONFIG_EXAMPLES_NSH_DISABLESCRIPT
+ This can be set to 'y' to suppress support for scripting. This
+ setting disables the 'sh', 'test', and '[' commands and the
+ if-then[-else]-fi construct. This would only be set on systems
+ where a minimal footprint is a necessity and scripting is not.
+
* CONFIG_EXAMPLES_NSH_CONSOLE
If CONFIG_EXAMPLES_NSH_CONSOLE is set to 'y', then a serial
console front-end is selected.
diff --git a/examples/nsh/Makefile b/examples/nsh/Makefile
index db2f26677e..5ff7e3c6a8 100644
--- a/examples/nsh/Makefile
+++ b/examples/nsh/Makefile
@@ -53,6 +53,10 @@ ifeq ($(CONFIG_EXAMPLES_NSH_TELNET),y)
CSRCS += nsh_telnetd.c
endif
+ifneq ($(CONFIG_EXAMPLES_NSH_DISABLESCRIPT),y)
+CSRCS += nsh_test.c
+endif
+
AOBJS = $(ASRCS:.S=$(OBJEXT))
COBJS = $(CSRCS:.c=$(OBJEXT))
diff --git a/examples/nsh/nsh.h b/examples/nsh/nsh.h
index 76e2701baa..7afade1951 100644
--- a/examples/nsh/nsh.h
+++ b/examples/nsh/nsh.h
@@ -163,7 +163,9 @@ struct nsh_parser_s
#endif
boolean np_redirect; /* TRUE: Output from the last command was re-directed */
boolean np_fail; /* TRUE: The last command failed */
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
ubyte np_ndx; /* Current index into np_st[] */
+#endif
#ifndef CONFIG_DISABLE_PTHREAD
int np_nice; /* "nice" value applied to last background cmd */
#endif
@@ -172,7 +174,9 @@ struct nsh_parser_s
* execution of commands that span multiple lines (like if-then-else-fi)
*/
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
struct nsh_state_s np_st[CONFIG_EXAMPLES_NSH_NESTDEPTH];
+#endif
};
struct nsh_vtbl_s
@@ -207,11 +211,11 @@ typedef int (*cmd_t)(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
extern const char g_nshgreeting[];
extern const char g_nshprompt[];
+extern const char g_nshsyntax[];
extern const char g_fmtargrequired[];
extern const char g_fmtarginvalid[];
extern const char g_fmtargrange[];
extern const char g_fmtcmdnotfound[];
-extern const char g_fmtcmdnotimpl[];
extern const char g_fmtnosuch[];
extern const char g_fmttoomanyargs[];
extern const char g_fmtdeepnesting[];
@@ -256,13 +260,18 @@ extern int cmd_mw(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
extern int cmd_mem(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
extern int cmd_ps(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
+extern int cmd_test(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
+extern int cmd_lbracket(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
+#endif
+
#if CONFIG_NFILE_DESCRIPTORS > 0
extern int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
extern int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
extern int cmd_ls(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
-# if CONFIG_NFILE_STREAMS > 0
+# if CONFIG_NFILE_STREAMS > 0 && !defined(CONFIG_EXAMPLES_NSH_DISABLESCRIPT)
extern int cmd_sh(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
-# endif /* CONFIG_NFILE_STREAMS */
+# endif /* CONFIG_NFILE_STREAMS && !CONFIG_EXAMPLES_NSH_DISABLESCRIPT */
# ifndef CONFIG_DISABLE_MOUNTPOINT
extern int cmd_mkdir(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
extern int cmd_mkfifo(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
diff --git a/examples/nsh/nsh_fscmds.c b/examples/nsh/nsh_fscmds.c
index b4f5161c1a..458f1130e9 100644
--- a/examples/nsh/nsh_fscmds.c
+++ b/examples/nsh/nsh_fscmds.c
@@ -905,7 +905,7 @@ int cmd_rmdir(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
* Name: cmd_sh
****************************************************************************/
-#if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0
+#if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0 && !defined(CONFIG_EXAMPLES_NSH_DISABLESCRIPT)
int cmd_sh(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
char *fullpath;
diff --git a/examples/nsh/nsh_main.c b/examples/nsh/nsh_main.c
index f8c9bdb242..01f5ed6908 100644
--- a/examples/nsh/nsh_main.c
+++ b/examples/nsh/nsh_main.c
@@ -128,6 +128,9 @@ static const char g_failure[] = "1";
static const struct cmdmap_s g_cmdmap[] =
{
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
+ { "[", cmd_lbracket, 4, NSH_MAX_ARGUMENTS, " ]" },
+#endif
#if CONFIG_NFILE_DESCRIPTORS > 0
{ "cat", cmd_cat, 2, NSH_MAX_ARGUMENTS, " [ [ ...]]" },
#ifndef CONFIG_DISABLE_ENVIRON
@@ -169,19 +172,22 @@ static const struct cmdmap_s g_cmdmap[] =
#if CONFIG_NFILE_DESCRIPTORS > 0 && !defined(CONFIG_DISABLE_ENVIRON)
{ "pwd", cmd_pwd, 1, 1, NULL },
#endif
-#ifndef CONFIG_DISABLE_ENVIRON
- { "set", cmd_set, 3, 3, " " },
-#endif
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0
{ "rm", cmd_rm, 2, 2, "" },
{ "rmdir", cmd_rmdir, 2, 2, "" },
#endif
-#if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0
+#ifndef CONFIG_DISABLE_ENVIRON
+ { "set", cmd_set, 3, 3, " " },
+#endif
+#if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0 && !defined(CONFIG_EXAMPLES_NSH_DISABLESCRIPT)
{ "sh", cmd_sh, 2, 2, "" },
#endif /* CONFIG_NFILE_DESCRIPTORS && CONFIG_NFILE_STREAMS */
#ifndef CONFIG_DISABLE_SIGNALS
{ "sleep", cmd_sleep, 2, 2, "" },
#endif /* CONFIG_DISABLE_SIGNALS */
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
+ { "test", cmd_test, 3, NSH_MAX_ARGUMENTS, "" },
+#endif
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0
# ifdef CONFIG_FS_FAT /* Need at least one filesytem in configuration */
{ "umount", cmd_umount, 2, 2, "" },
@@ -202,11 +208,11 @@ static const struct cmdmap_s g_cmdmap[] =
const char g_nshgreeting[] = "NuttShell (NSH)\n";
const char g_nshprompt[] = "nsh> ";
+const char g_nshsyntax[] = "nsh: %s: syntax error\n";
const char g_fmtargrequired[] = "nsh: %s: missing required argument(s)\n";
const char g_fmtarginvalid[] = "nsh: %s: argument invalid\n";
const char g_fmtargrange[] = "nsh: %s: value out of range\n";
const char g_fmtcmdnotfound[] = "nsh: %s: command not found\n";
-const char g_fmtcmdnotimpl[] = "nsh: %s: command not implemented\n";
const char g_fmtnosuch[] = "nsh: %s: no such %s: %s\n";
const char g_fmttoomanyargs[] = "nsh: %s: too many arguments\n";
const char g_fmtdeepnesting[] = "nsh: %s: nesting too deep\n";
@@ -237,6 +243,7 @@ static int cmd_help(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
#else
nsh_output(vtbl, " [> |>> ]\n");
#endif
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
nsh_output(vtbl, "OR\n");
nsh_output(vtbl, " if \n");
nsh_output(vtbl, " then\n");
@@ -244,6 +251,7 @@ static int cmd_help(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
nsh_output(vtbl, " else\n");
nsh_output(vtbl, " [sequence of ]\n");
nsh_output(vtbl, " fi\n");
+#endif
nsh_output(vtbl, "Where is one of:\n");
for (ptr = g_cmdmap; ptr->cmd; ptr++)
{
@@ -569,6 +577,7 @@ char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr)
* Name: nsh_cmdenabled
****************************************************************************/
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
static inline boolean nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
{
struct nsh_parser_s *np = &vtbl->np;
@@ -593,11 +602,13 @@ static inline boolean nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
}
return ret;
}
+#endif
/****************************************************************************
* Name: nsh_ifthenelse
****************************************************************************/
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, FAR char **saveptr)
{
struct nsh_parser_s *np = &vtbl->np;
@@ -730,6 +741,7 @@ errout:
np->np_st[0].ns_ifcond = FALSE;
return ERROR;
}
+#endif
/****************************************************************************
* Name: nsh_saveresult
@@ -739,6 +751,7 @@ static inline int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, boolean result)
{
struct nsh_parser_s *np = &vtbl->np;
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
if (np->np_st[np->np_ndx].ns_state == NSH_PARSER_IF)
{
np->np_fail = FALSE;
@@ -746,6 +759,7 @@ static inline int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, boolean result)
return OK;
}
else
+#endif
{
np->np_fail = result;
return result ? ERROR : OK;
@@ -898,10 +912,12 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline)
/* Handler if-then-else-fi */
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
if (nsh_ifthenelse(vtbl, &cmd, &saveptr) != 0)
{
goto errout;
}
+#endif
/* Handle nice */
@@ -916,7 +932,11 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, char *cmdline)
* currently disabled.
*/
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
if (!cmd || !nsh_cmdenabled(vtbl))
+#else
+ if (!cmd)
+#endif
{
/* An empty line is not an error and an unprocessed command cannot
* generate an error, but neither should they change the last
diff --git a/examples/nsh/nsh_test.c b/examples/nsh/nsh_test.c
new file mode 100644
index 0000000000..a767a3ac4a
--- /dev/null
+++ b/examples/nsh/nsh_test.c
@@ -0,0 +1,446 @@
+/****************************************************************************
+ * examples/nsh/nsh_test.c
+ *
+ * Copyright (C) 2008 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt
+ *
+ * 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.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 syntax:
+ *
+ * expression = simple-expression | !expression |
+ * expression -o expression | expression -a expression
+ *
+ * simple-expression = unary-expression | binary-expression
+ *
+ * unary-expression = string-unary | file-unary
+ *
+ * string-unary = -n string | -z string
+ *
+ * file-unary = -b file | -c file | -d file | -e file | -f file |
+ * -r file | -s file | -w file
+ *
+ * binary-expression = string-binary | numeric-binary
+ *
+ * string-binary = string = string | string == string | string != string
+ *
+ * numeric-binary = integer -eq integer | integer -ge integer |
+ * integer -gt integer | integer -le integer |
+ * integer -lt integer | integer -ne integer
+ *
+ * Note that the smallest expression consists of two strings.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include
+
+#include
+#include
+#include
+
+#include "nsh.h"
+
+#ifndef CONFIG_EXAMPLES_NSH_DISABLESCRIPT
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+#define TEST_TRUE OK
+#define TEST_FALSE ERROR
+#define TEST_ERROR 1
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: binaryexpression
+ ****************************************************************************/
+
+static inline int binaryexpression(FAR struct nsh_vtbl_s *vtbl, char **argv)
+{
+ char *endptr;
+ long integer1;
+ long integer2;
+
+ /* STRING2 = STRING2 */
+
+ if (strcmp(argv[1], "=") == 0 || strcmp(argv[1], "==") == 0)
+ {
+ /* Return true if the strings are identical */
+
+ return strcmp(argv[0], argv[2]) == 0 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* STRING1 != STRING2 */
+
+ if (strcmp(argv[1], "!=") == 0)
+ {
+ /* Return true if the strings are different */
+
+ return strcmp(argv[0], argv[2]) != 0 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* The remaining operators assuming that the two values are integers */
+
+ integer1 = strtol(argv[0], &endptr, 0);
+ if (argv[0][0] == '\0' || *endptr != '\0')
+ {
+ return TEST_ERROR;
+ }
+
+ integer2 = strtol(argv[2], &endptr, 0);
+ if (argv[2][0] == '\0' || *endptr != '\0')
+ {
+ return TEST_ERROR;
+ }
+
+ /* INTEGER1 -eq INTEGER2 */
+
+ if (strcmp(argv[1], "-eq") == 0)
+ {
+ /* Return true if the strings are different */
+
+ return integer1 == integer2 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* INTEGER1 -ge INTEGER2 */
+
+ if (strcmp(argv[1], "-ge") == 0)
+ {
+ /* Return true if the strings are different */
+
+ return integer1 >= integer2 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* INTEGER1 -gt INTEGER2 */
+
+ if (strcmp(argv[1], "-gt") == 0)
+ {
+ /* Return true if the strings are different */
+
+ return integer1 > integer2 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* INTEGER1 -le INTEGER2 */
+
+ if (strcmp(argv[1], "-le") == 0)
+ {
+ /* Return true if the strings are different */
+
+ return integer1 <= integer2 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* INTEGER1 -lt INTEGER2 */
+
+ if (strcmp(argv[1], "-lt") == 0)
+ {
+ /* Return true if the strings are different */
+
+ return integer1 < integer2 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* INTEGER1 -ne INTEGER2 */
+
+ if (strcmp(argv[1], "-ne") == 0)
+ {
+ /* Return true if the strings are different */
+
+ return integer1 != integer2 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ return TEST_ERROR;
+}
+
+/****************************************************************************
+ * Name: unaryexpression
+ ****************************************************************************/
+
+static inline int unaryexpression(FAR struct nsh_vtbl_s *vtbl, char **argv)
+{
+ struct stat buf;
+ char *fullpath;
+ int ret;
+
+ /* -n STRING */
+
+ if (strcmp(argv[0], "-n") == 0)
+ {
+ /* Return true if the length of the string is non-zero */
+
+ return strlen(argv[1]) != 0 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* -z STRING */
+
+ if (strcmp(argv[0], "-z") == 0)
+ {
+ /* Return true if the length of the string is zero */
+
+ return strlen(argv[1]) == 0 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* All of the remaining assume that the following argument is the
+ * path to a file.
+ */
+
+ fullpath = nsh_getfullpath(vtbl, argv[1]);
+ if (!fullpath)
+ {
+ return TEST_FALSE;
+ }
+
+ ret = stat(fullpath, &buf);
+ nsh_freefullpath(fullpath);
+
+ if (ret != 0)
+ {
+ /* The file does not exist (or another error occurred) -- return FALSE */
+
+ return TEST_FALSE;
+ }
+
+ /* -b FILE */
+
+ if (strcmp(argv[0], "-b") == 0)
+ {
+ /* Return true if the path is a block device */
+
+ return S_ISBLK(buf.st_mode) ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* -c FILE */
+
+ if (strcmp(argv[0], "-c") == 0)
+ {
+ /* Return true if the path is a character device */
+
+ return S_ISCHR(buf.st_mode) ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* -d FILE */
+
+ if (strcmp(argv[0], "-d") == 0)
+ {
+ /* Return true if the path is a directory */
+
+ return S_ISDIR(buf.st_mode) ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* -e FILE */
+
+ if (strcmp(argv[0], "-e") == 0)
+ {
+ /* Return true if the file exists */
+
+ return TEST_TRUE;
+ }
+
+ /* -f FILE */
+
+ if (strcmp(argv[0], "-f") == 0)
+ {
+ /* Return true if the path refers to a regular file */
+
+ return S_ISREG(buf.st_mode) ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* -r FILE */
+
+ if (strcmp(argv[0], "-r") == 0)
+ {
+ /* Return true if the file is readable */
+
+ return (buf.st_mode & (S_IRUSR|S_IRGRP|S_IROTH)) != 0 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* -s FILE */
+
+ if (strcmp(argv[0], "-s") == 0)
+ {
+ /* Return true if the size of the file is greater than zero */
+
+ return buf.st_size > 0 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* -w FILE */
+
+ if (strcmp(argv[0], "-w") == 0)
+ {
+ /* Return true if the file is write-able */
+
+ return (buf.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0 ? TEST_TRUE : TEST_FALSE;
+ }
+
+ /* Unrecognized operator */
+
+ return TEST_ERROR;
+}
+
+/****************************************************************************
+ * Name: expression
+ ****************************************************************************/
+
+static int expression(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
+{
+ int value;
+ boolean valid;
+ int i = 0;
+
+ /* Check for unary operations on expressions */
+
+ if (strcmp(argv[0], "!") == 0)
+ {
+ if (argc < 2)
+ {
+ goto errout_syntax;
+ }
+ return expression(vtbl, argc-1, &argv[1]) == TEST_TRUE ? TEST_FALSE : TEST_TRUE;
+ }
+
+ /* Check for unary operations on simple, typed arguments */
+
+ else if (argv[0][0] == '-')
+ {
+ if (argc < 2)
+ {
+ goto errout_syntax;
+ }
+ i += 2;
+ value = unaryexpression(vtbl, argv);
+ }
+
+ /* Check for binary operations on simple, typed arguments */
+
+ else
+ {
+ if (argc < 3)
+ {
+ goto errout_syntax;
+ }
+ i += 3;
+ value = binaryexpression(vtbl, argv);
+ }
+
+ /* Test if there any failure */
+
+ if (value == TEST_ERROR)
+ {
+ goto errout_syntax;
+ }
+
+ /* Is there anything after the simple expression? */
+
+ if (i < argc)
+ {
+ /* EXPRESSION -a EXPRESSION */
+
+ if (strcmp(argv[i], "-a") == 0)
+ {
+ if (!valid)
+ {
+ goto errout_syntax;
+ }
+ else if (value != TEST_TRUE)
+ {
+ return TEST_FALSE;
+ }
+ else
+ {
+ i++;
+ return expression(vtbl, argc-i, &argv[i]);
+ }
+ }
+
+ /* EXPRESSION -o EXPRESSION */
+
+ else if (strcmp(argv[i], "-o") == 0)
+ {
+ if (!valid)
+ {
+ goto errout_syntax;
+ }
+ else if (value == TEST_TRUE)
+ {
+ return TEST_TRUE;
+ }
+ else
+ {
+ i++;
+ return expression(vtbl, argc-i, &argv[i]);
+ }
+ }
+ else
+ {
+ goto errout_syntax;
+ }
+ }
+ return value;
+
+errout_syntax:
+ nsh_output(vtbl, g_nshsyntax, "test");
+ return TEST_FALSE;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: cmd_test
+ ****************************************************************************/
+
+int cmd_test(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
+{
+ return expression(vtbl, argc-1, &argv[1]);
+}
+
+/****************************************************************************
+ * Name: cmd_lbracket
+ ****************************************************************************/
+
+int cmd_lbracket(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
+{
+ if (strcmp(argv[argc-1], "]") != 0)
+ {
+ nsh_output(vtbl, g_nshsyntax, argv[0]);
+ return ERROR;
+ }
+ else
+ {
+ return expression(vtbl, argc-2, &argv[1]);
+ }
+}
+
+#endif /* !CONFIG_EXAMPLES_NSH_DISABLESCRIPT */