From c9e72e0eed21a1c8c679bc094b0960dabf31c84d Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 18 Jan 2014 09:39:16 -0600 Subject: NSH: Add a break command; if-then-else and looping behavior can not be configured out of the build for small systems that need minimal scripting capability --- apps/ChangeLog.txt | 7 +++- apps/nshlib/Kconfig | 21 ++++++++++ apps/nshlib/README.txt | 21 ++++++++++ apps/nshlib/nsh.h | 21 +++++++++- apps/nshlib/nsh_command.c | 8 ++++ apps/nshlib/nsh_parse.c | 86 +++++++++++++++++++++++++++++++------- apps/nshlib/nsh_script.c | 4 +- nuttx/Documentation/NuttShell.html | 49 ++++++++++++++++++---- 8 files changed, 191 insertions(+), 26 deletions(-) diff --git a/apps/ChangeLog.txt b/apps/ChangeLog.txt index fb3bf2979..a5204eab9 100644 --- a/apps/ChangeLog.txt +++ b/apps/ChangeLog.txt @@ -796,4 +796,9 @@ a command to be on the same line as the then and else tokens like: "if true; then echo true; else echo false; fi". Much more like bash! (2014-1-17). - + * apps/nshlib/Kconfig, README.txt, nsh.h, nsh_command.c, and + nsh_script.c: Add an option to conditionally compile out support for + loop and for if-then-else-fi sequence (2014-1-17). + * apps/nshlib/nsh.h, nsh_command.c, and nsh_parse.c: Add a break + command that can be executed with a loop to terminate the loop + immediately (2014-1-17). diff --git a/apps/nshlib/Kconfig b/apps/nshlib/Kconfig index df1a9c518..7fb6a52b3 100644 --- a/apps/nshlib/Kconfig +++ b/apps/nshlib/Kconfig @@ -345,6 +345,27 @@ config NSH_DISABLESCRIPT if-then[-else]-fi construct. This would only be set on systems where a minimal footprint is a necessity and scripting is not. +if !NSH_DISABLESCRIPT + +config NSH_DISABLE_ITEF + bool "Disable if-then-else-fi" + default n + ---help--- + This can be set to 'y' to suppress support for if-then-else-fi + sequences in scripts. This would only be set on systems where + some minimal scripting is required but if-then-else-fi is not. + +config NSH_DISABLE_LOOPS + bool "Disable loops" + default n + ---help--- + This can be set to 'y' to suppress support for while-do-done and + until-do-done sequences in scripts. This would only be set on + systems where some minimal scripting is required but looping + is not. + +endif # !NSH_DISABLESCRIPT + config NSH_DISABLEBG bool "Disable background commands" default n diff --git a/apps/nshlib/README.txt b/apps/nshlib/README.txt index 1e4731066..cdf7600bf 100644 --- a/apps/nshlib/README.txt +++ b/apps/nshlib/README.txt @@ -7,6 +7,7 @@ apps/nshlib - Console/NSH Front End - Command Overview - Conditional Command Execution + - Looping - Built-In Variables - Current Working Directory Environment Variables @@ -121,6 +122,12 @@ Looping Execute as long as has a non-zero exit status. + A break command is also supported. The break command is only valid + within the body of the a while or until loop, between the do and done + tokens. If the break command is executed within the body of a loop, the + loop will immediately terminate and execution will continue with the + next command immediately following the done token. + Built-In Variables ^^^^^^^^^^^^^^^^^^ @@ -1094,6 +1101,20 @@ NSH-Specific Configuration Settings if-then[-else]-fi construct. This would only be set on systems where a minimal footprint is a necessity and scripting is not. + * CONFIG_NSH_DISABLE_ITEF + + If scripting is enabled, then then this option can be selected to + suppress support for if-then-else-fi sequences in scripts. This would + only be set on systems where some minimal scripting is required but + if-then-else-fi is not. + + * CONFIG_NSH_DISABLE_LOOPS + + If scripting is enabled, then then this option can be selected + suppress support for while-do-done and until-do-done sequences in + scripts. This would only be set on systems where some minimal + scripting is required but looping is not. + * CONFIG_NSH_DISABLEBG This can be set to 'y' to suppress support for background commands. This setting disables the 'nice' command prefix and diff --git a/apps/nshlib/nsh.h b/apps/nshlib/nsh.h index 4786692f7..2ea49706d 100644 --- a/apps/nshlib/nsh.h +++ b/apps/nshlib/nsh.h @@ -453,6 +453,8 @@ /**************************************************************************** * Public Types ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_ITEF /* State when parsing and if-then-else sequence */ enum nsh_itef_e @@ -472,7 +474,9 @@ struct nsh_itef_s uint8_t ie_unused : 4; uint8_t ie_state : 2; /* If-then-else state (see enum nsh_itef_e) */ }; +#endif +#ifndef CONFIG_NSH_DISABLE_LOOPS /* State when parsing and while-do-done or until-do-done sequence */ enum nsh_lp_e @@ -490,8 +494,12 @@ struct nsh_loop_s uint8_t lp_enable : 1; /* Loop command processing is enabled */ uint8_t lp_unused : 5; uint8_t lp_state : 2; /* Loop state (see enume nsh_lp_e) */ +#ifndef CONFIG_NSH_DISABLE_ITEF + uint8_t lp_iendx; /* Saved if-then-else-fi index */ +#endif long lp_topoffs; /* Top of loop file offset */ }; +#endif /* These structure provides the overall state of the parser */ @@ -510,19 +518,27 @@ struct nsh_parser_s #ifndef CONFIG_NSH_DISABLESCRIPT FILE *np_stream; /* Stream of current script */ +#ifndef CONFIG_NSH_DISABLE_LOOPS long np_foffs; /* File offset to the beginning of a line */ #ifndef NSH_DISABLE_SEMICOLON uint16_t np_loffs; /* Byte offset to the beginning of a command */ bool np_jump; /* "Jump" to the top of the loop */ #endif - uint8_t np_iendx; /* Current index into np_iestate[] */ uint8_t np_lpndx; /* Current index into np_lpstate[] */ +#endif +#ifndef CONFIG_NSH_DISABLE_ITEF + uint8_t np_iendx; /* Current index into np_iestate[] */ +#endif /* This is a stack of parser state information. */ +#ifndef CONFIG_NSH_DISABLE_ITEF struct nsh_itef_s np_iestate[CONFIG_NSH_NESTDEPTH]; +#endif +#ifndef CONFIG_NSH_DISABLE_LOOPS struct nsh_loop_s np_lpstate[CONFIG_NSH_NESTDEPTH]; #endif +#endif }; struct nsh_vtbl_s; /* Defined in nsh_console.h */ @@ -643,6 +659,9 @@ void nsh_usbtrace(void); /* Shell command handlers */ +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS) + int cmd_break(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); +#endif #ifndef CONFIG_NSH_DISABLE_ECHO int cmd_echo(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); #endif diff --git a/apps/nshlib/nsh_command.c b/apps/nshlib/nsh_command.c index 6ae3acfa7..ea028d463 100644 --- a/apps/nshlib/nsh_command.c +++ b/apps/nshlib/nsh_command.c @@ -119,6 +119,10 @@ static const struct cmdmap_s g_cmdmap[] = # endif #endif +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS) + { "break", cmd_break, 1, 1, NULL }, +#endif + #if CONFIG_NFILE_DESCRIPTORS > 0 # ifndef CONFIG_NSH_DISABLE_CAT { "cat", cmd_cat, 2, CONFIG_NSH_MAXARGUMENTS, " [ [ ...]]" }, @@ -465,6 +469,7 @@ static inline void help_usage(FAR struct nsh_vtbl_s *vtbl) nsh_output(vtbl, " [> |>> ]\n\n"); #endif #ifndef CONFIG_NSH_DISABLESCRIPT +#ifndef CONFIG_NSH_DISABLE_ITEF nsh_output(vtbl, "OR\n"); nsh_output(vtbl, " if \n"); nsh_output(vtbl, " then\n"); @@ -472,6 +477,8 @@ static inline void help_usage(FAR struct nsh_vtbl_s *vtbl) nsh_output(vtbl, " else\n"); nsh_output(vtbl, " [sequence of ]\n"); nsh_output(vtbl, " fi\n\n"); +#endif +#ifndef CONFIG_NSH_DISABLE_LOOPS nsh_output(vtbl, "OR\n"); nsh_output(vtbl, " while \n"); nsh_output(vtbl, " do\n"); @@ -483,6 +490,7 @@ static inline void help_usage(FAR struct nsh_vtbl_s *vtbl) nsh_output(vtbl, " [sequence of ]\n"); nsh_output(vtbl, " done\n\n"); #endif +#endif } #endif diff --git a/apps/nshlib/nsh_parse.c b/apps/nshlib/nsh_parse.c index c7505f34d..9641cd416 100644 --- a/apps/nshlib/nsh_parse.c +++ b/apps/nshlib/nsh_parse.c @@ -160,14 +160,22 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr, FAR NSH_MEMLIST_TYPE *memlist); #ifndef CONFIG_NSH_DISABLESCRIPT +#ifndef CONFIG_NSH_DISABLE_LOOPS static bool nsh_loop_enabled(FAR struct nsh_vtbl_s *vtbl); +#endif +#ifndef CONFIG_NSH_DISABLE_ITEF static bool nsh_itef_enabled(FAR struct nsh_vtbl_s *vtbl); +#endif static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl); +#ifndef CONFIG_NSH_DISABLE_LOOPS static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist); +#endif +#ifndef CONFIG_NSH_DISABLE_ITEF static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist); #endif +#endif #ifndef CONFIG_NSH_DISABLEBG static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, @@ -396,6 +404,7 @@ static int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, bool result) struct nsh_parser_s *np = &vtbl->np; #ifndef CONFIG_NSH_DISABLESCRIPT +#ifndef CONFIG_NSH_DISABLE_LOOPS /* Check if we are waiting for the condition associated with a while * token. * @@ -421,22 +430,26 @@ static int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, bool result) * status. */ - else if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_UNTIL) + else if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_UNTIL) { np->np_fail = false; np->np_lpstate[np->np_lpndx].lp_enable = (result != OK); return OK; } + else +#endif +#ifndef CONFIG_NSH_DISABLE_ITEF /* Check if we are waiting for the condition associated with an if token */ - else if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF) + if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF) { np->np_fail = false; np->np_iestate[np->np_iendx].ie_ifcond = result; return OK; } else +#endif #endif { np->np_fail = result; @@ -1369,7 +1382,7 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr, * Name: nsh_loop_enabled ****************************************************************************/ -#ifndef CONFIG_NSH_DISABLESCRIPT +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS) static bool nsh_loop_enabled(FAR struct nsh_vtbl_s *vtbl) { FAR struct nsh_parser_s *np = &vtbl->np; @@ -1388,13 +1401,15 @@ static bool nsh_loop_enabled(FAR struct nsh_vtbl_s *vtbl) return true; } +#else +# define nsh_loop_enabled(vtbl) true #endif /**************************************************************************** * Name: nsh_itef_enabled ****************************************************************************/ -#ifndef CONFIG_NSH_DISABLESCRIPT +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_ITEF) static bool nsh_itef_enabled(FAR struct nsh_vtbl_s *vtbl) { FAR struct nsh_parser_s *np = &vtbl->np; @@ -1420,6 +1435,8 @@ static bool nsh_itef_enabled(FAR struct nsh_vtbl_s *vtbl) return ret; } +#else +# define nsh_itef_enabled(vtbl) true #endif /**************************************************************************** @@ -1442,7 +1459,7 @@ static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl) * Name: nsh_loop ****************************************************************************/ -#ifndef CONFIG_NSH_DISABLESCRIPT +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS) static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist) { @@ -1476,7 +1493,10 @@ static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, /* Verify that "while" or "until" is valid in this context */ - if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF || + if ( +#ifndef CONFIG_NSH_DISABLE_ITEF + np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF || +#endif np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_WHILE || np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_UNTIL || np->np_stream == NULL || np->np_foffs < 0) @@ -1509,6 +1529,9 @@ static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, np->np_lpndx++; np->np_lpstate[np->np_lpndx].lp_state = state; np->np_lpstate[np->np_lpndx].lp_enable = enable; +#ifndef CONFIG_NSH_DISABLE_ITEF + np->np_lpstate[np->np_lpndx].lp_iendx = np->np_iendx; +#endif np->np_lpstate[np->np_lpndx].lp_topoffs = offset; } @@ -1516,14 +1539,9 @@ static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, else if (strcmp(cmd, "do") == 0) { - /* Get the cmd following the "do" -- there shouldn't be one */ + /* Get the cmd following the "do" -- there may or may not be one */ *ppcmd = nsh_argument(vtbl, saveptr, memlist); - if (*ppcmd) - { - nsh_output(vtbl, g_fmtarginvalid, "do"); - goto errout; - } /* Verify that "do" is valid in this context */ @@ -1632,7 +1650,7 @@ errout: * Name: nsh_itef ****************************************************************************/ -#ifndef CONFIG_NSH_DISABLESCRIPT +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_ITEF) static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist) { @@ -1982,6 +2000,7 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) cmd = nsh_argument(vtbl, &saveptr, &memlist); #ifndef CONFIG_NSH_DISABLESCRIPT +#ifndef CONFIG_NSH_DISABLE_LOOPS /* Handle while-do-done and until-do-done loops */ if (nsh_loop(vtbl, &cmd, &saveptr, &memlist) != 0) @@ -1989,7 +2008,9 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) NSH_MEMLIST_FREE(&memlist); return nsh_saveresult(vtbl, true); } +#endif +#ifndef CONFIG_NSH_DISABLE_ITEF /* Handle if-then-else-fi */ if (nsh_itef(vtbl, &cmd, &saveptr, &memlist) != 0) @@ -1997,6 +2018,8 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) NSH_MEMLIST_FREE(&memlist); return nsh_saveresult(vtbl, true); } + +#endif #endif /* Handle nice */ @@ -2137,7 +2160,7 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) return nsh_parse_command(vtbl, cmdline); #else -#ifndef CONFIG_NSH_DISABLESCRIPT +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS) FAR struct nsh_parser_s *np = &vtbl->np; #endif FAR char *start = cmdline; @@ -2151,13 +2174,13 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) * at the top of the loop. */ -#ifndef CONFIG_NSH_DISABLESCRIPT +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS) for (np->np_jump = false; !np->np_jump; ) #else for (;;) #endif { -#ifndef CONFIG_NSH_DISABLESCRIPT +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS) /* Save the offset on the line to the start of the command */ np->np_loffs = (uint16_t)(working - cmdline); @@ -2241,3 +2264,34 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) #endif #endif } + +/**************************************************************************** + * Name: cmd_break + ****************************************************************************/ + +#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS) +int cmd_break(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) +{ + FAR struct nsh_parser_s *np = &vtbl->np; + + /* Break outside of a loop is ignored */ + + if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_DO) + { +#ifndef CONFIG_NSH_DISABLE_ITEF + /* Yes... pop the original if-then-else-if state */ + + np->np_iendx = np->np_lpstate[np->np_lpndx].lp_iendx; +#endif + /* Disable all command processing until 'done' is encountered. */ + + np->np_lpstate[np->np_lpndx].lp_enable = false; + } + + /* No syntax errors are detected(?). Break is a nop everywhere except + * the supported context. + */ + + return OK; +} +#endif diff --git a/apps/nshlib/nsh_script.c b/apps/nshlib/nsh_script.c index e5c1958a1..b115dc8fb 100644 --- a/apps/nshlib/nsh_script.c +++ b/apps/nshlib/nsh_script.c @@ -129,10 +129,11 @@ int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, do { - /* Get the next line of input from the file */ + /* Flush any output generated by the previous line */ fflush(stdout); +#ifndef CONFIG_NSH_DISABLE_LOOPS /* Get the current file position. This is used to control * looping. If a loop begins in the next line, then this file * offset will be needed to locate the top of the loop in the @@ -146,6 +147,7 @@ int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, { nsh_output(vtbl, g_fmtcmdfailed, "loop", "ftell", NSH_ERRNO); } +#endif /* Now read the next line from the script file */ diff --git a/nuttx/Documentation/NuttShell.html b/nuttx/Documentation/NuttShell.html index 2eeed4e67..0be5cf5a0 100644 --- a/nuttx/Documentation/NuttShell.html +++ b/nuttx/Documentation/NuttShell.html @@ -8,7 +8,7 @@

