NSH: Add support for while-do-done and until-do-done loops

This commit is contained in:
Gregory Nutt 2014-01-17 15:56:32 -06:00
parent 97e1d8b535
commit cfb7c77ed3
6 changed files with 423 additions and 54 deletions

View File

@ -788,3 +788,7 @@
* apps/nshlib/nsh_script.c: Now saves the FILE stream for the script * apps/nshlib/nsh_script.c: Now saves the FILE stream for the script
file in the vtbl structure so that it can be accessed by forthcoming file in the vtbl structure so that it can be accessed by forthcoming
while and until logic (2014-1-17). while and until logic (2014-1-17).
* apps/nshlib/nsh.h, nsh_command.c, nsh_parse.c, and nsh_script.c: Add
support for while-do-done and until-do-done loops. These only work
when executing a script file because they depend on the ability to seek
in the file to implement the looping behaviors (2014-1-17).

View File

@ -104,6 +104,23 @@ Conditional Command Execution
[sequence of <cmd>] [sequence of <cmd>]
fi fi
Looping
^^^^^^^
while-do-done and until-do-done looping constructs are also supported.
These works from the command line but are primarily intended for use
within NSH scripts (see the sh command). The syntax is as follows:
while <test-cmd>; do <cmd-sequence>; done
Execute <cmd-sequence> as long as <test-cmd> has an exit status of
zero.
until <test-cmd>; do <cmd-sequence>; done
Execute <cmd-sequence> as long as <test-cmd> has a non-zero exit
status.
Built-In Variables Built-In Variables
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^

View File

@ -455,22 +455,42 @@
****************************************************************************/ ****************************************************************************/
/* State when parsing and if-then-else sequence */ /* State when parsing and if-then-else sequence */
enum nsh_parser_e enum nsh_itef_e
{ {
NSH_PARSER_NORMAL = 0, /* Not in any special sequence */ NSH_ITEF_NORMAL = 0, /* Not in an if-then-else sequence */
NSH_PARSER_IF, /* Just parsed 'if', expect condition */ NSH_ITEF_IF, /* Just parsed 'if', expect condition */
NSH_PARSER_THEN, /* Just parsed 'then', looking for 'else' or 'fi' */ NSH_ITEF_THEN, /* Just parsed 'then', looking for 'else' or 'fi' */
NSH_PARSER_ELSE /* Just parsed 'else', look for 'fi' */ NSH_ITEF_ELSE /* Just parsed 'else', look for 'fi' */
}; };
/* All state data for parsing one if-then-else sequence */ /* All state data for parsing one if-then-else sequence */
struct nsh_ifthenelse_s struct nsh_itef_s
{ {
uint8_t ie_ifcond : 1; /* Value of command in 'if' statement */ uint8_t ie_ifcond : 1; /* Value of command in 'if' statement */
uint8_t ie_disabled : 1; /* TRUE: Unconditionally disabled */ uint8_t ie_disabled : 1; /* TRUE: Unconditionally disabled */
uint8_t ie_unused : 4; uint8_t ie_unused : 4;
uint8_t ie_state : 2; /* Parser state (see enum nsh_parser_e) */ uint8_t ie_state : 2; /* If-then-else state (see enum nsh_itef_e) */
};
/* State when parsing and while-do-done or until-do-done sequence */
enum nsh_lp_e
{
NSH_LOOP_NORMAL = 0, /* Not in a while-do-done or until-do-done sequence */
NSH_LOOP_WHILE, /* Just parsed 'while', expect condition */
NSH_LOOP_UNTIL, /* Just parsed 'until', expect condition */
NSH_LOOP_DO /* Just parsed 'do', looking for 'done' */
};
/* All state data for parsing one while-do-done or until-do-done sequence */
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) */
long lp_topoffs; /* Top of loop file offset */
}; };
/* These structure provides the overall state of the parser */ /* These structure provides the overall state of the parser */
@ -478,23 +498,30 @@ struct nsh_ifthenelse_s
struct nsh_parser_s struct nsh_parser_s
{ {
#ifndef CONFIG_NSH_DISABLEBG #ifndef CONFIG_NSH_DISABLEBG
bool np_bg; /* true: The last command executed in background */ bool np_bg; /* true: The last command executed in background */
#endif #endif
#if CONFIG_NFILE_STREAMS > 0 #if CONFIG_NFILE_STREAMS > 0
bool np_redirect; /* true: Output from the last command was re-directed */ bool np_redirect; /* true: Output from the last command was re-directed */
#endif #endif
bool np_fail; /* true: The last command failed */ bool np_fail; /* true: The last command failed */
#ifndef CONFIG_NSH_DISABLEBG #ifndef CONFIG_NSH_DISABLEBG
int np_nice; /* "nice" value applied to last background cmd */ int np_nice; /* "nice" value applied to last background cmd */
#endif #endif
#ifndef CONFIG_NSH_DISABLESCRIPT #ifndef CONFIG_NSH_DISABLESCRIPT
FILE *np_stream; /* Stream of current script */ FILE *np_stream; /* Stream of current script */
uint8_t np_iendx; /* Current index into np_iestate[] */ 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[] */
/* This is a stack of if-then-else state information. */ /* This is a stack of parser state information. */
struct nsh_ifthenelse_s np_iestate[CONFIG_NSH_NESTDEPTH]; struct nsh_itef_s np_iestate[CONFIG_NSH_NESTDEPTH];
struct nsh_loop_s np_lpstate[CONFIG_NSH_NESTDEPTH];
#endif #endif
}; };

