Avoid leaving unnecessary waiting shells in many forms of sh -c COMMAND.

This change only affects strings passed to -c, when the -s
option is not used.

The approach is to check if there may be additional data
in the string after parsing each command. If there is none,
use the EV_EXIT flag so that a fork may be omitted in
specific cases.

If there are empty lines after the command, the check will
not see the end and forks will not be omitted. The same
thing seems to happen in bash.

Example:
  sh -c 'ps lT'
No longer shows a shell process waiting for ps to finish.

PR:		bin/113860
Reviewed by:	stefanf
Approved by:	ed (mentor)
This commit is contained in:
Jilles Tjoelker 2009-06-13 21:17:45 +00:00
parent 6e28dacfda
commit 960da93430
5 changed files with 35 additions and 9 deletions

View File

@ -74,11 +74,6 @@ __FBSDID("$FreeBSD$");
#endif #endif
/* flags in argument to evaltree */
#define EV_EXIT 01 /* exit after evaluating tree */
#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
#define EV_BACKCMD 04 /* command executing within back quotes */
MKINIT int evalskip; /* set if we are skipping commands */ MKINIT int evalskip; /* set if we are skipping commands */
STATIC int skipcount; /* number of levels to skip */ STATIC int skipcount; /* number of levels to skip */
MKINIT int loopnest; /* current loop nesting level */ MKINIT int loopnest; /* current loop nesting level */
@ -163,20 +158,28 @@ evalstring(char *s, int flags)
{ {
union node *n; union node *n;
struct stackmark smark; struct stackmark smark;
int flags_exit;
flags_exit = flags & EV_EXIT;
flags &= ~EV_EXIT;
setstackmark(&smark); setstackmark(&smark);
setinputstring(s, 1); setinputstring(s, 1);
while ((n = parsecmd(0)) != NEOF) { while ((n = parsecmd(0)) != NEOF) {
if (n != NULL) if (n != NULL) {
evaltree(n, flags); if (flags_exit && preadateof())
evaltree(n, flags | EV_EXIT);
else
evaltree(n, flags);
}
popstackmark(&smark); popstackmark(&smark);
} }
popfile(); popfile();
popstackmark(&smark); popstackmark(&smark);
if (flags_exit)
exitshell(exitstatus);
} }
/* /*
* Evaluate a parse tree. The value is left in the global variable * Evaluate a parse tree. The value is left in the global variable
* exitstatus. * exitstatus.

View File

@ -45,6 +45,11 @@ struct backcmd { /* result of evalbackcmd */
struct job *jp; /* job structure for command */ struct job *jp; /* job structure for command */
}; };
/* flags in argument to evaltree/evalstring */
#define EV_EXIT 01 /* exit after evaluating tree */
#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
#define EV_BACKCMD 04 /* command executing within back quotes */
int evalcmd(int, char **); int evalcmd(int, char **);
void evalstring(char *, int); void evalstring(char *, int);
union node; /* BLETCH for ansi C */ union node; /* BLETCH for ansi C */

View File

@ -320,6 +320,23 @@ preadbuffer(void)
return *parsenextc++; return *parsenextc++;
} }
/*
* Returns if we are certain we are at EOF. Does not cause any more input
* to be read from the outside world.
*/
int
preadateof(void)
{
if (parsenleft > 0)
return 0;
if (parsefile->strpush)
return 0;
if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
return 1;
return 0;
}
/* /*
* Undo the last call to pgetc. Only one character may be pushed back. * Undo the last call to pgetc. Only one character may be pushed back.
* PEOF may be pushed back. * PEOF may be pushed back.

View File

@ -48,6 +48,7 @@ extern int init_editline; /* 0 == not setup, 1 == OK, -1 == failed */
char *pfgets(char *, int); char *pfgets(char *, int);
int pgetc(void); int pgetc(void);
int preadbuffer(void); int preadbuffer(void);
int preadateof(void);
void pungetc(void); void pungetc(void);
void pushstring(char *, int, void *); void pushstring(char *, int, void *);
void popstring(void); void popstring(void);

View File

@ -178,7 +178,7 @@ main(int argc, char *argv[])
state3: state3:
state = 4; state = 4;
if (minusc) { if (minusc) {
evalstring(minusc, 0); evalstring(minusc, sflag ? 0 : EV_EXIT);
} }
if (sflag || minusc == NULL) { if (sflag || minusc == NULL) {
state4: /* XXX ??? - why isn't this before the "if" statement */ state4: /* XXX ??? - why isn't this before the "if" statement */