#endif
#include "busybox.h" /* for applet_names */
+//TODO: pull in some .h and find out do we have SINGLE_APPLET_MAIN?
+//#include "applet_tables.h" doesn't work
#include <paths.h>
#include <setjmp.h>
#include <fnmatch.h>
-#if JOBS || ENABLE_ASH_READ_NCHARS
-#include <termios.h>
+
+#if defined SINGLE_APPLET_MAIN
+/* STANDALONE does not make sense, and won't compile */
+#undef CONFIG_FEATURE_SH_STANDALONE
+#undef ENABLE_FEATURE_SH_STANDALONE
+#undef USE_FEATURE_SH_STANDALONE
+#undef SKIP_FEATURE_SH_STANDALONE(...)
+#define ENABLE_FEATURE_SH_STANDALONE 0
+#define USE_FEATURE_SH_STANDALONE(...)
+#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__
#endif
#ifndef PIPE_BUF
#endif
#if defined(__uClinux__)
-#error "Do not even bother, ash will not run on uClinux"
+#error "Do not even bother, ash will not run on NOMMU machine"
#endif
#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
-/* C99 say: "char" declaration may be signed or unsigned default */
+/* C99 says: "char" declaration may be signed or unsigned by default */
#define signed_char2int(sc) ((int)((signed char)sc))
/*
* Sigmode records the current value of the signal handlers for the various
* modes. A value of zero means that the current handler is not known.
- * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
+ * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
*/
char sigmode[NSIG - 1];
-#define S_DFL 1 /* default signal handling (SIG_DFL) */
-#define S_CATCH 2 /* signal is caught */
-#define S_IGN 3 /* signal is ignored (SIG_IGN) */
+#define S_DFL 1 /* default signal handling (SIG_DFL) */
+#define S_CATCH 2 /* signal is caught */
+#define S_IGN 3 /* signal is ignored (SIG_IGN) */
#define S_HARD_IGN 4 /* signal is ignored permenantly */
-#define S_RESET 5 /* temporary - to reset a hard ignored sig */
/* indicates specified signal received */
char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
} while (0)
/*
- * Ignore a signal. Only one usage site - in forkchild()
+ * Ignore a signal. Avoids unnecessary system calls.
*/
static void
ignoresig(int signo)
#define NHERE 24
#define NXHERE 25
#define NNOT 26
+#define N_NUMBER 27
union node;
struct strpush {
struct strpush *prev; /* preceding string on stack */
- char *prevstring;
- int prevnleft;
+ char *prev_string;
+ int prev_left_in_line;
#if ENABLE_ASH_ALIAS
struct alias *ap; /* if push was associated with an alias */
#endif
struct parsefile *prev; /* preceding file on stack */
int linno; /* current line */
int fd; /* file descriptor (or -1 if string) */
- int nleft; /* number of chars left in this line */
- int lleft; /* number of chars left in this buffer */
- char *nextc; /* next char in buffer */
+ int left_in_line; /* number of chars left in this line */
+ int left_in_buffer; /* number of chars left in this buffer past the line */
+ char *next_to_pgetc; /* next char in buffer */
char *buf; /* input buffer */
struct strpush *strpush; /* for pushing strings at this level */
struct strpush basestrpush; /* so pushing one is fast */
static void
setsignal(int signo)
{
- int action;
- char *t, tsig;
+ char *t;
+ char cur_act, new_act;
struct sigaction act;
t = trap[signo];
- action = S_IGN;
- if (t == NULL)
- action = S_DFL;
- else if (*t != '\0')
- action = S_CATCH;
- if (rootshell && action == S_DFL) {
+ new_act = S_DFL;
+ if (t != NULL) { /* trap for this sig is set */
+ new_act = S_CATCH;
+ if (t[0] == '\0') /* trap is "": ignore this sig */
+ new_act = S_IGN;
+ }
+
+ if (rootshell && new_act == S_DFL) {
switch (signo) {
case SIGINT:
if (iflag || minusc || sflag == 0)
- action = S_CATCH;
+ new_act = S_CATCH;
break;
case SIGQUIT:
#if DEBUG
if (debug)
break;
#endif
- /* FALLTHROUGH */
+ /* man bash:
+ * "In all cases, bash ignores SIGQUIT. Non-builtin
+ * commands run by bash have signal handlers
+ * set to the values inherited by the shell
+ * from its parent". */
+ new_act = S_IGN;
+ break;
case SIGTERM:
if (iflag)
- action = S_IGN;
+ new_act = S_IGN;
break;
#if JOBS
case SIGTSTP:
case SIGTTOU:
if (mflag)
- action = S_IGN;
+ new_act = S_IGN;
break;
#endif
}
}
+//TODO: if !rootshell, we reset SIGQUIT to DFL,
+//whereas we have to restore it to what shell got on entry
+//from the parent. See comment above
t = &sigmode[signo - 1];
- tsig = *t;
- if (tsig == 0) {
- /*
- * current setting unknown
- */
- if (sigaction(signo, NULL, &act) == -1) {
- /*
- * Pretend it worked; maybe we should give a warning
- * here, but other shells don't. We don't alter
- * sigmode, so that we retry every time.
- */
+ cur_act = *t;
+ if (cur_act == 0) {
+ /* current setting is not yet known */
+ if (sigaction(signo, NULL, &act)) {
+ /* pretend it worked; maybe we should give a warning,
+ * but other shells don't. We don't alter sigmode,
+ * so we retry every time.
+ * btw, in Linux it never fails. --vda */
return;
}
- tsig = S_RESET; /* force to be set */
if (act.sa_handler == SIG_IGN) {
- tsig = S_HARD_IGN;
+ cur_act = S_HARD_IGN;
if (mflag
&& (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
) {
- tsig = S_IGN; /* don't hard ignore these */
+ cur_act = S_IGN; /* don't hard ignore these */
}
}
}
- if (tsig == S_HARD_IGN || tsig == action)
+ if (cur_act == S_HARD_IGN || cur_act == new_act)
return;
+
act.sa_handler = SIG_DFL;
- switch (action) {
+ switch (new_act) {
case S_CATCH:
act.sa_handler = onsig;
+ act.sa_flags = 0; /* matters only if !DFL and !IGN */
+ sigfillset(&act.sa_mask); /* ditto */
break;
case S_IGN:
act.sa_handler = SIG_IGN;
break;
}
- *t = action;
- act.sa_flags = 0;
- sigfillset(&act.sa_mask);
sigaction_set(signo, &act);
+
+ *t = new_act;
}
/* mode flags for set_curjob */
return col;
}
-/*
- * Do a wait system call. If job control is compiled in, we accept
- * stopped processes. If block is zero, we return a value of zero
- * rather than blocking.
- *
- * System V doesn't have a non-blocking wait system call. It does
- * have a SIGCLD signal that is sent to a process when one of it's
- * children dies. The obvious way to use SIGCLD would be to install
- * a handler for SIGCLD which simply bumped a counter when a SIGCLD
- * was received, and have waitproc bump another counter when it got
- * the status of a process. Waitproc would then know that a wait
- * system call would not block if the two counters were different.
- * This approach doesn't work because if a process has children that
- * have not been waited for, System V will send it a SIGCLD when it
- * installs a signal handler for SIGCLD. What this means is that when
- * a child exits, the shell will be sent SIGCLD signals continuously
- * until is runs out of stack space, unless it does a wait call before
- * restoring the signal handler. The code below takes advantage of
- * this (mis)feature by installing a signal handler for SIGCLD and
- * then checking to see whether it was called. If there are any
- * children to be waited for, it will be.
- *
- * If neither SYSV nor BSD is defined, we don't implement nonblocking
- * waits at all. In this case, the user will not be informed when
- * a background process until the next time she runs a real program
- * (as opposed to running a builtin command or just typing return),
- * and the jobs command may give out of date information.
- */
-static int
-waitproc(int wait_flags, int *status)
-{
-#if JOBS
- if (doing_jobctl)
- wait_flags |= WUNTRACED;
-#endif
- /* NB: _not_ safe_waitpid, we need to detect EINTR */
- return waitpid(-1, status, wait_flags);
-}
-
-/*
- * Wait for a process to terminate.
- */
static int
dowait(int wait_flags, struct job *job)
{
struct job *thisjob;
int state;
- TRACE(("dowait(%d) called\n", wait_flags));
- pid = waitproc(wait_flags, &status);
- TRACE(("wait returns pid=%d, status=%d\n", pid, status));
- if (pid <= 0) {
- /* If we were doing blocking wait and (probably) got EINTR,
- * check for pending sigs received while waiting.
- * (NB: can be moved into callers if needed) */
- if (wait_flags == DOWAIT_BLOCK && pendingsig)
- raise_exception(EXSIG);
+ TRACE(("dowait(0x%x) called\n", wait_flags));
+
+ /* Do a wait system call. If job control is compiled in, we accept
+ * stopped processes. wait_flags may have WNOHANG, preventing blocking.
+ * NB: _not_ safe_waitpid, we need to detect EINTR */
+ pid = waitpid(-1, &status,
+ (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
+ TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
+ if (pid <= 0)
return pid;
- }
+
INT_OFF;
thisjob = NULL;
for (jp = curjob; jp; jp = jp->prev_job) {
return pid;
}
+static int
+blocking_wait_with_raise_on_sig(struct job *job)
+{
+ pid_t pid = dowait(DOWAIT_BLOCK, job);
+ if (pid <= 0 && pendingsig)
+ raise_exception(EXSIG);
+ return pid;
+}
+
#if JOBS
static void
showjob(FILE *out, struct job *jp, int mode)
TRACE(("showjobs(%x) called\n", mode));
- /* If not even one job changed, there is nothing to do */
+ /* Handle all finished jobs */
while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
continue;
jp->waited = 1;
jp = jp->prev_job;
}
- dowait(DOWAIT_BLOCK, NULL);
+ /* man bash:
+ * "When bash is waiting for an asynchronous command via
+ * the wait builtin, the reception of a signal for which a trap
+ * has been set will cause the wait builtin to return immediately
+ * with an exit status greater than 128, immediately after which
+ * the trap is executed."
+ * Do we do it that way? */
+ blocking_wait_with_raise_on_sig(NULL);
}
}
job = getjob(*argv, 0);
/* loop until process terminated or stopped */
while (job->state == JOBRUNNING)
- dowait(DOWAIT_BLOCK, NULL);
+ blocking_wait_with_raise_on_sig(NULL);
job->waited = 1;
retval = getstatus(job);
- repeat:
- ;
+ repeat: ;
} while (*++argv);
ret:
oldlvl = shlvl;
shlvl++;
+ /* man bash: "Non-builtin commands run by bash have signal handlers
+ * set to the values inherited by the shell from its parent".
+ * Do we do it correctly? */
+
closescript();
clear_traps();
#if JOBS
pgrp = getpid();
else
pgrp = jp->ps[0].pid;
- /* This can fail because we are doing it in the parent also */
- (void)setpgid(0, pgrp);
+ /* this can fail because we are doing it in the parent also */
+ setpgid(0, pgrp);
if (mode == FORK_FG)
xtcsetpgrp(ttyfd, pgrp);
setsignal(SIGTSTP);
} else
#endif
if (mode == FORK_BG) {
+ /* man bash: "When job control is not in effect,
+ * asynchronous commands ignore SIGINT and SIGQUIT" */
ignoresig(SIGINT);
ignoresig(SIGQUIT);
if (jp->nprocs == 0) {
ash_msg_and_raise_error("can't open %s", bb_dev_null);
}
}
- if (!oldlvl && iflag) {
- setsignal(SIGINT);
+ if (!oldlvl) {
+ if (iflag) { /* why if iflag only? */
+ setsignal(SIGINT);
+ setsignal(SIGTERM);
+ }
+ /* man bash:
+ * "In all cases, bash ignores SIGQUIT. Non-builtin
+ * commands run by bash have signal handlers
+ * set to the values inherited by the shell
+ * from its parent".
+ * Take care of the second rule: */
setsignal(SIGQUIT);
- setsignal(SIGTERM);
}
for (jp = curjob; jp; jp = jp->prev_job)
freejob(jp);
/*
* Wait for job to finish.
*
- * Under job control we have the problem that while a child process is
- * running interrupts generated by the user are sent to the child but not
- * to the shell. This means that an infinite loop started by an inter-
- * active user may be hard to kill. With job control turned off, an
- * interactive user may place an interactive program inside a loop. If
- * the interactive program catches interrupts, the user doesn't want
+ * Under job control we have the problem that while a child process
+ * is running interrupts generated by the user are sent to the child
+ * but not to the shell. This means that an infinite loop started by
+ * an interactive user may be hard to kill. With job control turned off,
+ * an interactive user may place an interactive program inside a loop.
+ * If the interactive program catches interrupts, the user doesn't want
* these interrupts to also abort the loop. The approach we take here
* is to have the shell ignore interrupt signals while waiting for a
* foreground process to terminate, and then send itself an interrupt
int st;
TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+
+ INT_OFF;
while (jp->state == JOBRUNNING) {
+ /* In non-interactive shells, we _can_ get
+ * a keyboard signal here and be EINTRed,
+ * but we just loop back, waiting for command to complete.
+ *
+ * man bash:
+ * "If bash is waiting for a command to complete and receives
+ * a signal for which a trap has been set, the trap
+ * will not be executed until the command completes."
+ *
+ * Reality is that even if trap is not set, bash
+ * will not act on the signal until command completes.
+ * Try this. sleep5intoff.c:
+ * #include <signal.h>
+ * #include <unistd.h>
+ * int main() {
+ * sigset_t set;
+ * sigemptyset(&set);
+ * sigaddset(&set, SIGINT);
+ * sigaddset(&set, SIGQUIT);
+ * sigprocmask(SIG_BLOCK, &set, NULL);
+ * sleep(5);
+ * return 0;
+ * }
+ * $ bash -c './sleep5intoff; echo hi'
+ * ^C^C^C^C <--- pressing ^C once a second
+ * $ _
+ * $ bash -c './sleep5intoff; echo hi'
+ * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
+ * $ _
+ */
dowait(DOWAIT_BLOCK, jp);
}
+ INT_ON;
+
st = getstatus(jp);
#if JOBS
if (jp->jobctl) {
if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
/* child */
close(pip[0]);
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-#ifdef SIGTSTP
- signal(SIGTSTP, SIG_IGN);
-#endif
+ ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
+ ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
+ ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
+ ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
signal(SIGPIPE, SIG_DFL);
if (redir->type == NHERE)
full_write(pip[1], redir->nhere.doc->narg.text, len);
result->buf = NULL;
result->nleft = 0;
result->jp = NULL;
- if (n == NULL) {
+ if (n == NULL)
goto out;
- }
saveherefd = herefd;
herefd = -1;
int flag;
int len;
- /* ifsfree(); */
+ /* ifsfree(); */
/*
* This routine is slightly over-complicated for
if (*end == CTLENDVAR) {
if (varflags & VSNUL)
tail = " or null";
- } else
+ } else {
msg = umsg;
+ }
}
ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
}
idx++;
rmesc++;
}
- } else
+ } else {
idx = loc;
+ }
for (loc = repl; *loc; loc++) {
restart_detect = stackblock();
/* ============ eval.c */
-static int funcblocksize; /* size of structures in function */
-static int funcstringsize; /* size of strings in node */
-static void *funcblock; /* block to allocate function from */
-static char *funcstring; /* block to allocate strings from */
+static int funcblocksize; /* size of structures in function */
+static int funcstringsize; /* size of strings in node */
+static void *funcblock; /* block to allocate function from */
+static char *funcstring; /* block to allocate strings from */
/* 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_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 */
-static const short nodesize[26] = {
- SHELL_ALIGN(sizeof(struct ncmd)),
- SHELL_ALIGN(sizeof(struct npipe)),
- SHELL_ALIGN(sizeof(struct nredir)),
- SHELL_ALIGN(sizeof(struct nredir)),
- SHELL_ALIGN(sizeof(struct nredir)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nif)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nfor)),
- SHELL_ALIGN(sizeof(struct ncase)),
- SHELL_ALIGN(sizeof(struct nclist)),
- SHELL_ALIGN(sizeof(struct narg)),
- SHELL_ALIGN(sizeof(struct narg)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct ndup)),
- SHELL_ALIGN(sizeof(struct ndup)),
- SHELL_ALIGN(sizeof(struct nhere)),
- SHELL_ALIGN(sizeof(struct nhere)),
- SHELL_ALIGN(sizeof(struct nnot)),
+static const short nodesize[N_NUMBER] = {
+ [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
+ [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
+ [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
+ [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
+ [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
+ [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
+ [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
+ [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
+ [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
+ [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
+ [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
+ [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
+#if ENABLE_ASH_BASH_COMPAT
+ [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
+#endif
+ [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
+ [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
+ [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
+ [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
+ [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
+ [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
+ [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
+ [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
+ [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
};
static void calcsize(union node *n);
/* Execute the command. */
switch (cmdentry.cmdtype) {
default:
+
#if ENABLE_FEATURE_SH_NOFORK
+/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
+ * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
{
/* find_command() encodes applet_no as (-2 - applet_no) */
int applet_no = (- cmdentry.u.index - 2);
}
}
#endif
-
/* Fork off a child process if necessary. */
if (!(flags & EV_EXIT) || trap[0]) {
INT_OFF;
}
listsetvar(list, i);
}
+ /* Tight loop with builtins only:
+ * "while kill -0 $child; do true; done"
+ * will never exit even if $child died, unless we do this
+ * to reap the zombie and make kill detect that it's gone: */
+ dowait(DOWAIT_NONBLOCK, NULL);
+
if (evalbltin(cmdentry.u.cmd, argc, argv)) {
int exit_status;
int i = exception;
case CMDFUNCTION:
listsetvar(varlist.list, 0);
+ /* See above for the rationale */
+ dowait(DOWAIT_NONBLOCK, NULL);
if (evalfun(cmdentry.u.func, argc, argv, flags))
goto raise;
break;
* This implements the input routines used by the parser.
*/
-#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
-
enum {
INPUT_PUSH_FILE = 1,
INPUT_NOFILE_OK = 2,
};
-static int plinno = 1; /* input line number */
-/* number of characters left in input buffer */
-static int parsenleft; /* copy of parsefile->nleft */
-static int parselleft; /* copy of parsefile->lleft */
-/* next character in input buffer */
-static char *parsenextc; /* copy of parsefile->nextc */
-
static smallint checkkwd;
/* values of checkkwd variable */
#define CHKALIAS 0x1
#define CHKKWD 0x2
#define CHKNL 0x4
+/*
+ * Push a string back onto the input at this current parsefile level.
+ * We handle aliases this way.
+ */
+#if !ENABLE_ASH_ALIAS
+#define pushstring(s, ap) pushstring(s)
+#endif
+static void
+pushstring(char *s, struct alias *ap)
+{
+ struct strpush *sp;
+ int len;
+
+ len = strlen(s);
+ INT_OFF;
+ if (g_parsefile->strpush) {
+ sp = ckzalloc(sizeof(*sp));
+ sp->prev = g_parsefile->strpush;
+ } else {
+ sp = &(g_parsefile->basestrpush);
+ }
+ g_parsefile->strpush = sp;
+ sp->prev_string = g_parsefile->next_to_pgetc;
+ sp->prev_left_in_line = g_parsefile->left_in_line;
+#if ENABLE_ASH_ALIAS
+ sp->ap = ap;
+ if (ap) {
+ ap->flag |= ALIASINUSE;
+ sp->string = s;
+ }
+#endif
+ g_parsefile->next_to_pgetc = s;
+ g_parsefile->left_in_line = len;
+ INT_ON;
+}
+
static void
popstring(void)
{
INT_OFF;
#if ENABLE_ASH_ALIAS
if (sp->ap) {
- if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
+ if (g_parsefile->next_to_pgetc[-1] == ' '
+ || g_parsefile->next_to_pgetc[-1] == '\t'
+ ) {
checkkwd |= CHKALIAS;
}
if (sp->string != sp->ap->val) {
}
}
#endif
- parsenextc = sp->prevstring;
- parsenleft = sp->prevnleft;
-/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
+ g_parsefile->next_to_pgetc = sp->prev_string;
+ g_parsefile->left_in_line = sp->prev_left_in_line;
g_parsefile->strpush = sp->prev;
if (sp != &(g_parsefile->basestrpush))
free(sp);
INT_ON;
}
+//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
+//it peeks whether it is &>, and then pushes back both chars.
+//This function needs to save last *next_to_pgetc to buf[0]
+//to make two pungetc() reliable. Currently,
+// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
static int
preadfd(void)
{
int nr;
char *buf = g_parsefile->buf;
- parsenextc = buf;
+ g_parsefile->next_to_pgetc = buf;
#if ENABLE_FEATURE_EDITING
retry:
- if (!iflag || g_parsefile->fd)
+ if (!iflag || g_parsefile->fd != STDIN_FILENO)
nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1);
else {
#if ENABLE_FEATURE_TAB_COMPLETION
* Refill the input buffer and return the next input character:
*
* 1) If a string was pushed back on the input, pop it;
- * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
- * from a string so we can't refill the buffer, return EOF.
+ * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
+ * or we are reading from a string so we can't refill the buffer,
+ * return EOF.
* 3) If the is more stuff in this buffer, use it else call read to fill it.
* 4) Process input up to the next newline, deleting nul characters.
*/
+//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
+#define pgetc_debug(...) ((void)0)
static int
preadbuffer(void)
{
char *q;
int more;
- char savec;
while (g_parsefile->strpush) {
#if ENABLE_ASH_ALIAS
- if (parsenleft == -1 && g_parsefile->strpush->ap &&
- parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
+ if (g_parsefile->left_in_line == -1
+ && g_parsefile->strpush->ap
+ && g_parsefile->next_to_pgetc[-1] != ' '
+ && g_parsefile->next_to_pgetc[-1] != '\t'
+ ) {
+ pgetc_debug("preadbuffer PEOA");
return PEOA;
}
#endif
popstring();
- if (--parsenleft >= 0)
- return signed_char2int(*parsenextc++);
- }
- if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
+ /* try "pgetc" now: */
+ pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
+ if (--g_parsefile->left_in_line >= 0)
+ return (unsigned char)(*g_parsefile->next_to_pgetc++);
+ }
+ /* on both branches above g_parsefile->left_in_line < 0.
+ * "pgetc" needs refilling.
+ */
+
+ /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
+ * pungetc() may increment it a few times.
+ * Assuming it won't increment it to less than -90.
+ */
+ if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
+ pgetc_debug("preadbuffer PEOF1");
+ /* even in failure keep left_in_line and next_to_pgetc
+ * in lock step, for correct multi-layer pungetc.
+ * left_in_line was decremented before preadbuffer(),
+ * must inc next_to_pgetc: */
+ g_parsefile->next_to_pgetc++;
return PEOF;
- flush_stdout_stderr();
+ }
- more = parselleft;
+ more = g_parsefile->left_in_buffer;
if (more <= 0) {
+ flush_stdout_stderr();
again:
more = preadfd();
if (more <= 0) {
- parselleft = parsenleft = EOF_NLEFT;
+ /* don't try reading again */
+ g_parsefile->left_in_line = -99;
+ pgetc_debug("preadbuffer PEOF2");
+ g_parsefile->next_to_pgetc++;
return PEOF;
}
}
- q = parsenextc;
-
- /* delete nul characters */
+ /* Find out where's the end of line.
+ * Set g_parsefile->left_in_line
+ * and g_parsefile->left_in_buffer acordingly.
+ * NUL chars are deleted.
+ */
+ q = g_parsefile->next_to_pgetc;
for (;;) {
- int c;
+ char c;
more--;
- c = *q;
- if (!c)
+ c = *q;
+ if (c == '\0') {
memmove(q, q + 1, more);
- else {
+ } else {
q++;
if (c == '\n') {
- parsenleft = q - parsenextc - 1;
+ g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
break;
}
}
if (more <= 0) {
- parsenleft = q - parsenextc - 1;
- if (parsenleft < 0)
+ g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
+ if (g_parsefile->left_in_line < 0)
goto again;
break;
}
}
- parselleft = more;
-
- savec = *q;
- *q = '\0';
+ g_parsefile->left_in_buffer = more;
if (vflag) {
- out2str(parsenextc);
+ char save = *q;
+ *q = '\0';
+ out2str(g_parsefile->next_to_pgetc);
+ *q = save;
}
- *q = savec;
-
- return signed_char2int(*parsenextc++);
+ pgetc_debug("preadbuffer at %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
+ return (unsigned char)(*g_parsefile->next_to_pgetc++);
}
-#define pgetc_as_macro() (--parsenleft >= 0 ? signed_char2int(*parsenextc++) : preadbuffer())
+#define pgetc_as_macro() \
+ (--g_parsefile->left_in_line >= 0 \
+ ? (unsigned char)(*g_parsefile->next_to_pgetc++) \
+ : preadbuffer() \
+ )
+
static int
pgetc(void)
{
+ pgetc_debug("pgetc_fast at %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
return pgetc_as_macro();
}
{
int c;
do {
+ pgetc_debug("pgetc_fast at %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
c = pgetc_fast();
} while (c == PEOA);
return c;
static void
pungetc(void)
{
- parsenleft++;
- parsenextc--;
-}
-
-/*
- * Push a string back onto the input at this current parsefile level.
- * We handle aliases this way.
- */
-#if !ENABLE_ASH_ALIAS
-#define pushstring(s, ap) pushstring(s)
-#endif
-static void
-pushstring(char *s, struct alias *ap)
-{
- struct strpush *sp;
- size_t len;
-
- len = strlen(s);
- INT_OFF;
- if (g_parsefile->strpush) {
- sp = ckzalloc(sizeof(struct strpush));
- sp->prev = g_parsefile->strpush;
- g_parsefile->strpush = sp;
- } else
- sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
- sp->prevstring = parsenextc;
- sp->prevnleft = parsenleft;
-#if ENABLE_ASH_ALIAS
- sp->ap = ap;
- if (ap) {
- ap->flag |= ALIASINUSE;
- sp->string = s;
- }
-#endif
- parsenextc = s;
- parsenleft = len;
- INT_ON;
+ g_parsefile->left_in_line++;
+ g_parsefile->next_to_pgetc--;
+ pgetc_debug("pushed back to %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
}
/*
{
struct parsefile *pf;
- g_parsefile->nleft = parsenleft;
- g_parsefile->lleft = parselleft;
- g_parsefile->nextc = parsenextc;
- g_parsefile->linno = plinno;
pf = ckzalloc(sizeof(*pf));
pf->prev = g_parsefile;
pf->fd = -1;
popstring();
g_parsefile = pf->prev;
free(pf);
- parsenleft = g_parsefile->nleft;
- parselleft = g_parsefile->lleft;
- parsenextc = g_parsefile->nextc;
- plinno = g_parsefile->linno;
INT_ON;
}
close_on_exec_on(fd);
if (push) {
pushfile();
- g_parsefile->buf = 0;
+ g_parsefile->buf = NULL;
}
g_parsefile->fd = fd;
if (g_parsefile->buf == NULL)
g_parsefile->buf = ckmalloc(IBUFSIZ);
- parselleft = parsenleft = 0;
- plinno = 1;
+ g_parsefile->left_in_buffer = 0;
+ g_parsefile->left_in_line = 0;
+ g_parsefile->linno = 1;
}
/*
{
INT_OFF;
pushfile();
- parsenextc = string;
- parsenleft = strlen(string);
+ g_parsefile->next_to_pgetc = string;
+ g_parsefile->left_in_line = strlen(string);
g_parsefile->buf = NULL;
- plinno = 1;
+ g_parsefile->linno = 1;
INT_ON;
}
char msg[64];
int l;
- l = sprintf(msg, "%s unexpected", tokname(lasttoken));
+ l = sprintf(msg, "unexpected %s", tokname(lasttoken));
if (token >= 0)
sprintf(msg + l, " (expecting %s)", tokname(token));
raise_error_syntax(msg);
(void) &prevsyntax;
(void) &syntax;
#endif
- startlinno = plinno;
+ startlinno = g_parsefile->linno;
bqlist = NULL;
quotef = 0;
oldstyle = 0;
if (syntax == BASESYNTAX)
goto endword; /* exit outer loop */
USTPUTC(c, out);
- plinno++;
+ g_parsefile->linno++;
if (doprompt)
setprompt(2);
c = pgetc();
if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
raise_error_syntax("unterminated quoted string");
if (varnest != 0) {
- startlinno = plinno;
+ startlinno = g_parsefile->linno;
/* { */
raise_error_syntax("missing '}'");
}
continue;
if (*p == '\n' && *q == '\0') {
c = PEOF;
- plinno++;
+ g_parsefile->linno++;
needprompt = doprompt;
} else {
pushstring(line, NULL);
case '\\':
pc = pgetc();
if (pc == '\n') {
- plinno++;
+ g_parsefile->linno++;
if (doprompt)
setprompt(2);
/*
#if ENABLE_ASH_ALIAS
case PEOA:
#endif
- startlinno = plinno;
+ startlinno = g_parsefile->linno;
raise_error_syntax("EOF in backquote substitution");
case '\n':
- plinno++;
+ g_parsefile->linno++;
needprompt = doprompt;
break;
if (needprompt) {
setprompt(2);
}
- startlinno = plinno;
+ startlinno = g_parsefile->linno;
for (;;) { /* until token or start of word found */
c = pgetc_fast();
if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
pungetc();
break; /* return readtoken1(...) */
}
- startlinno = ++plinno;
+ startlinno = ++g_parsefile->linno;
if (doprompt)
setprompt(2);
} else {
p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
if (c != PEOF) {
if (c == '\n') {
- plinno++;
+ g_parsefile->linno++;
needprompt = doprompt;
}
if (needprompt) {
setprompt(2);
}
- startlinno = plinno;
+ startlinno = g_parsefile->linno;
for (;;) { /* until token or start of word found */
c = pgetc_fast();
switch (c) {
continue;
case '\\':
if (pgetc() == '\n') {
- startlinno = ++plinno;
+ startlinno = ++g_parsefile->linno;
if (doprompt)
setprompt(2);
continue;
pungetc();
goto breakloop;
case '\n':
- plinno++;
+ g_parsefile->linno++;
needprompt = doprompt;
RETURN(TNL);
case PEOF:
init(void)
{
/* from input.c: */
- basepf.nextc = basepf.buf = basebuf;
+ basepf.next_to_pgetc = basepf.buf = basebuf;
/* from trap.c: */
signal(SIGCHLD, SIG_DFL);
evalskip = 0;
loopnest = 0;
/* from input.c: */
- parselleft = parsenleft = 0; /* clear input buffer */
+ g_parsefile->left_in_buffer = 0;
+ g_parsefile->left_in_line = 0; /* clear input buffer */
popallfiles();
/* from parser.c: */
tokpushback = 0;