NuttShell (NSH)

-

Last Updated: January 17, 2014

+

Last Updated: January 18, 2014

@@ -616,7 +616,7 @@ fi -

+

Looping Constructs. while-do-done and until-do-done looping constructs are also supported. These work from the command line but are primarily intended for use within NSH scripts (see the sh command). @@ -646,7 +646,13 @@ done

- + + +

The break Command. + A break command is also supported. + The break command is only valid within the body of the a while or until loop, between the do and done tokens. + If the break command is executed within the body of a loop, the loop will immediately terminate and execution will continue with the next command immediately following the done token. +

@@ -2799,6 +2805,20 @@ set FOOBAR ABC_${FOO}_${BAR} where a minimal footprint is a necessity and scripting is not. + + + + + + + +
CONFIG_NSH_DISABLE_ITEF + If scripting is enabled, then then this option can be selected to suppress support for if-then-else-fi sequences in scripts. + This would only be set on systems where some minimal scripting is required but if-then-else-fi is not. +
CONFIG_NSH_DISABLE_LOOPS + If scripting is enabled, then then this option can be selected suppress support for while-do-done and until-do-done sequences in scripts. + This would only be set on systems where some minimal scripting is required but looping is not. +
CONFIG_NSH_DISABLEBG @@ -3979,7 +3999,7 @@ mount -t vfat /dev/ram1 /tmp
- + - + -- cgit v1.2.3
+
+
  • /etc/init.d/rcS
  • exec
  • exec_builtin()
  • @@ -4086,6 +4116,9 @@ mount -t vfat /dev/ram1 /tmp
  • mb
  • md5
  • mh
  • +
    +