View File

@ -460,9 +460,9 @@ static inline void help_usage(FAR struct nsh_vtbl_s *vtbl)
{ {
nsh_output(vtbl, "NSH command forms:\n"); nsh_output(vtbl, "NSH command forms:\n");
#ifndef CONFIG_NSH_DISABLEBG #ifndef CONFIG_NSH_DISABLEBG
nsh_output(vtbl, " [nice [-d <niceness>>]] <cmd> [> <file>|>> <file>] [&]\n"); nsh_output(vtbl, " [nice [-d <niceness>>]] <cmd> [> <file>|>> <file>] [&]\n\n");
#else #else
nsh_output(vtbl, " <cmd> [> <file>|>> <file>]\n"); nsh_output(vtbl, " <cmd> [> <file>|>> <file>]\n\n");
#endif #endif
#ifndef CONFIG_NSH_DISABLESCRIPT #ifndef CONFIG_NSH_DISABLESCRIPT
nsh_output(vtbl, "OR\n"); nsh_output(vtbl, "OR\n");
@ -472,6 +472,16 @@ static inline void help_usage(FAR struct nsh_vtbl_s *vtbl)
nsh_output(vtbl, " else\n"); nsh_output(vtbl, " else\n");
nsh_output(vtbl, " [sequence of <cmd>]\n"); nsh_output(vtbl, " [sequence of <cmd>]\n");
nsh_output(vtbl, " fi\n\n"); nsh_output(vtbl, " fi\n\n");
nsh_output(vtbl, "OR\n");
nsh_output(vtbl, " while <cmd>\n");
nsh_output(vtbl, " do\n");
nsh_output(vtbl, " [sequence of <cmd>]\n");
nsh_output(vtbl, " done\n\n");
nsh_output(vtbl, "OR\n");
nsh_output(vtbl, " until <cmd>\n");
nsh_output(vtbl, " do\n");
nsh_output(vtbl, " [sequence of <cmd>]\n");
nsh_output(vtbl, " done\n\n");
#endif #endif
} }
#endif #endif

View File

