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 */