Support nested if-then[-else]-fi

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@832 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2008-08-19 23:16:45 +00:00
parent cab9dbe225
commit 07c715e09c
3 changed files with 109 additions and 39 deletions

View File

@ -79,6 +79,10 @@ examples/nsh
new threads are generated when a command is executed in background new threads are generated when a command is executed in background
or as new TELNET connections are established. 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 * CONFIG_EXAMPLES_NSH_CONSOLE
If CONFIG_EXAMPLES_NSH_CONSOLE is set to 'y', then a serial If CONFIG_EXAMPLES_NSH_CONSOLE is set to 'y', then a serial
console front-end is selected. console front-end is selected.

View File

@ -87,6 +87,14 @@
# define CONFIG_EXAMPLES_NSH_STACKSIZE 4096 # define CONFIG_EXAMPLES_NSH_STACKSIZE 4096
#endif #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 */ /* Define to enable dumping of all input/output buffers */
#undef CONFIG_EXAMPLES_NSH_TELNETD_DUMPBUFFER #undef CONFIG_EXAMPLES_NSH_TELNETD_DUMPBUFFER
@ -120,14 +128,27 @@ enum nsh_parser_e
NSH_PARSER_ELSE 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 struct nsh_parser_s
{ {
boolean np_bg; /* TRUE: The last command executed in background */ boolean np_bg; /* TRUE: The last command executed in background */
boolean np_redirect; /* TRUE: Output from the last command was re-directed */ boolean np_redirect; /* TRUE: Output from the last command was re-directed */
boolean np_fail; /* TRUE: The last command failed */ boolean np_fail; /* TRUE: The last command failed */
boolean np_ifcond; /* Value of command in 'if' statement */ ubyte np_ndx; /* Current index into np_st[] */
ubyte np_state; /* Parser state (see enum nsh_parser_e) */
int np_nice; /* "nice" value applied to last background cmd */ 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 struct nsh_vtbl_s
@ -165,9 +186,11 @@ extern const char g_fmtcmdnotfound[];
extern const char g_fmtcmdnotimpl[]; extern const char g_fmtcmdnotimpl[];
extern const char g_fmtnosuch[]; extern const char g_fmtnosuch[];
extern const char g_fmttoomanyargs[]; extern const char g_fmttoomanyargs[];
extern const char g_fmtdeepnesting[];
extern const char g_fmtcontext[]; extern const char g_fmtcontext[];
extern const char g_fmtcmdfailed[]; extern const char g_fmtcmdfailed[];
extern const char g_fmtcmdoutofmemory[]; extern const char g_fmtcmdoutofmemory[];
extern const char g_fmtinternalerror[];
/**************************************************************************** /****************************************************************************
* Public Function Prototypes * Public Function Prototypes

View File

@ -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_fmtcmdnotimpl[] = "nsh: %s: command not implemented\n";
const char g_fmtnosuch[] = "nsh: %s: no such %s: %s\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_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"; const char g_fmtcontext[] = "nsh: %s: not valid in this context\n";
#ifdef CONFIG_EXAMPLES_NSH_STRERROR #ifdef CONFIG_EXAMPLES_NSH_STRERROR
const char g_fmtcmdfailed[] = "nsh: %s: %s failed: %s\n"; 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"; const char g_fmtcmdfailed[] = "nsh: %s: %s failed: %d\n";
#endif #endif
const char g_fmtcmdoutofmemory[] = "nsh: %s: out of memory\n"; const char g_fmtcmdoutofmemory[] = "nsh: %s: out of memory\n";
const char g_fmtinternalerror[] = "nsh: %s: Internal error\n";
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
@ -433,13 +435,45 @@ char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr)
return pbegin; 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 * Name: nsh_ifthenelse
****************************************************************************/ ****************************************************************************/
static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, FAR char **saveptr) 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; FAR char *cmd = *ppcmd;
boolean disabled;
if (cmd) if (cmd)
{ {
/* Check if the command is preceeded by "if" */ /* 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 */ /* 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"); nsh_output(vtbl, g_fmtcontext, "if");
goto errout; 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) 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 */ /* 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"); nsh_output(vtbl, g_fmtcontext, "then");
goto errout; 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) 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 */ /* 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"); nsh_output(vtbl, g_fmtcontext, "else");
goto errout; 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) 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 */ /* 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"); nsh_output(vtbl, g_fmtcontext, "fi");
goto errout; 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); nsh_output(vtbl, g_fmtcontext, cmd);
goto errout; goto errout;
@ -533,48 +594,30 @@ static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
return OK; return OK;
errout: 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; 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 * Name: nsh_saveresult
****************************************************************************/ ****************************************************************************/
static inline int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, boolean result) static inline int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, boolean result)
{ {
vtbl->np.np_fail = result; struct nsh_parser_s *np = &vtbl->np;
if (vtbl->np.np_state == NSH_PARSER_IF)
if (np->np_st[np->np_ndx].ns_state == NSH_PARSER_IF)
{ {
vtbl->np.np_fail = FALSE; np->np_fail = FALSE;
vtbl->np.np_ifcond = result; np->np_st[np->np_ndx].ns_ifcond = result;
return OK; return OK;
} }
else else
{ {
vtbl->np.np_fail = result; np->np_fail = result;
return result ? ERROR : OK; return result ? ERROR : OK;
} }
} }