NSH: Add support for while-do-done and until-do-done loops
This commit is contained in:
parent
97e1d8b535
commit
cfb7c77ed3
@ -788,3 +788,7 @@
|
||||
* 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
|
||||
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).
|
||||
|
@ -104,6 +104,23 @@ Conditional Command Execution
|
||||
[sequence of <cmd>]
|
||||
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
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
61
nshlib/nsh.h
61
nshlib/nsh.h
@ -455,22 +455,42 @@
|
||||
****************************************************************************/
|
||||
/* 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_PARSER_IF, /* Just parsed 'if', expect condition */
|
||||
NSH_PARSER_THEN, /* Just parsed 'then', looking for 'else' or 'fi' */
|
||||
NSH_PARSER_ELSE /* Just parsed 'else', look for 'fi' */
|
||||
NSH_ITEF_NORMAL = 0, /* Not in an if-then-else sequence */
|
||||
NSH_ITEF_IF, /* Just parsed 'if', expect condition */
|
||||
NSH_ITEF_THEN, /* Just parsed 'then', looking for 'else' or 'fi' */
|
||||
NSH_ITEF_ELSE /* Just parsed 'else', look for 'fi' */
|
||||
};
|
||||
|
||||
/* 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_disabled : 1; /* TRUE: Unconditionally disabled */
|
||||
uint8_t ie_ifcond : 1; /* Value of command in 'if' statement */
|
||||
uint8_t ie_disabled : 1; /* TRUE: Unconditionally disabled */
|
||||
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 */
|
||||
@ -478,23 +498,30 @@ struct nsh_ifthenelse_s
|
||||
struct nsh_parser_s
|
||||
{
|
||||
#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
|
||||
#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
|
||||
bool np_fail; /* true: The last command failed */
|
||||
bool np_fail; /* true: The last command failed */
|
||||
#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
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLESCRIPT
|
||||
FILE *np_stream; /* Stream of current script */
|
||||
uint8_t np_iendx; /* Current index into np_iestate[] */
|
||||
FILE *np_stream; /* Stream of current script */
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -460,9 +460,9 @@ static inline void help_usage(FAR struct nsh_vtbl_s *vtbl)
|
||||
{
|
||||
nsh_output(vtbl, "NSH command forms:\n");
|
||||
#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
|
||||
nsh_output(vtbl, " <cmd> [> <file>|>> <file>]\n");
|
||||
nsh_output(vtbl, " <cmd> [> <file>|>> <file>]\n\n");
|
||||
#endif
|
||||
#ifndef CONFIG_NSH_DISABLESCRIPT
|
||||
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, " [sequence of <cmd>]\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
|
||||
|
@ -160,9 +160,13 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr,
|
||||
FAR NSH_MEMLIST_TYPE *memlist);
|
||||
|
||||
#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 int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist);
|
||||
static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
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
|
||||
|
||||
#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;
|
||||
|
||||
#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_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
|
||||
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;
|
||||
if (ret)
|
||||
{
|
||||
switch (np->np_iestate[np->np_iendx].ie_state)
|
||||
{
|
||||
case NSH_PARSER_NORMAL :
|
||||
case NSH_PARSER_IF:
|
||||
case NSH_ITEF_NORMAL:
|
||||
case NSH_ITEF_IF:
|
||||
default:
|
||||
break;
|
||||
|
||||
case NSH_PARSER_THEN:
|
||||
case NSH_ITEF_THEN:
|
||||
ret = !np->np_iestate[np->np_iendx].ie_ifcond;
|
||||
break;
|
||||
|
||||
case NSH_PARSER_ELSE:
|
||||
case NSH_ITEF_ELSE:
|
||||
ret = np->np_iestate[np->np_iendx].ie_ifcond;
|
||||
break;
|
||||
}
|
||||
@ -1360,12 +1423,218 @@ static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nsh_ifthenelse
|
||||
* Name: nsh_cmdenabled
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLESCRIPT
|
||||
static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist)
|
||||
static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
|
||||
{
|
||||
/* 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 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 */
|
||||
|
||||
if (np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_NORMAL &&
|
||||
np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_THEN &&
|
||||
np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_ELSE)
|
||||
if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF)
|
||||
{
|
||||
nsh_output(vtbl, g_fmtcontext, "if");
|
||||
goto errout;
|
||||
@ -1408,16 +1675,16 @@ static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
|
||||
disabled = !nsh_cmdenabled(vtbl);
|
||||
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_ifcond = false;
|
||||
}
|
||||
|
||||
/* Check if the command is "then" */
|
||||
/* Check if the token is "then" */
|
||||
|
||||
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);
|
||||
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 */
|
||||
|
||||
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");
|
||||
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)
|
||||
{
|
||||
/* 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);
|
||||
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 */
|
||||
|
||||
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");
|
||||
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)
|
||||
{
|
||||
@ -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 */
|
||||
|
||||
if (np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_THEN &&
|
||||
np->np_iestate[np->np_iendx].ie_state != NSH_PARSER_ELSE)
|
||||
if (np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_THEN &&
|
||||
np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_ELSE)
|
||||
{
|
||||
nsh_output(vtbl, g_fmtcontext, "fi");
|
||||
goto errout;
|
||||
@ -1494,9 +1761,9 @@ static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
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);
|
||||
goto errout;
|
||||
@ -1507,7 +1774,7 @@ static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
|
||||
errout:
|
||||
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_ifcond = false;
|
||||
return ERROR;
|
||||
@ -1724,10 +1991,18 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
|
||||
saveptr = cmdline;
|
||||
cmd = nsh_argument(vtbl, &saveptr, &memlist);
|
||||
|
||||
/* Handler if-then-else-fi */
|
||||
|
||||
#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);
|
||||
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);
|
||||
|
||||
#else
|
||||
#ifndef CONFIG_NSH_DISABLESCRIPT
|
||||
FAR struct nsh_parser_s *np = &vtbl->np;
|
||||
#endif
|
||||
FAR char *start = cmdline;
|
||||
FAR char *working = cmdline;
|
||||
FAR char *ptr;
|
||||
size_t len;
|
||||
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 (;;)
|
||||
#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
|
||||
* line, or a semicolon. NOTE that the set of delimiting characters
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLESCRIPT
|
||||
return OK;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
@ -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 */
|
||||
|
||||
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);
|
||||
if (pret)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user