@ -160,9 +160,13 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr,
FAR NSH_MEMLIST_TYPE *memlist); FAR NSH_MEMLIST_TYPE *memlist);
#ifndef CONFIG_NSH_DISABLESCRIPT #ifndef CONFIG_NSH_DISABLESCRIPT
static bool nsh_loop_enabled(FAR struct nsh_vtbl_s *vtbl);
static bool nsh_itef_enabled(FAR struct nsh_vtbl_s *vtbl);
static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl); static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl);
static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist); FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist);
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 #ifndef CONFIG_NSH_DISABLEBG
@ -392,7 +396,41 @@ static int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, bool result)
struct nsh_parser_s *np = &vtbl->np; struct nsh_parser_s *np = &vtbl->np;
#ifndef CONFIG_NSH_DISABLESCRIPT #ifndef CONFIG_NSH_DISABLESCRIPT
if (np->np_iestate[np->np_iendx].ie_state == NSH_PARSER_IF) /* Check if we are waiting for the condition associated with a while
* token.
*
* while <test-cmd>; do <cmd-sequence>; done
*
* Execute <cmd-sequence> as long as <test-cmd> has an exit status of
* zero.
*/
if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_WHILE)
{
np->np_fail = false;
np->np_lpstate[np->np_lpndx].lp_enable = (result == OK);
return OK;
}
/* Check if we are waiting for the condition associated with an until
* token.
*
* until <test-cmd>; do <cmd-sequence>; done
*
* Execute <cmd-sequence> as long as <test-cmd> has a non-zero exit
* status.
*/
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;
}
/* 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)
{ {
np->np_fail = false; np->np_fail = false;
np->np_iestate[np->np_iendx].ie_ifcond = result; np->np_iestate[np->np_iendx].ie_ifcond = result;
@ -1328,28 +1366,53 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
} }
/**************************************************************************** /****************************************************************************
* Name: nsh_cmdenabled * Name: nsh_loop_enabled
****************************************************************************/ ****************************************************************************/
#ifndef CONFIG_NSH_DISABLESCRIPT #ifndef CONFIG_NSH_DISABLESCRIPT
static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl) static bool nsh_loop_enabled(FAR struct nsh_vtbl_s *vtbl)
{ {
struct nsh_parser_s *np = &vtbl->np; FAR struct nsh_parser_s *np = &vtbl->np;
/* If we are looping and the disable bit is set, then we are skipping
* all data until we next get to the 'done' token at the end of the
* loop.
*/
if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_DO)
{
/* We have parsed 'do', looking for 'done' */
return (bool)np->np_lpstate[np->np_lpndx].lp_enable;
}
return true;
}
#endif
/****************************************************************************
* Name: nsh_itef_enabled
****************************************************************************/
#ifndef CONFIG_NSH_DISABLESCRIPT
static bool nsh_itef_enabled(FAR struct nsh_vtbl_s *vtbl)
{
FAR struct nsh_parser_s *np = &vtbl->np;
bool ret = !np->np_iestate[np->np_iendx].ie_disabled; bool ret = !np->np_iestate[np->np_iendx].ie_disabled;
if (ret) if (ret)
{ {
switch (np->np_iestate[np->np_iendx].ie_state) switch (np->np_iestate[np->np_iendx].ie_state)
{ {
case NSH_PARSER_NORMAL : case NSH_ITEF_NORMAL:
case NSH_PARSER_IF: case NSH_ITEF_IF:
default: default:
break; break;
case NSH_PARSER_THEN: case NSH_ITEF_THEN:
ret = !np->np_iestate[np->np_iendx].ie_ifcond; ret = !np->np_iestate[np->np_iendx].ie_ifcond;
break; break;
case NSH_PARSER_ELSE: case NSH_ITEF_ELSE:
ret = np->np_iestate[np->np_iendx].ie_ifcond; ret = np->np_iestate[np->np_iendx].ie_ifcond;
break; break;
} }
@ -1360,12 +1423,218 @@ static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
#endif #endif
/**************************************************************************** /****************************************************************************
* Name: nsh_ifthenelse * Name: nsh_cmdenabled
****************************************************************************/ ****************************************************************************/
#ifndef CONFIG_NSH_DISABLESCRIPT #ifndef CONFIG_NSH_DISABLESCRIPT
static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist) {
/* Return true if command processing is enabled on this pass through the
* loop AND if command processing is enabled in this part of the if-then-
* else-fi sequence.
*/
return (nsh_loop_enabled(vtbl) && nsh_itef_enabled(vtbl));
}
#endif
/****************************************************************************
* Name: nsh_loop
****************************************************************************/
#ifndef CONFIG_NSH_DISABLESCRIPT
static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist)
{
FAR struct nsh_parser_s *np = &vtbl->np;
FAR char *cmd = *ppcmd;
long offset;
bool whilematch;
bool untilmatch;
bool enable;
int ret;
if (cmd)
{
/* Check if the command is preceded by "while" or "until" */
whilematch = strcmp(cmd, "while");
untilmatch = strcmp(cmd, "until");
if (whilematch == 0 || untilmatch == 0)
{
uint8_t state;
/* Get the cmd following the "while" or "until" */
*ppcmd = nsh_argument(vtbl, saveptr, memlist);
if (!*ppcmd)
{
nsh_output(vtbl, g_fmtarginvalid, "if");
goto errout;
}
/* Verify that "while" or "until" is valid in this context */
if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF ||
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)
{
nsh_output(vtbl, g_fmtcontext, cmd);
goto errout;
}
/* Check if we have exceeded the maximum depth of nesting */
if (np->np_lpndx >= CONFIG_NSH_NESTDEPTH-1)
{
nsh_output(vtbl, g_fmtdeepnesting, cmd);
goto errout;
}
/* "Push" the old state and set the new state */
state = whilematch == 0 ? NSH_LOOP_WHILE : NSH_LOOP_UNTIL;
enable = nsh_cmdenabled(vtbl);
#ifdef NSH_DISABLE_SEMICOLON
offset = np->np_foffs;
#else
offset = np->np_foffs + np->np_loffs;
#endif
#ifndef NSH_DISABLE_SEMICOLON
np->np_jump = false;
#endif
np->np_lpndx++;
np->np_lpstate[np->np_lpndx].lp_state = state;
np->np_lpstate[np->np_lpndx].lp_enable = enable;
np->np_lpstate[np->np_lpndx].lp_topoffs = offset;
}
/* Check if the token is "do" */
else if (strcmp(cmd, "do") == 0)
{
/* Get the cmd following the "do" -- there shouldn't 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 */
if (np->np_lpstate[np->np_lpndx].lp_state != NSH_LOOP_WHILE &&
np->np_lpstate[np->np_lpndx].lp_state != NSH_LOOP_UNTIL)
{
nsh_output(vtbl, g_fmtcontext, "do");
goto errout;
}
np->np_lpstate[np->np_lpndx].lp_state = NSH_LOOP_DO;
}
/* Check if the token is "done" */
else if (strcmp(cmd, "done") == 0)
{
/* Get the cmd following the "done" -- there should be one */
*ppcmd = nsh_argument(vtbl, saveptr, memlist);
if (*ppcmd)
{
nsh_output(vtbl, g_fmtarginvalid, "done");
goto errout;
}
/* Verify that "done" is valid in this context */
if (np->np_lpstate[np->np_lpndx].lp_state != NSH_LOOP_DO)
{
nsh_output(vtbl, g_fmtcontext, "done");
goto errout;
}
if (np->np_lpndx < 1) /* Shouldn't happen */
{
nsh_output(vtbl, g_fmtinternalerror, "done");
goto errout;
}
/* Now what do we do? We either: Do go back to the top of the
* loop (if lp_enable == true) or continue past the end of the
* loop (if lp_enable == false)
*/
if (np->np_lpstate[np->np_lpndx].lp_enable)
{
/* Set the new file position to the top of the loop offset */
ret = fseek(np->np_stream,
np->np_lpstate[np->np_lpndx].lp_topoffs,
SEEK_SET);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, "done", "fseek", NSH_ERRNO);
}
#ifndef NSH_DISABLE_SEMICOLON
/* Signal nsh_parse that we need to stop processing the
* current line and jump back to the top of the loop.
*/
np->np_jump = true;
#endif
}
else
{
np->np_lpstate[np->np_lpndx].lp_enable = true;
}
/* "Pop" the previous state. We do this no matter what we
* decided to do
*/
np->np_lpstate[np->np_lpndx].lp_state = NSH_LOOP_NORMAL;
np->np_lpndx--;
}
/* If we just parsed "while" or "until", then nothing is acceptable
* other than "do"
*/
else if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_WHILE ||
np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_UNTIL)
{
nsh_output(vtbl, g_fmtcontext, cmd);
goto errout;
}
}
return OK;
errout:
#ifndef NSH_DISABLE_SEMICOLON
np->np_jump = false;
#endif
np->np_lpndx = 0;
np->np_lpstate[0].lp_state = NSH_LOOP_NORMAL;
np->np_lpstate[0].lp_enable = true;
np->np_lpstate[0].lp_topoffs = 0;
return ERROR;
}
#endif
/****************************************************************************
* Name: nsh_itef
****************************************************************************/
#ifndef CONFIG_NSH_DISABLESCRIPT
static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist)
{ {
FAR struct nsh_parser_s *np = &vtbl->np; FAR struct nsh_parser_s *np = &vtbl->np;
FAR char *cmd = *ppcmd; FAR char *cmd = *ppcmd;
@ -1388,9 +1657,7 @@ static 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 (np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_NORMAL && if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF)
np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_THEN &&
np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_ELSE)
{ {
nsh_output(vtbl, g_fmtcontext, "if"); nsh_output(vtbl, g_fmtcontext, "if");
goto errout; goto errout;
@ -1408,16 +1675,16 @@ static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
disabled = !nsh_cmdenabled(vtbl); disabled = !nsh_cmdenabled(vtbl);
np->np_iendx++; np->np_iendx++;
np->np_iestate[np->np_iendx].ie_state = NSH_PARSER_IF; np->np_iestate[np->np_iendx].ie_state = NSH_ITEF_IF;
np->np_iestate[np->np_iendx].ie_disabled = disabled; np->np_iestate[np->np_iendx].ie_disabled = disabled;
np->np_iestate[np->np_iendx].ie_ifcond = false; np->np_iestate[np->np_iendx].ie_ifcond = false;
} }
/* Check if the command is "then" */ /* Check if the token is "then" */
else if (strcmp(cmd, "then") == 0) else if (strcmp(cmd, "then") == 0)
{ {
/* Get the cmd following the then -- there shouldn't be one */ /* Get the cmd following the "then" -- there shouldn't be one */
*ppcmd = nsh_argument(vtbl, saveptr, memlist); *ppcmd = nsh_argument(vtbl, saveptr, memlist);
if (*ppcmd) if (*ppcmd)
@ -1428,20 +1695,20 @@ static 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 (np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_IF) if (np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_IF)
{ {
nsh_output(vtbl, g_fmtcontext, "then"); nsh_output(vtbl, g_fmtcontext, "then");
goto errout; goto errout;
} }
np->np_iestate[np->np_iendx].ie_state = NSH_PARSER_THEN; np->np_iestate[np->np_iendx].ie_state = NSH_ITEF_THEN;
} }
/* Check if the command is "else" */ /* Check if the token is "else" */
else if (strcmp(cmd, "else") == 0) else if (strcmp(cmd, "else") == 0)
{ {
/* Get the cmd following the else -- there shouldn't be one */ /* Get the cmd following the "else" -- there shouldn't be one */
*ppcmd = nsh_argument(vtbl, saveptr, memlist); *ppcmd = nsh_argument(vtbl, saveptr, memlist);
if (*ppcmd) if (*ppcmd)
@ -1452,16 +1719,16 @@ static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
/* Verify that "else" is valid in this context */ /* Verify that "else" is valid in this context */
if (np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_THEN) if (np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_THEN)
{ {
nsh_output(vtbl, g_fmtcontext, "else"); nsh_output(vtbl, g_fmtcontext, "else");
goto errout; goto errout;
} }
np->np_iestate[np->np_iendx].ie_state = NSH_PARSER_ELSE; np->np_iestate[np->np_iendx].ie_state = NSH_ITEF_ELSE;
} }
/* Check if the command is "fi" */ /* Check if the token is "fi" */
else if (strcmp(cmd, "fi") == 0) else if (strcmp(cmd, "fi") == 0)
{ {
@ -1476,8 +1743,8 @@ static 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 (np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_THEN && if (np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_THEN &&
np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_ELSE) np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_ELSE)
{ {
nsh_output(vtbl, g_fmtcontext, "fi"); nsh_output(vtbl, g_fmtcontext, "fi");
goto errout; goto errout;
@ -1494,9 +1761,9 @@ static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
np->np_iendx--; np->np_iendx--;
} }
/* If we just parsed 'if', then nothing is acceptable other than 'then' */ /* If we just parsed "if", then nothing is acceptable other than "then" */
else if (np->np_iestate[np->np_iendx].ie_state == NSH_PARSER_IF) else if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF)
{ {
nsh_output(vtbl, g_fmtcontext, cmd); nsh_output(vtbl, g_fmtcontext, cmd);
goto errout; goto errout;
@ -1507,7 +1774,7 @@ static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
errout: errout:
np->np_iendx = 0; np->np_iendx = 0;
np->np_iestate[0].ie_state = NSH_PARSER_NORMAL; np->np_iestate[0].ie_state = NSH_ITEF_NORMAL;
np->np_iestate[0].ie_disabled = false; np->np_iestate[0].ie_disabled = false;
np->np_iestate[0].ie_ifcond = false; np->np_iestate[0].ie_ifcond = false;
return ERROR; return ERROR;
@ -1724,10 +1991,18 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
saveptr = cmdline; saveptr = cmdline;
cmd = nsh_argument(vtbl, &saveptr, &memlist); cmd = nsh_argument(vtbl, &saveptr, &memlist);
/* Handler if-then-else-fi */
#ifndef CONFIG_NSH_DISABLESCRIPT #ifndef CONFIG_NSH_DISABLESCRIPT
if (nsh_ifthenelse(vtbl, &cmd, &saveptr, &memlist) != 0) /* Handle while-do-done and until-do-done loops */
if (nsh_loop(vtbl, &cmd, &saveptr, &memlist) != 0)
{
NSH_MEMLIST_FREE(&memlist);
return nsh_saveresult(vtbl, true);
}
/* Handle if-then-else-fi */
if (nsh_itef(vtbl, &cmd, &saveptr, &memlist) != 0)
{ {
NSH_MEMLIST_FREE(&memlist); NSH_MEMLIST_FREE(&memlist);
return nsh_saveresult(vtbl, true); return nsh_saveresult(vtbl, true);
@ -1872,16 +2147,31 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
return nsh_parse_command(vtbl, cmdline); return nsh_parse_command(vtbl, cmdline);
#else #else
#ifndef CONFIG_NSH_DISABLESCRIPT
FAR struct nsh_parser_s *np = &vtbl->np;
#endif
FAR char *start = cmdline; FAR char *start = cmdline;
FAR char *working = cmdline; FAR char *working = cmdline;
FAR char *ptr; FAR char *ptr;
size_t len; size_t len;
int ret; int ret;
/* Loop until all of the commands on the command line have been processed */ /* Loop until all of the commands on the command line have been processed OR
* until the end-of-loop has been recountered and we need to reload the line
* at the top of the loop.
*/
#ifndef CONFIG_NSH_DISABLESCRIPT
for (np->np_jump = false; !np->np_jump; )
#else
for (;;) for (;;)
#endif
{ {
#ifndef CONFIG_NSH_DISABLESCRIPT
/* Save the offset on the line to the start of the command */
np->np_loffs = (uint16_t)(working - cmdline);
#endif
/* A command may be terminated with a newline character, the end of the /* A command may be terminated with a newline character, the end of the
* line, or a semicolon. NOTE that the set of delimiting characters * line, or a semicolon. NOTE that the set of delimiting characters
* includes the quotation mark. We need to handle quotation marks here * includes the quotation mark. We need to handle quotation marks here
@ -1955,5 +2245,9 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
working = ++tmp; working = ++tmp;
} }
} }
#ifndef CONFIG_NSH_DISABLESCRIPT
return OK;
#endif
#endif #endif
} }

View File

@ -132,6 +132,23 @@ int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd,
/* Get the next line of input from the file */ /* Get the next line of input from the file */
fflush(stdout); fflush(stdout);
/* 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
* script file. Note that ftell will return -1 on failure.
*/
vtbl->np.np_foffs = ftell(vtbl->np.np_stream);
vtbl->np.np_loffs = 0;
if (vtbl->np.np_foffs < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, "loop", "ftell", NSH_ERRNO);
}
/* Now read the next line from the script file */
pret = fgets(buffer, CONFIG_NSH_LINELEN, vtbl->np.np_stream); pret = fgets(buffer, CONFIG_NSH_LINELEN, vtbl->np.np_stream);
if (pret) if (pret)
{ {