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:
parent
cab9dbe225
commit
07c715e09c
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user