diff --git a/examples/README.txt b/examples/README.txt index a3cf60a451..130ab5cb55 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -79,6 +79,10 @@ examples/nsh new threads are generated when a command is executed in background or as new TELNET connections are established. + * CONFIG_EXAMPLES_NSH_NESTDEPTH + The maximum number of nested if-then[-else]-fi sequences that + are permissable. Default: 3 + * 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/nsh.h b/examples/nsh/nsh.h index 574e032212..57c75e1bc0 100644 --- a/examples/nsh/nsh.h +++ b/examples/nsh/nsh.h @@ -87,6 +87,14 @@ # define CONFIG_EXAMPLES_NSH_STACKSIZE 4096 #endif +/* The maximum number of nested if-then[-else]-fi sequences that + * are permissable. + */ + +#ifndef CONFIG_EXAMPLES_NSH_NESTDEPTH +# define CONFIG_EXAMPLES_NSH_NESTDEPTH 3 +#endif + /* Define to enable dumping of all input/output buffers */ #undef CONFIG_EXAMPLES_NSH_TELNETD_DUMPBUFFER @@ -120,14 +128,27 @@ enum nsh_parser_e NSH_PARSER_ELSE }; +struct nsh_state_s +{ + ubyte ns_ifcond : 1; /* Value of command in 'if' statement */ + ubyte ns_disabled : 1; /* TRUE: Unconditionally disabled */ + ubyte ns_unused : 4; + ubyte ns_state : 2; /* Parser state (see enum nsh_parser_e) */ +}; + struct nsh_parser_s { boolean np_bg; /* TRUE: The last command executed in background */ boolean np_redirect; /* TRUE: Output from the last command was re-directed */ boolean np_fail; /* TRUE: The last command failed */ - boolean np_ifcond; /* Value of command in 'if' statement */ - ubyte np_state; /* Parser state (see enum nsh_parser_e) */ + ubyte np_ndx; /* Current index into np_st[] */ int np_nice; /* "nice" value applied to last background cmd */ + + /* This is a stack of parser state information. It supports nested + * execution of commands that span multiple lines (like if-then-else-fi) + */ + + struct nsh_state_s np_st[CONFIG_EXAMPLES_NSH_NESTDEPTH]; }; struct nsh_vtbl_s @@ -165,9 +186,11 @@ 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[]; extern const char g_fmtcontext[]; extern const char g_fmtcmdfailed[]; extern const char g_fmtcmdoutofmemory[]; +extern const char g_fmtinternalerror[]; /**************************************************************************** * Public Function Prototypes diff --git a/examples/nsh/nsh_main.c b/examples/nsh/nsh_main.c index 8c2999cf71..ff3398ea11 100644 --- a/examples/nsh/nsh_main.c +++ b/examples/nsh/nsh_main.c @@ -156,6 +156,7 @@ 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"; const char g_fmtcontext[] = "nsh: %s: not valid in this context\n"; #ifdef CONFIG_EXAMPLES_NSH_STRERROR const char g_fmtcmdfailed[] = "nsh: %s: %s failed: %s\n"; @@ -163,6 +164,7 @@ const char g_fmtcmdfailed[] = "nsh: %s: %s failed: %s\n"; const char g_fmtcmdfailed[] = "nsh: %s: %s failed: %d\n"; #endif const char g_fmtcmdoutofmemory[] = "nsh: %s: out of memory\n"; +const char g_fmtinternalerror[] = "nsh: %s: Internal error\n"; /**************************************************************************** * Private Functions @@ -433,13 +435,45 @@ char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr) return pbegin; } +/**************************************************************************** + * Name: nsh_cmdenabled + ****************************************************************************/ + +static inline boolean nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl) +{ + struct nsh_parser_s *np = &vtbl->np; + boolean ret = !np->np_st[np->np_ndx].ns_disabled; + if (ret) + { + switch (np->np_st[np->np_ndx].ns_state) + { + case NSH_PARSER_NORMAL : + case NSH_PARSER_IF: + default: + break; + + case NSH_PARSER_THEN: + ret = !np->np_st[np->np_ndx].ns_ifcond; + break; + + case NSH_PARSER_ELSE: + ret = np->np_st[np->np_ndx].ns_ifcond; + break; + } + } + return ret; +} + /**************************************************************************** * Name: nsh_ifthenelse ****************************************************************************/ static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, FAR char **saveptr) { + struct nsh_parser_s *np = &vtbl->np; FAR char *cmd = *ppcmd; + boolean disabled; + if (cmd) { /* Check if the command is preceeded by "if" */ @@ -457,12 +491,29 @@ static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, /* Verify that "if" is valid in this context */ - if (vtbl->np.np_state != NSH_PARSER_NORMAL) + if (np->np_st[np->np_ndx].ns_state != NSH_PARSER_NORMAL && + np->np_st[np->np_ndx].ns_state != NSH_PARSER_THEN && + np->np_st[np->np_ndx].ns_state != NSH_PARSER_ELSE) { nsh_output(vtbl, g_fmtcontext, "if"); goto errout; } - vtbl->np.np_state = NSH_PARSER_IF; + + /* Check if we have exceeded the maximum depth of nesting */ + + if (np->np_ndx >= CONFIG_EXAMPLES_NSH_NESTDEPTH-1) + { + nsh_output(vtbl, g_fmtdeepnesting, "if"); + goto errout; + } + + /* "Push" the old state and set the new state */ + + disabled = !nsh_cmdenabled(vtbl); + np->np_ndx++; + np->np_st[np->np_ndx].ns_state = NSH_PARSER_IF; + np->np_st[np->np_ndx].ns_disabled = disabled; + np->np_st[np->np_ndx].ns_ifcond = FALSE; } else if (strcmp(cmd, "then") == 0) { @@ -477,12 +528,12 @@ static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, /* Verify that "then" is valid in this context */ - if (vtbl->np.np_state != NSH_PARSER_IF) + if (np->np_st[np->np_ndx].ns_state != NSH_PARSER_IF) { nsh_output(vtbl, g_fmtcontext, "then"); goto errout; } - vtbl->np.np_state = NSH_PARSER_THEN; + np->np_st[np->np_ndx].ns_state = NSH_PARSER_THEN; } else if (strcmp(cmd, "else") == 0) { @@ -497,12 +548,12 @@ static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, /* Verify that "then" is valid in this context */ - if (vtbl->np.np_state != NSH_PARSER_THEN) + if (np->np_st[np->np_ndx].ns_state != NSH_PARSER_THEN) { nsh_output(vtbl, g_fmtcontext, "else"); goto errout; } - vtbl->np.np_state = NSH_PARSER_ELSE; + np->np_st[np->np_ndx].ns_state = NSH_PARSER_ELSE; } else if (strcmp(cmd, "fi") == 0) { @@ -517,14 +568,24 @@ static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, /* Verify that "fi" is valid in this context */ - if (vtbl->np.np_state != NSH_PARSER_THEN && vtbl->np.np_state != NSH_PARSER_ELSE) + if (np->np_st[np->np_ndx].ns_state != NSH_PARSER_THEN && + np->np_st[np->np_ndx].ns_state != NSH_PARSER_ELSE) { nsh_output(vtbl, g_fmtcontext, "fi"); goto errout; } - vtbl->np.np_state = NSH_PARSER_NORMAL; + + if (np->np_ndx < 1) /* Shouldn't happen */ + { + nsh_output(vtbl, g_fmtinternalerror, "if"); + goto errout; + } + + /* "Pop" the previous state */ + + np->np_ndx--; } - else if (vtbl->np.np_state == NSH_PARSER_IF) + else if (np->np_st[np->np_ndx].ns_state == NSH_PARSER_IF) { nsh_output(vtbl, g_fmtcontext, cmd); goto errout; @@ -533,48 +594,30 @@ static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, return OK; errout: - vtbl->np.np_state = NSH_PARSER_NORMAL; + np->np_ndx = 0; + np->np_st[0].ns_state = NSH_PARSER_NORMAL; + np->np_st[0].ns_disabled = FALSE; + np->np_st[0].ns_ifcond = FALSE; return ERROR; } -/**************************************************************************** - * Name: nsh_cmdenabled - ****************************************************************************/ - -static inline boolean nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl) -{ - switch (vtbl->np.np_state) - { - case NSH_PARSER_NORMAL : - case NSH_PARSER_IF: - default: - break; - - case NSH_PARSER_THEN: - return !vtbl->np.np_ifcond; - - case NSH_PARSER_ELSE: - return vtbl->np.np_ifcond; - } - return TRUE; -} - /**************************************************************************** * Name: nsh_saveresult ****************************************************************************/ static inline int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, boolean result) { - vtbl->np.np_fail = result; - if (vtbl->np.np_state == NSH_PARSER_IF) + struct nsh_parser_s *np = &vtbl->np; + + if (np->np_st[np->np_ndx].ns_state == NSH_PARSER_IF) { - vtbl->np.np_fail = FALSE; - vtbl->np.np_ifcond = result; + np->np_fail = FALSE; + np->np_st[np->np_ndx].ns_ifcond = result; return OK; } else { - vtbl->np.np_fail = result; + np->np_fail = result; return result ? ERROR : OK; } }