diff -urpN busybox-1.13.3/shell/ash.c busybox-1.13.3-ash/shell/ash.c --- busybox-1.13.3/shell/ash.c 2009-02-26 12:46:55.000000000 +0100 +++ busybox-1.13.3-ash/shell/ash.c 2009-04-01 01:16:44.000000000 +0200 @@ -30,7 +30,7 @@ */ /* - * The follow should be set to reflect the type of system you have: + * The following should be set to reflect the type of system you have: * JOBS -> 1 if you have Berkeley job control, 0 otherwise. * define SYSV if you are running under System V. * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) @@ -40,6 +40,11 @@ * a quit signal will generate a core dump. */ #define DEBUG 0 +/* Tweak debug output verbosity here */ +#define DEBUG_TIME 0 +#define DEBUG_PID 1 +#define DEBUG_SIG 1 + #define PROFILE 0 #define IFS_BROKEN @@ -47,9 +52,9 @@ #define JOBS ENABLE_ASH_JOB_CONTROL #if DEBUG -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif #endif #include "busybox.h" /* for applet_names */ @@ -57,15 +62,15 @@ #include #include #if JOBS || ENABLE_ASH_READ_NCHARS -#include +# include #endif #ifndef PIPE_BUF -#define PIPE_BUF 4096 /* amount of buffering in a pipe */ +# define PIPE_BUF 4096 /* amount of buffering in a pipe */ #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 uClinux" #endif @@ -76,14 +81,6 @@ #define CMDTABLESIZE 31 /* should be prime */ -/* ============ Misc helpers */ - -#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) - -/* C99 say: "char" declaration may be signed or unsigned default */ -#define signed_char2int(sc) ((int)((signed char)sc)) - - /* ============ Shell options */ static const char *const optletters_optnames[] = { @@ -245,7 +242,30 @@ extern struct globals_misc *const ash_pt } while (0) +/* ============ DEBUG */ +#if DEBUG +static void trace_printf(const char *fmt, ...); +static void trace_vprintf(const char *fmt, va_list va); +# define TRACE(param) trace_printf param +# define TRACEV(param) trace_vprintf param +# define close(f) do { \ + int dfd = (f); \ + if (close(dfd) < 0) \ + bb_error_msg("bug on %d: closing %d(%x)", \ + __LINE__, dfd, dfd); \ +} while (0) +#else +# define TRACE(param) +# define TRACEV(param) +#endif + + /* ============ Utility functions */ +#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) + +/* C99 say: "char" declaration may be signed or unsigned by default */ +#define signed_char2int(sc) ((int)(signed char)(sc)) + static int isdigit_str9(const char *str) { int maxlen = 9 + 1; /* max 9 digits: 999999999 */ @@ -284,6 +304,12 @@ raise_exception(int e) exception = e; longjmp(exception_handler->loc, 1); } +#if DEBUG +#define raise_exception(e) do { \ + TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \ + raise_exception(e); \ +} while (0) +#endif /* * Called from trap.c when a SIGINT is received. (If the user specifies @@ -316,6 +342,12 @@ raise_interrupt(void) raise_exception(i); /* NOTREACHED */ } +#if DEBUG +#define raise_interrupt() do { \ + TRACE(("raising interrupt on line %d\n", __LINE__)); \ + raise_interrupt(); \ +} while (0) +#endif #if ENABLE_ASH_OPTIMIZE_FOR_SIZE static void @@ -334,7 +366,9 @@ force_int_on(void) raise_interrupt(); } #define FORCE_INT_ON force_int_on() -#else + +#else /* !ASH_OPTIMIZE_FOR_SIZE */ + #define INT_ON do { \ xbarrier(); \ if (--suppressint == 0 && intpending) \ @@ -346,7 +380,7 @@ force_int_on(void) if (intpending) \ raise_interrupt(); \ } while (0) -#endif /* ASH_OPTIMIZE_FOR_SIZE */ +#endif /* !ASH_OPTIMIZE_FOR_SIZE */ #define SAVE_INT(v) ((v) = suppressint) @@ -376,7 +410,6 @@ static void onsig(int signo) { gotsig[signo - 1] = 1; - pendingsig = signo; if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) { if (!suppressint) { @@ -384,6 +417,8 @@ onsig(int signo) raise_interrupt(); /* does not return */ } intpending = 1; + } else { + pendingsig = signo; } } @@ -684,6 +719,12 @@ trace_printf(const char *fmt, ...) if (debug != 1) return; + if (DEBUG_TIME) + fprintf(tracefile, "%u ", (int) time(NULL)); + if (DEBUG_PID) + fprintf(tracefile, "[%u] ", (int) getpid()); + if (DEBUG_SIG) + fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); va_start(va, fmt); vfprintf(tracefile, fmt, va); va_end(va); @@ -694,6 +735,12 @@ trace_vprintf(const char *fmt, va_list v { if (debug != 1) return; + if (DEBUG_TIME) + fprintf(tracefile, "%u ", (int) time(NULL)); + if (DEBUG_PID) + fprintf(tracefile, "[%u] ", (int) getpid()); + if (DEBUG_SIG) + fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); vfprintf(tracefile, fmt, va); } @@ -998,14 +1045,6 @@ showtree(union node *n) shtree(n, 1, NULL, stdout); } -#define TRACE(param) trace_printf param -#define TRACEV(param) trace_vprintf param - -#else - -#define TRACE(param) -#define TRACEV(param) - #endif /* DEBUG */ @@ -3779,7 +3818,7 @@ dowait(int wait_flags, struct job *job) * 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)); + TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", pid, status, errno, strerror(errno))); if (pid <= 0) { /* If we were doing blocking wait and (probably) got EINTR, @@ -5031,7 +5070,9 @@ redirect(union node *redir, int flags) if (newfd < 0) { /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */ if (redir->ndup.dupfd < 0) { /* "fd>&-" */ - close(fd); + /* Don't want to trigger debugging */ + if (fd != -1) + close(fd); } else { copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT); } @@ -5084,7 +5125,7 @@ popredir(int drop, int restore) /*close(fd);*/ copyfd(copy, fd | COPYFD_EXACT); } - close(copy); + close(copy & ~COPYFD_RESTORE); } } redirlist = rp->next; @@ -7871,20 +7912,30 @@ dotrap(void) pendingsig = 0; xbarrier(); + TRACE(("dotrap entered\n")); for (i = 1, q = gotsig; i < NSIG; i++, q++) { if (!*q) continue; - *q = '\0'; p = trap[i]; + /* non-trapped SIGINT is handled separately by raise_interrupt, + * don't upset it by resetting gotsig[SIGINT-1] */ + if (i == SIGINT && !p) + continue; + + TRACE(("sig %d is active, will run handler '%s'\n", i, p)); + *q = '\0'; if (!p) continue; skip = evalstring(p, SKIPEVAL); exitstatus = savestatus; - if (skip) + if (skip) { + TRACE(("dotrap returns %d\n", skip)); return skip; + } } + TRACE(("dotrap returns 0\n")); return 0; } @@ -7906,28 +7957,32 @@ static void prehash(union node *); static void evaltree(union node *n, int flags) { - struct jmploc *volatile savehandler = exception_handler; struct jmploc jmploc; int checkexit = 0; void (*evalfn)(union node *, int); int status; + int int_level; + + SAVE_INT(int_level); if (n == NULL) { TRACE(("evaltree(NULL) called\n")); goto out1; } - TRACE(("pid %d, evaltree(%p: %d, %d) called\n", - getpid(), n, n->type, flags)); + TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); exception_handler = &jmploc; { int err = setjmp(jmploc.loc); if (err) { /* if it was a signal, check for trap handlers */ - if (exception == EXSIG) + if (exception == EXSIG) { + TRACE(("exception %d (EXSIG) in evaltree, err=%d\n", exception, err)); goto out; + } /* continue on the way out */ + TRACE(("exception %d in evaltree, propagating err=%d\n", exception, err)); exception_handler = savehandler; longjmp(exception_handler->loc, err); } @@ -8010,7 +8065,8 @@ evaltree(union node *n, int flags) if (exitstatus == 0) { n = n->nif.ifpart; goto evaln; - } else if (n->nif.elsepart) { + } + if (n->nif.elsepart) { n = n->nif.elsepart; goto evaln; } @@ -8036,6 +8092,9 @@ evaltree(union node *n, int flags) exexit: raise_exception(EXEXIT); } + + RESTORE_INT(int_level); + TRACE(("leaving evaltree (no interrupts)\n")); } #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) @@ -8281,7 +8340,9 @@ evalpipe(union node *n, int flags) if (prevfd >= 0) close(prevfd); prevfd = pip[0]; - close(pip[1]); + /* Don't want to trigger debugging */ + if (pip[1] != -1) + close(pip[1]); } if (n->npipe.pipe_backgnd == 0) { exitstatus = waitforjob(jp); @@ -8913,6 +8974,7 @@ evalcommand(union node *cmd, int flags) if (forkshell(jp, cmd, FORK_FG) != 0) { exitstatus = waitforjob(jp); INT_ON; + TRACE(("forked child exited with %d\n", exitstatus)); break; } FORCE_INT_ON; @@ -12391,7 +12453,7 @@ readcmd(int argc UNUSED_PARAM, char **ar #endif status = 0; - startword = 1; + startword = 2; backslash = 0; #if ENABLE_ASH_READ_TIMEOUT if (timeout) /* NB: ensuring end_ms is nonzero */ @@ -12399,6 +12461,8 @@ readcmd(int argc UNUSED_PARAM, char **ar #endif STARTSTACKSTR(p); do { + const char *is_ifs; + #if ENABLE_ASH_READ_TIMEOUT if (end_ms) { struct pollfd pfd[1]; @@ -12428,25 +12492,34 @@ readcmd(int argc UNUSED_PARAM, char **ar continue; } if (!rflag && c == '\\') { - backslash++; + backslash = 1; continue; } if (c == '\n') break; - if (startword && *ifs == ' ' && strchr(ifs, c)) { - continue; + is_ifs = strchr(ifs, c); + if (startword && is_ifs) { + if (isspace(c)) + continue; + /* non-space ifs char */ + startword--; + if (startword == 1) /* first one? */ + continue; } startword = 0; - if (ap[1] != NULL && strchr(ifs, c) != NULL) { + if (ap[1] != NULL && is_ifs) { + const char *beg; STACKSTRNUL(p); - setvar(*ap, stackblock(), 0); + beg = stackblock(); + setvar(*ap, beg, 0); ap++; - startword = 1; + /* can we skip one non-space ifs? (2: yes) */ + startword = isspace(c) ? 2 : 1; STARTSTACKSTR(p); - } else { - put: - STPUTC(c, p); + continue; } + put: + STPUTC(c, p); } /* end of do {} while: */ #if ENABLE_ASH_READ_NCHARS @@ -12460,8 +12533,8 @@ readcmd(int argc UNUSED_PARAM, char **ar #endif STACKSTRNUL(p); - /* Remove trailing blanks */ - while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL) + /* Remove trailing space ifs chars */ + while ((char *)stackblock() <= --p && isspace(*p) && strchr(ifs, *p) != NULL) *p = '\0'; setvar(*ap, stackblock(), 0); while (*++ap != NULL) @@ -13640,7 +13713,7 @@ int ash_main(int argc UNUSED_PARAM, char exception_handler = &jmploc; #if DEBUG opentrace(); - trace_puts("Shell args: "); + TRACE(("Shell args: ")); trace_puts_args(argv); #endif rootpid = getpid(); @@ -13692,8 +13765,14 @@ int ash_main(int argc UNUSED_PARAM, char } state3: state = 4; - if (minusc) + if (minusc) { + /* evalstring pushes parsefile stack. + * Ensure we don't falsely claim that 0 (stdin) + * is one of stacked source fds */ + if (!sflag) + g_parsefile->fd = -1; evalstring(minusc, 0); + } if (sflag || minusc == NULL) { #if ENABLE_FEATURE_EDITING_SAVEHISTORY @@ -13720,14 +13799,6 @@ int ash_main(int argc UNUSED_PARAM, char /* NOTREACHED */ } -#if DEBUG -const char *applet_name = "debug stuff usage"; -int main(int argc, char **argv) -{ - return ash_main(argc, argv); -} -#endif - /*- * Copyright (c) 1989, 1991, 1993, 1994 diff -urpN busybox-1.13.3/shell/ash_test/ash-read/read_ifs.right busybox-1.13.3-ash/shell/ash_test/ash-read/read_ifs.right --- busybox-1.13.3/shell/ash_test/ash-read/read_ifs.right 1970-01-01 01:00:00.000000000 +0100 +++ busybox-1.13.3-ash/shell/ash_test/ash-read/read_ifs.right 2009-04-01 01:16:44.000000000 +0200 @@ -0,0 +1,7 @@ +.a. .b. .c. +.a. .b. .c. +.a. .. .b,c. +.a. .. .b,c. +.a. .. .c. +.a. .. .c. .d. +.a. .. .b,c,d , ,. diff -urpN busybox-1.13.3/shell/ash_test/ash-read/read_ifs.tests busybox-1.13.3-ash/shell/ash_test/ash-read/read_ifs.tests --- busybox-1.13.3/shell/ash_test/ash-read/read_ifs.tests 1970-01-01 01:00:00.000000000 +0100 +++ busybox-1.13.3-ash/shell/ash_test/ash-read/read_ifs.tests 2009-04-01 01:16:44.000000000 +0200 @@ -0,0 +1,7 @@ +printf 'a\t\tb\tc\n' | ( IFS=$(printf "\t") read a b c; echo ".$a. .$b. .$c." ) +printf 'a\t\tb\tc\n' | ( IFS=$(printf " \t") read a b c; echo ".$a. .$b. .$c." ) +printf 'a,,b,c\n' | ( IFS="," read a b c; echo ".$a. .$b. .$c." ) +printf 'a,,b,c\n' | ( IFS=" ," read a b c; echo ".$a. .$b. .$c." ) +printf 'a ,, c\n' | ( IFS=" ," read a b c; echo ".$a. .$b. .$c." ) +printf 'a ,, c d\n' | ( IFS=" ," read a b c d; echo ".$a. .$b. .$c. .$d." ) +printf ' a,,b,c,d , ,\n' | ( IFS=" ," read a b c; echo ".$a. .$b. .$c." )