diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-15 00:11:22 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-15 00:11:22 +0000 |
| commit | 8f0c89e05a7b52f8c5ae7b451de6fd50732aeb45 (patch) | |
| tree | 9bf309fce9eff03da8980e84d3de5741627bf96d /shell | |
| parent | bc7177187f6b4f32c2f9562358294dfc7b521f67 (diff) | |
| download | busybox-w32-1_13_4.tar.gz busybox-w32-1_13_4.tar.bz2 busybox-w32-1_13_4.zip | |
apply post-1.13.3 fixes, bump version to 1.13.41_13_4
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash.c | 189 | ||||
| -rw-r--r-- | shell/ash_test/ash-read/read_ifs.right | 7 | ||||
| -rwxr-xr-x | shell/ash_test/ash-read/read_ifs.tests | 7 | ||||
| -rw-r--r-- | shell/hush.c | 95 | ||||
| -rw-r--r-- | shell/hush_test/hush-parsing/starquoted2.right | 1 | ||||
| -rwxr-xr-x | shell/hush_test/hush-parsing/starquoted2.tests | 3 |
6 files changed, 230 insertions, 72 deletions
diff --git a/shell/ash.c b/shell/ash.c index d6fd38849..e3f79fcd5 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -30,7 +30,7 @@ | |||
| 30 | */ | 30 | */ |
| 31 | 31 | ||
| 32 | /* | 32 | /* |
| 33 | * The follow should be set to reflect the type of system you have: | 33 | * The following should be set to reflect the type of system you have: |
| 34 | * JOBS -> 1 if you have Berkeley job control, 0 otherwise. | 34 | * JOBS -> 1 if you have Berkeley job control, 0 otherwise. |
| 35 | * define SYSV if you are running under System V. | 35 | * define SYSV if you are running under System V. |
| 36 | * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) | 36 | * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) |
| @@ -40,6 +40,11 @@ | |||
| 40 | * a quit signal will generate a core dump. | 40 | * a quit signal will generate a core dump. |
| 41 | */ | 41 | */ |
| 42 | #define DEBUG 0 | 42 | #define DEBUG 0 |
| 43 | /* Tweak debug output verbosity here */ | ||
| 44 | #define DEBUG_TIME 0 | ||
| 45 | #define DEBUG_PID 1 | ||
| 46 | #define DEBUG_SIG 1 | ||
| 47 | |||
| 43 | #define PROFILE 0 | 48 | #define PROFILE 0 |
| 44 | 49 | ||
| 45 | #define IFS_BROKEN | 50 | #define IFS_BROKEN |
| @@ -47,9 +52,9 @@ | |||
| 47 | #define JOBS ENABLE_ASH_JOB_CONTROL | 52 | #define JOBS ENABLE_ASH_JOB_CONTROL |
| 48 | 53 | ||
| 49 | #if DEBUG | 54 | #if DEBUG |
| 50 | #ifndef _GNU_SOURCE | 55 | # ifndef _GNU_SOURCE |
| 51 | #define _GNU_SOURCE | 56 | # define _GNU_SOURCE |
| 52 | #endif | 57 | # endif |
| 53 | #endif | 58 | #endif |
| 54 | 59 | ||
| 55 | #include "busybox.h" /* for applet_names */ | 60 | #include "busybox.h" /* for applet_names */ |
| @@ -57,15 +62,15 @@ | |||
| 57 | #include <setjmp.h> | 62 | #include <setjmp.h> |
| 58 | #include <fnmatch.h> | 63 | #include <fnmatch.h> |
| 59 | #if JOBS || ENABLE_ASH_READ_NCHARS | 64 | #if JOBS || ENABLE_ASH_READ_NCHARS |
| 60 | #include <termios.h> | 65 | # include <termios.h> |
| 61 | #endif | 66 | #endif |
| 62 | 67 | ||
| 63 | #ifndef PIPE_BUF | 68 | #ifndef PIPE_BUF |
| 64 | #define PIPE_BUF 4096 /* amount of buffering in a pipe */ | 69 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ |
| 65 | #endif | 70 | #endif |
| 66 | 71 | ||
| 67 | #if defined(__uClinux__) | 72 | #if defined(__uClinux__) |
| 68 | #error "Do not even bother, ash will not run on uClinux" | 73 | # error "Do not even bother, ash will not run on uClinux" |
| 69 | #endif | 74 | #endif |
| 70 | 75 | ||
| 71 | 76 | ||
| @@ -76,14 +81,6 @@ | |||
| 76 | #define CMDTABLESIZE 31 /* should be prime */ | 81 | #define CMDTABLESIZE 31 /* should be prime */ |
| 77 | 82 | ||
| 78 | 83 | ||
| 79 | /* ============ Misc helpers */ | ||
| 80 | |||
| 81 | #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) | ||
| 82 | |||
| 83 | /* C99 say: "char" declaration may be signed or unsigned default */ | ||
| 84 | #define signed_char2int(sc) ((int)((signed char)sc)) | ||
| 85 | |||
| 86 | |||
| 87 | /* ============ Shell options */ | 84 | /* ============ Shell options */ |
| 88 | 85 | ||
| 89 | static const char *const optletters_optnames[] = { | 86 | static const char *const optletters_optnames[] = { |
| @@ -245,7 +242,30 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; | |||
| 245 | } while (0) | 242 | } while (0) |
| 246 | 243 | ||
| 247 | 244 | ||
| 245 | /* ============ DEBUG */ | ||
| 246 | #if DEBUG | ||
| 247 | static void trace_printf(const char *fmt, ...); | ||
| 248 | static void trace_vprintf(const char *fmt, va_list va); | ||
| 249 | # define TRACE(param) trace_printf param | ||
| 250 | # define TRACEV(param) trace_vprintf param | ||
| 251 | # define close(f) do { \ | ||
| 252 | int dfd = (f); \ | ||
| 253 | if (close(dfd) < 0) \ | ||
| 254 | bb_error_msg("bug on %d: closing %d(%x)", \ | ||
| 255 | __LINE__, dfd, dfd); \ | ||
| 256 | } while (0) | ||
| 257 | #else | ||
| 258 | # define TRACE(param) | ||
| 259 | # define TRACEV(param) | ||
| 260 | #endif | ||
| 261 | |||
| 262 | |||
| 248 | /* ============ Utility functions */ | 263 | /* ============ Utility functions */ |
| 264 | #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) | ||
| 265 | |||
| 266 | /* C99 say: "char" declaration may be signed or unsigned by default */ | ||
| 267 | #define signed_char2int(sc) ((int)(signed char)(sc)) | ||
| 268 | |||
| 249 | static int isdigit_str9(const char *str) | 269 | static int isdigit_str9(const char *str) |
| 250 | { | 270 | { |
| 251 | int maxlen = 9 + 1; /* max 9 digits: 999999999 */ | 271 | int maxlen = 9 + 1; /* max 9 digits: 999999999 */ |
| @@ -284,6 +304,12 @@ raise_exception(int e) | |||
| 284 | exception = e; | 304 | exception = e; |
| 285 | longjmp(exception_handler->loc, 1); | 305 | longjmp(exception_handler->loc, 1); |
| 286 | } | 306 | } |
| 307 | #if DEBUG | ||
| 308 | #define raise_exception(e) do { \ | ||
| 309 | TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \ | ||
| 310 | raise_exception(e); \ | ||
| 311 | } while (0) | ||
| 312 | #endif | ||
| 287 | 313 | ||
| 288 | /* | 314 | /* |
| 289 | * Called from trap.c when a SIGINT is received. (If the user specifies | 315 | * Called from trap.c when a SIGINT is received. (If the user specifies |
| @@ -316,6 +342,12 @@ raise_interrupt(void) | |||
| 316 | raise_exception(i); | 342 | raise_exception(i); |
| 317 | /* NOTREACHED */ | 343 | /* NOTREACHED */ |
| 318 | } | 344 | } |
| 345 | #if DEBUG | ||
| 346 | #define raise_interrupt() do { \ | ||
| 347 | TRACE(("raising interrupt on line %d\n", __LINE__)); \ | ||
| 348 | raise_interrupt(); \ | ||
| 349 | } while (0) | ||
| 350 | #endif | ||
| 319 | 351 | ||
| 320 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE | 352 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
| 321 | static void | 353 | static void |
| @@ -334,7 +366,9 @@ force_int_on(void) | |||
| 334 | raise_interrupt(); | 366 | raise_interrupt(); |
| 335 | } | 367 | } |
| 336 | #define FORCE_INT_ON force_int_on() | 368 | #define FORCE_INT_ON force_int_on() |
| 337 | #else | 369 | |
| 370 | #else /* !ASH_OPTIMIZE_FOR_SIZE */ | ||
| 371 | |||
| 338 | #define INT_ON do { \ | 372 | #define INT_ON do { \ |
| 339 | xbarrier(); \ | 373 | xbarrier(); \ |
| 340 | if (--suppressint == 0 && intpending) \ | 374 | if (--suppressint == 0 && intpending) \ |
| @@ -346,7 +380,7 @@ force_int_on(void) | |||
| 346 | if (intpending) \ | 380 | if (intpending) \ |
| 347 | raise_interrupt(); \ | 381 | raise_interrupt(); \ |
| 348 | } while (0) | 382 | } while (0) |
| 349 | #endif /* ASH_OPTIMIZE_FOR_SIZE */ | 383 | #endif /* !ASH_OPTIMIZE_FOR_SIZE */ |
| 350 | 384 | ||
| 351 | #define SAVE_INT(v) ((v) = suppressint) | 385 | #define SAVE_INT(v) ((v) = suppressint) |
| 352 | 386 | ||
| @@ -376,7 +410,6 @@ static void | |||
| 376 | onsig(int signo) | 410 | onsig(int signo) |
| 377 | { | 411 | { |
| 378 | gotsig[signo - 1] = 1; | 412 | gotsig[signo - 1] = 1; |
| 379 | pendingsig = signo; | ||
| 380 | 413 | ||
| 381 | if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) { | 414 | if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) { |
| 382 | if (!suppressint) { | 415 | if (!suppressint) { |
| @@ -384,6 +417,8 @@ onsig(int signo) | |||
| 384 | raise_interrupt(); /* does not return */ | 417 | raise_interrupt(); /* does not return */ |
| 385 | } | 418 | } |
| 386 | intpending = 1; | 419 | intpending = 1; |
| 420 | } else { | ||
| 421 | pendingsig = signo; | ||
| 387 | } | 422 | } |
| 388 | } | 423 | } |
| 389 | 424 | ||
| @@ -684,6 +719,12 @@ trace_printf(const char *fmt, ...) | |||
| 684 | 719 | ||
| 685 | if (debug != 1) | 720 | if (debug != 1) |
| 686 | return; | 721 | return; |
| 722 | if (DEBUG_TIME) | ||
| 723 | fprintf(tracefile, "%u ", (int) time(NULL)); | ||
| 724 | if (DEBUG_PID) | ||
| 725 | fprintf(tracefile, "[%u] ", (int) getpid()); | ||
| 726 | if (DEBUG_SIG) | ||
| 727 | fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); | ||
| 687 | va_start(va, fmt); | 728 | va_start(va, fmt); |
| 688 | vfprintf(tracefile, fmt, va); | 729 | vfprintf(tracefile, fmt, va); |
| 689 | va_end(va); | 730 | va_end(va); |
| @@ -694,6 +735,12 @@ trace_vprintf(const char *fmt, va_list va) | |||
| 694 | { | 735 | { |
| 695 | if (debug != 1) | 736 | if (debug != 1) |
| 696 | return; | 737 | return; |
| 738 | if (DEBUG_TIME) | ||
| 739 | fprintf(tracefile, "%u ", (int) time(NULL)); | ||
| 740 | if (DEBUG_PID) | ||
| 741 | fprintf(tracefile, "[%u] ", (int) getpid()); | ||
| 742 | if (DEBUG_SIG) | ||
| 743 | fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); | ||
| 697 | vfprintf(tracefile, fmt, va); | 744 | vfprintf(tracefile, fmt, va); |
| 698 | } | 745 | } |
| 699 | 746 | ||
| @@ -998,14 +1045,6 @@ showtree(union node *n) | |||
| 998 | shtree(n, 1, NULL, stdout); | 1045 | shtree(n, 1, NULL, stdout); |
| 999 | } | 1046 | } |
| 1000 | 1047 | ||
| 1001 | #define TRACE(param) trace_printf param | ||
| 1002 | #define TRACEV(param) trace_vprintf param | ||
| 1003 | |||
| 1004 | #else | ||
| 1005 | |||
| 1006 | #define TRACE(param) | ||
| 1007 | #define TRACEV(param) | ||
| 1008 | |||
| 1009 | #endif /* DEBUG */ | 1048 | #endif /* DEBUG */ |
| 1010 | 1049 | ||
| 1011 | 1050 | ||
| @@ -3779,7 +3818,7 @@ dowait(int wait_flags, struct job *job) | |||
| 3779 | * NB: _not_ safe_waitpid, we need to detect EINTR */ | 3818 | * NB: _not_ safe_waitpid, we need to detect EINTR */ |
| 3780 | pid = waitpid(-1, &status, | 3819 | pid = waitpid(-1, &status, |
| 3781 | (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags)); | 3820 | (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags)); |
| 3782 | TRACE(("wait returns pid=%d, status=0x%x\n", pid, status)); | 3821 | TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", pid, status, errno, strerror(errno))); |
| 3783 | 3822 | ||
| 3784 | if (pid <= 0) { | 3823 | if (pid <= 0) { |
| 3785 | /* If we were doing blocking wait and (probably) got EINTR, | 3824 | /* If we were doing blocking wait and (probably) got EINTR, |
| @@ -5031,7 +5070,9 @@ redirect(union node *redir, int flags) | |||
| 5031 | if (newfd < 0) { | 5070 | if (newfd < 0) { |
| 5032 | /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */ | 5071 | /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */ |
| 5033 | if (redir->ndup.dupfd < 0) { /* "fd>&-" */ | 5072 | if (redir->ndup.dupfd < 0) { /* "fd>&-" */ |
| 5034 | close(fd); | 5073 | /* Don't want to trigger debugging */ |
| 5074 | if (fd != -1) | ||
| 5075 | close(fd); | ||
| 5035 | } else { | 5076 | } else { |
| 5036 | copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT); | 5077 | copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT); |
| 5037 | } | 5078 | } |
| @@ -5084,7 +5125,7 @@ popredir(int drop, int restore) | |||
| 5084 | /*close(fd);*/ | 5125 | /*close(fd);*/ |
| 5085 | copyfd(copy, fd | COPYFD_EXACT); | 5126 | copyfd(copy, fd | COPYFD_EXACT); |
| 5086 | } | 5127 | } |
| 5087 | close(copy); | 5128 | close(copy & ~COPYFD_RESTORE); |
| 5088 | } | 5129 | } |
| 5089 | } | 5130 | } |
| 5090 | redirlist = rp->next; | 5131 | redirlist = rp->next; |
| @@ -7871,20 +7912,30 @@ dotrap(void) | |||
| 7871 | pendingsig = 0; | 7912 | pendingsig = 0; |
| 7872 | xbarrier(); | 7913 | xbarrier(); |
| 7873 | 7914 | ||
| 7915 | TRACE(("dotrap entered\n")); | ||
| 7874 | for (i = 1, q = gotsig; i < NSIG; i++, q++) { | 7916 | for (i = 1, q = gotsig; i < NSIG; i++, q++) { |
| 7875 | if (!*q) | 7917 | if (!*q) |
| 7876 | continue; | 7918 | continue; |
| 7877 | *q = '\0'; | ||
| 7878 | 7919 | ||
| 7879 | p = trap[i]; | 7920 | p = trap[i]; |
| 7921 | /* non-trapped SIGINT is handled separately by raise_interrupt, | ||
| 7922 | * don't upset it by resetting gotsig[SIGINT-1] */ | ||
| 7923 | if (i == SIGINT && !p) | ||
| 7924 | continue; | ||
| 7925 | |||
| 7926 | TRACE(("sig %d is active, will run handler '%s'\n", i, p)); | ||
| 7927 | *q = '\0'; | ||
| 7880 | if (!p) | 7928 | if (!p) |
| 7881 | continue; | 7929 | continue; |
| 7882 | skip = evalstring(p, SKIPEVAL); | 7930 | skip = evalstring(p, SKIPEVAL); |
| 7883 | exitstatus = savestatus; | 7931 | exitstatus = savestatus; |
| 7884 | if (skip) | 7932 | if (skip) { |
| 7933 | TRACE(("dotrap returns %d\n", skip)); | ||
| 7885 | return skip; | 7934 | return skip; |
| 7935 | } | ||
| 7886 | } | 7936 | } |
| 7887 | 7937 | ||
| 7938 | TRACE(("dotrap returns 0\n")); | ||
| 7888 | return 0; | 7939 | return 0; |
| 7889 | } | 7940 | } |
| 7890 | 7941 | ||
| @@ -7906,28 +7957,32 @@ static void prehash(union node *); | |||
| 7906 | static void | 7957 | static void |
| 7907 | evaltree(union node *n, int flags) | 7958 | evaltree(union node *n, int flags) |
| 7908 | { | 7959 | { |
| 7909 | |||
| 7910 | struct jmploc *volatile savehandler = exception_handler; | 7960 | struct jmploc *volatile savehandler = exception_handler; |
| 7911 | struct jmploc jmploc; | 7961 | struct jmploc jmploc; |
| 7912 | int checkexit = 0; | 7962 | int checkexit = 0; |
| 7913 | void (*evalfn)(union node *, int); | 7963 | void (*evalfn)(union node *, int); |
| 7914 | int status; | 7964 | int status; |
| 7965 | int int_level; | ||
| 7966 | |||
| 7967 | SAVE_INT(int_level); | ||
| 7915 | 7968 | ||
| 7916 | if (n == NULL) { | 7969 | if (n == NULL) { |
| 7917 | TRACE(("evaltree(NULL) called\n")); | 7970 | TRACE(("evaltree(NULL) called\n")); |
| 7918 | goto out1; | 7971 | goto out1; |
| 7919 | } | 7972 | } |
| 7920 | TRACE(("pid %d, evaltree(%p: %d, %d) called\n", | 7973 | TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); |
| 7921 | getpid(), n, n->type, flags)); | ||
| 7922 | 7974 | ||
| 7923 | exception_handler = &jmploc; | 7975 | exception_handler = &jmploc; |
| 7924 | { | 7976 | { |
| 7925 | int err = setjmp(jmploc.loc); | 7977 | int err = setjmp(jmploc.loc); |
| 7926 | if (err) { | 7978 | if (err) { |
| 7927 | /* if it was a signal, check for trap handlers */ | 7979 | /* if it was a signal, check for trap handlers */ |
| 7928 | if (exception == EXSIG) | 7980 | if (exception == EXSIG) { |
| 7981 | TRACE(("exception %d (EXSIG) in evaltree, err=%d\n", exception, err)); | ||
| 7929 | goto out; | 7982 | goto out; |
| 7983 | } | ||
| 7930 | /* continue on the way out */ | 7984 | /* continue on the way out */ |
| 7985 | TRACE(("exception %d in evaltree, propagating err=%d\n", exception, err)); | ||
| 7931 | exception_handler = savehandler; | 7986 | exception_handler = savehandler; |
| 7932 | longjmp(exception_handler->loc, err); | 7987 | longjmp(exception_handler->loc, err); |
| 7933 | } | 7988 | } |
| @@ -8010,7 +8065,8 @@ evaltree(union node *n, int flags) | |||
| 8010 | if (exitstatus == 0) { | 8065 | if (exitstatus == 0) { |
| 8011 | n = n->nif.ifpart; | 8066 | n = n->nif.ifpart; |
| 8012 | goto evaln; | 8067 | goto evaln; |
| 8013 | } else if (n->nif.elsepart) { | 8068 | } |
| 8069 | if (n->nif.elsepart) { | ||
| 8014 | n = n->nif.elsepart; | 8070 | n = n->nif.elsepart; |
| 8015 | goto evaln; | 8071 | goto evaln; |
| 8016 | } | 8072 | } |
| @@ -8036,6 +8092,9 @@ evaltree(union node *n, int flags) | |||
| 8036 | exexit: | 8092 | exexit: |
| 8037 | raise_exception(EXEXIT); | 8093 | raise_exception(EXEXIT); |
| 8038 | } | 8094 | } |
| 8095 | |||
| 8096 | RESTORE_INT(int_level); | ||
| 8097 | TRACE(("leaving evaltree (no interrupts)\n")); | ||
| 8039 | } | 8098 | } |
| 8040 | 8099 | ||
| 8041 | #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) | 8100 | #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) |
| @@ -8281,7 +8340,9 @@ evalpipe(union node *n, int flags) | |||
| 8281 | if (prevfd >= 0) | 8340 | if (prevfd >= 0) |
| 8282 | close(prevfd); | 8341 | close(prevfd); |
| 8283 | prevfd = pip[0]; | 8342 | prevfd = pip[0]; |
| 8284 | close(pip[1]); | 8343 | /* Don't want to trigger debugging */ |
| 8344 | if (pip[1] != -1) | ||
| 8345 | close(pip[1]); | ||
| 8285 | } | 8346 | } |
| 8286 | if (n->npipe.pipe_backgnd == 0) { | 8347 | if (n->npipe.pipe_backgnd == 0) { |
| 8287 | exitstatus = waitforjob(jp); | 8348 | exitstatus = waitforjob(jp); |
| @@ -8913,6 +8974,7 @@ evalcommand(union node *cmd, int flags) | |||
| 8913 | if (forkshell(jp, cmd, FORK_FG) != 0) { | 8974 | if (forkshell(jp, cmd, FORK_FG) != 0) { |
| 8914 | exitstatus = waitforjob(jp); | 8975 | exitstatus = waitforjob(jp); |
| 8915 | INT_ON; | 8976 | INT_ON; |
| 8977 | TRACE(("forked child exited with %d\n", exitstatus)); | ||
| 8916 | break; | 8978 | break; |
| 8917 | } | 8979 | } |
| 8918 | FORCE_INT_ON; | 8980 | FORCE_INT_ON; |
| @@ -12391,7 +12453,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 12391 | #endif | 12453 | #endif |
| 12392 | 12454 | ||
| 12393 | status = 0; | 12455 | status = 0; |
| 12394 | startword = 1; | 12456 | startword = 2; |
| 12395 | backslash = 0; | 12457 | backslash = 0; |
| 12396 | #if ENABLE_ASH_READ_TIMEOUT | 12458 | #if ENABLE_ASH_READ_TIMEOUT |
| 12397 | if (timeout) /* NB: ensuring end_ms is nonzero */ | 12459 | if (timeout) /* NB: ensuring end_ms is nonzero */ |
| @@ -12399,6 +12461,8 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 12399 | #endif | 12461 | #endif |
| 12400 | STARTSTACKSTR(p); | 12462 | STARTSTACKSTR(p); |
| 12401 | do { | 12463 | do { |
| 12464 | const char *is_ifs; | ||
| 12465 | |||
| 12402 | #if ENABLE_ASH_READ_TIMEOUT | 12466 | #if ENABLE_ASH_READ_TIMEOUT |
| 12403 | if (end_ms) { | 12467 | if (end_ms) { |
| 12404 | struct pollfd pfd[1]; | 12468 | struct pollfd pfd[1]; |
| @@ -12428,25 +12492,34 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 12428 | continue; | 12492 | continue; |
| 12429 | } | 12493 | } |
| 12430 | if (!rflag && c == '\\') { | 12494 | if (!rflag && c == '\\') { |
| 12431 | backslash++; | 12495 | backslash = 1; |
| 12432 | continue; | 12496 | continue; |
| 12433 | } | 12497 | } |
| 12434 | if (c == '\n') | 12498 | if (c == '\n') |
| 12435 | break; | 12499 | break; |
| 12436 | if (startword && *ifs == ' ' && strchr(ifs, c)) { | 12500 | is_ifs = strchr(ifs, c); |
| 12437 | continue; | 12501 | if (startword && is_ifs) { |
| 12502 | if (isspace(c)) | ||
| 12503 | continue; | ||
| 12504 | /* non-space ifs char */ | ||
| 12505 | startword--; | ||
| 12506 | if (startword == 1) /* first one? */ | ||
| 12507 | continue; | ||
| 12438 | } | 12508 | } |
| 12439 | startword = 0; | 12509 | startword = 0; |
| 12440 | if (ap[1] != NULL && strchr(ifs, c) != NULL) { | 12510 | if (ap[1] != NULL && is_ifs) { |
| 12511 | const char *beg; | ||
| 12441 | STACKSTRNUL(p); | 12512 | STACKSTRNUL(p); |
| 12442 | setvar(*ap, stackblock(), 0); | 12513 | beg = stackblock(); |
| 12514 | setvar(*ap, beg, 0); | ||
| 12443 | ap++; | 12515 | ap++; |
| 12444 | startword = 1; | 12516 | /* can we skip one non-space ifs? (2: yes) */ |
| 12517 | startword = isspace(c) ? 2 : 1; | ||
| 12445 | STARTSTACKSTR(p); | 12518 | STARTSTACKSTR(p); |
| 12446 | } else { | 12519 | continue; |
| 12447 | put: | ||
| 12448 | STPUTC(c, p); | ||
| 12449 | } | 12520 | } |
| 12521 | put: | ||
| 12522 | STPUTC(c, p); | ||
| 12450 | } | 12523 | } |
| 12451 | /* end of do {} while: */ | 12524 | /* end of do {} while: */ |
| 12452 | #if ENABLE_ASH_READ_NCHARS | 12525 | #if ENABLE_ASH_READ_NCHARS |
| @@ -12460,8 +12533,8 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
| 12460 | #endif | 12533 | #endif |
| 12461 | 12534 | ||
| 12462 | STACKSTRNUL(p); | 12535 | STACKSTRNUL(p); |
| 12463 | /* Remove trailing blanks */ | 12536 | /* Remove trailing space ifs chars */ |
| 12464 | while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL) | 12537 | while ((char *)stackblock() <= --p && isspace(*p) && strchr(ifs, *p) != NULL) |
| 12465 | *p = '\0'; | 12538 | *p = '\0'; |
| 12466 | setvar(*ap, stackblock(), 0); | 12539 | setvar(*ap, stackblock(), 0); |
| 12467 | while (*++ap != NULL) | 12540 | while (*++ap != NULL) |
| @@ -13640,7 +13713,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 13640 | exception_handler = &jmploc; | 13713 | exception_handler = &jmploc; |
| 13641 | #if DEBUG | 13714 | #if DEBUG |
| 13642 | opentrace(); | 13715 | opentrace(); |
| 13643 | trace_puts("Shell args: "); | 13716 | TRACE(("Shell args: ")); |
| 13644 | trace_puts_args(argv); | 13717 | trace_puts_args(argv); |
| 13645 | #endif | 13718 | #endif |
| 13646 | rootpid = getpid(); | 13719 | rootpid = getpid(); |
| @@ -13692,8 +13765,14 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 13692 | } | 13765 | } |
| 13693 | state3: | 13766 | state3: |
| 13694 | state = 4; | 13767 | state = 4; |
| 13695 | if (minusc) | 13768 | if (minusc) { |
| 13769 | /* evalstring pushes parsefile stack. | ||
| 13770 | * Ensure we don't falsely claim that 0 (stdin) | ||
| 13771 | * is one of stacked source fds */ | ||
| 13772 | if (!sflag) | ||
| 13773 | g_parsefile->fd = -1; | ||
| 13696 | evalstring(minusc, 0); | 13774 | evalstring(minusc, 0); |
| 13775 | } | ||
| 13697 | 13776 | ||
| 13698 | if (sflag || minusc == NULL) { | 13777 | if (sflag || minusc == NULL) { |
| 13699 | #if ENABLE_FEATURE_EDITING_SAVEHISTORY | 13778 | #if ENABLE_FEATURE_EDITING_SAVEHISTORY |
| @@ -13720,14 +13799,6 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 13720 | /* NOTREACHED */ | 13799 | /* NOTREACHED */ |
| 13721 | } | 13800 | } |
| 13722 | 13801 | ||
| 13723 | #if DEBUG | ||
| 13724 | const char *applet_name = "debug stuff usage"; | ||
| 13725 | int main(int argc, char **argv) | ||
| 13726 | { | ||
| 13727 | return ash_main(argc, argv); | ||
| 13728 | } | ||
| 13729 | #endif | ||
| 13730 | |||
| 13731 | 13802 | ||
| 13732 | /*- | 13803 | /*- |
| 13733 | * Copyright (c) 1989, 1991, 1993, 1994 | 13804 | * Copyright (c) 1989, 1991, 1993, 1994 |
diff --git a/shell/ash_test/ash-read/read_ifs.right b/shell/ash_test/ash-read/read_ifs.right new file mode 100644 index 000000000..027ecd18f --- /dev/null +++ b/shell/ash_test/ash-read/read_ifs.right | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | .a. .b. .c. | ||
| 2 | .a. .b. .c. | ||
| 3 | .a. .. .b,c. | ||
| 4 | .a. .. .b,c. | ||
| 5 | .a. .. .c. | ||
| 6 | .a. .. .c. .d. | ||
| 7 | .a. .. .b,c,d , ,. | ||
diff --git a/shell/ash_test/ash-read/read_ifs.tests b/shell/ash_test/ash-read/read_ifs.tests new file mode 100755 index 000000000..cf7cd934c --- /dev/null +++ b/shell/ash_test/ash-read/read_ifs.tests | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | printf 'a\t\tb\tc\n' | ( IFS=$(printf "\t") read a b c; echo ".$a. .$b. .$c." ) | ||
| 2 | printf 'a\t\tb\tc\n' | ( IFS=$(printf " \t") read a b c; echo ".$a. .$b. .$c." ) | ||
| 3 | printf 'a,,b,c\n' | ( IFS="," read a b c; echo ".$a. .$b. .$c." ) | ||
| 4 | printf 'a,,b,c\n' | ( IFS=" ," read a b c; echo ".$a. .$b. .$c." ) | ||
| 5 | printf 'a ,, c\n' | ( IFS=" ," read a b c; echo ".$a. .$b. .$c." ) | ||
| 6 | printf 'a ,, c d\n' | ( IFS=" ," read a b c d; echo ".$a. .$b. .$c. .$d." ) | ||
| 7 | printf ' a,,b,c,d , ,\n' | ( IFS=" ," read a b c; echo ".$a. .$b. .$c." ) | ||
diff --git a/shell/hush.c b/shell/hush.c index 421272971..9e7b10535 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -458,8 +458,11 @@ struct globals { | |||
| 458 | smallint fake_mode; | 458 | smallint fake_mode; |
| 459 | /* these three support $?, $#, and $1 */ | 459 | /* these three support $?, $#, and $1 */ |
| 460 | smalluint last_return_code; | 460 | smalluint last_return_code; |
| 461 | char **global_argv; | 461 | /* is global_argv and global_argv[1..n] malloced? (note: not [0]) */ |
| 462 | smalluint global_args_malloced; | ||
| 463 | /* how many non-NULL argv's we have. NB: $# + 1 */ | ||
| 462 | int global_argc; | 464 | int global_argc; |
| 465 | char **global_argv; | ||
| 463 | #if ENABLE_HUSH_LOOPS | 466 | #if ENABLE_HUSH_LOOPS |
| 464 | unsigned depth_break_continue; | 467 | unsigned depth_break_continue; |
| 465 | unsigned depth_of_loop; | 468 | unsigned depth_of_loop; |
| @@ -633,7 +636,7 @@ static char *unbackslash(char *src) | |||
| 633 | return dst; | 636 | return dst; |
| 634 | } | 637 | } |
| 635 | 638 | ||
| 636 | static char **add_strings_to_strings(char **strings, char **add) | 639 | static char **add_strings_to_strings(char **strings, char **add, int need_to_dup) |
| 637 | { | 640 | { |
| 638 | int i; | 641 | int i; |
| 639 | unsigned count1; | 642 | unsigned count1; |
| @@ -658,7 +661,7 @@ static char **add_strings_to_strings(char **strings, char **add) | |||
| 658 | v[count1 + count2] = NULL; | 661 | v[count1 + count2] = NULL; |
| 659 | i = count2; | 662 | i = count2; |
| 660 | while (--i >= 0) | 663 | while (--i >= 0) |
| 661 | v[count1 + i] = add[i]; | 664 | v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]); |
| 662 | return v; | 665 | return v; |
| 663 | } | 666 | } |
| 664 | 667 | ||
| @@ -667,7 +670,7 @@ static char **add_string_to_strings(char **strings, char *add) | |||
| 667 | char *v[2]; | 670 | char *v[2]; |
| 668 | v[0] = add; | 671 | v[0] = add; |
| 669 | v[1] = NULL; | 672 | v[1] = NULL; |
| 670 | return add_strings_to_strings(strings, v); | 673 | return add_strings_to_strings(strings, v, /*dup:*/ 0); |
| 671 | } | 674 | } |
| 672 | 675 | ||
| 673 | static void putenv_all(char **strings) | 676 | static void putenv_all(char **strings) |
| @@ -1213,8 +1216,13 @@ static int o_glob(o_string *o, int n) | |||
| 1213 | * Otherwise, just finish current list[] and start new */ | 1216 | * Otherwise, just finish current list[] and start new */ |
| 1214 | static int o_save_ptr(o_string *o, int n) | 1217 | static int o_save_ptr(o_string *o, int n) |
| 1215 | { | 1218 | { |
| 1216 | if (o->o_glob) | 1219 | if (o->o_glob) { /* if globbing is requested */ |
| 1217 | return o_glob(o, n); /* o_save_ptr_helper is inside */ | 1220 | /* If o->has_empty_slot, list[n] was already globbed |
| 1221 | * (if it was requested back then when it was filled) | ||
| 1222 | * so don't do that again! */ | ||
| 1223 | if (!o->has_empty_slot) | ||
| 1224 | return o_glob(o, n); /* o_save_ptr_helper is inside */ | ||
| 1225 | } | ||
| 1218 | return o_save_ptr_helper(o, n); | 1226 | return o_save_ptr_helper(o, n); |
| 1219 | } | 1227 | } |
| 1220 | 1228 | ||
| @@ -4279,6 +4287,11 @@ int hush_main(int argc, char **argv) | |||
| 4279 | switch (opt) { | 4287 | switch (opt) { |
| 4280 | case 'c': | 4288 | case 'c': |
| 4281 | G.global_argv = argv + optind; | 4289 | G.global_argv = argv + optind; |
| 4290 | if (!argv[optind]) { | ||
| 4291 | /* -c 'script' (no params): prevent empty $0 */ | ||
| 4292 | *--G.global_argv = argv[0]; | ||
| 4293 | optind--; | ||
| 4294 | } /* else -c 'script' PAR0 PAR1: $0 is PAR0 */ | ||
| 4282 | G.global_argc = argc - optind; | 4295 | G.global_argc = argc - optind; |
| 4283 | opt = parse_and_run_string(optarg, 0 /* parse_flag */); | 4296 | opt = parse_and_run_string(optarg, 0 /* parse_flag */); |
| 4284 | goto final_return; | 4297 | goto final_return; |
| @@ -4639,17 +4652,68 @@ static int builtin_read(char **argv) | |||
| 4639 | return set_local_var(string, 0); | 4652 | return set_local_var(string, 0); |
| 4640 | } | 4653 | } |
| 4641 | 4654 | ||
| 4642 | /* built-in 'set [VAR=value]' handler */ | 4655 | /* built-in 'set' handler |
| 4656 | * SUSv3 says: | ||
| 4657 | * set [-abCefmnuvx] [-h] [-o option] [argument...] | ||
| 4658 | * set [+abCefmnuvx] [+h] [+o option] [argument...] | ||
| 4659 | * set -- [argument...] | ||
| 4660 | * set -o | ||
| 4661 | * set +o | ||
| 4662 | * Implementations shall support the options in both their hyphen and | ||
| 4663 | * plus-sign forms. These options can also be specified as options to sh. | ||
| 4664 | * Examples: | ||
| 4665 | * Write out all variables and their values: set | ||
| 4666 | * Set $1, $2, and $3 and set "$#" to 3: set c a b | ||
| 4667 | * Turn on the -x and -v options: set -xv | ||
| 4668 | * Unset all positional parameters: set -- | ||
| 4669 | * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" | ||
| 4670 | * Set the positional parameters to the expansion of x, even if x expands | ||
| 4671 | * with a leading '-' or '+': set -- $x | ||
| 4672 | * | ||
| 4673 | * So far, we only support "set -- [argument...]" by ignoring all options | ||
| 4674 | * (also, "-o option" will be mishandled by taking "option" as parameter #1). | ||
| 4675 | */ | ||
| 4643 | static int builtin_set(char **argv) | 4676 | static int builtin_set(char **argv) |
| 4644 | { | 4677 | { |
| 4645 | char *temp = argv[1]; | ||
| 4646 | struct variable *e; | 4678 | struct variable *e; |
| 4679 | char **pp; | ||
| 4680 | char *arg = *++argv; | ||
| 4647 | 4681 | ||
| 4648 | if (temp == NULL) | 4682 | if (arg == NULL) { |
| 4649 | for (e = G.top_var; e; e = e->next) | 4683 | for (e = G.top_var; e; e = e->next) |
| 4650 | puts(e->varstr); | 4684 | puts(e->varstr); |
| 4651 | else | 4685 | } else { |
| 4652 | set_local_var(xstrdup(temp), 0); | 4686 | /* NB: G.global_argv[0] ($0) is never freed/changed */ |
| 4687 | |||
| 4688 | if (G.global_args_malloced) { | ||
| 4689 | pp = G.global_argv; | ||
| 4690 | while (*++pp) | ||
| 4691 | free(*pp); | ||
| 4692 | G.global_argv[1] = NULL; | ||
| 4693 | } else { | ||
| 4694 | G.global_args_malloced = 1; | ||
| 4695 | pp = xzalloc(sizeof(pp[0]) * 2); | ||
| 4696 | pp[0] = G.global_argv[0]; /* retain $0 */ | ||
| 4697 | G.global_argv = pp; | ||
| 4698 | } | ||
| 4699 | do { | ||
| 4700 | if (arg[0] == '+') | ||
| 4701 | continue; | ||
| 4702 | if (arg[0] != '-') | ||
| 4703 | break; | ||
| 4704 | if (arg[1] == '-' && arg[2] == '\0') { | ||
| 4705 | argv++; | ||
| 4706 | break; | ||
| 4707 | } | ||
| 4708 | } while ((arg = *++argv) != NULL); | ||
| 4709 | /* Now argv[0] is 1st argument */ | ||
| 4710 | |||
| 4711 | /* This realloc's G.global_argv */ | ||
| 4712 | G.global_argv = pp = add_strings_to_strings(G.global_argv, argv, /*dup:*/ 1); | ||
| 4713 | G.global_argc = 1; | ||
| 4714 | while (*++pp) | ||
| 4715 | G.global_argc++; | ||
| 4716 | } | ||
| 4653 | 4717 | ||
| 4654 | return EXIT_SUCCESS; | 4718 | return EXIT_SUCCESS; |
| 4655 | } | 4719 | } |
| @@ -4661,9 +4725,14 @@ static int builtin_shift(char **argv) | |||
| 4661 | n = atoi(argv[1]); | 4725 | n = atoi(argv[1]); |
| 4662 | } | 4726 | } |
| 4663 | if (n >= 0 && n < G.global_argc) { | 4727 | if (n >= 0 && n < G.global_argc) { |
| 4664 | G.global_argv[n] = G.global_argv[0]; | 4728 | if (G.global_args_malloced) { |
| 4729 | int m = 1; | ||
| 4730 | while (m <= n) | ||
| 4731 | free(G.global_argv[m++]); | ||
| 4732 | } | ||
| 4665 | G.global_argc -= n; | 4733 | G.global_argc -= n; |
| 4666 | G.global_argv += n; | 4734 | memmove(&G.global_argv[1], &G.global_argv[n+1], |
| 4735 | G.global_argc * sizeof(G.global_argv[0])); | ||
| 4667 | return EXIT_SUCCESS; | 4736 | return EXIT_SUCCESS; |
| 4668 | } | 4737 | } |
| 4669 | return EXIT_FAILURE; | 4738 | return EXIT_FAILURE; |
diff --git a/shell/hush_test/hush-parsing/starquoted2.right b/shell/hush_test/hush-parsing/starquoted2.right index 46f24369e..f4624dad5 100644 --- a/shell/hush_test/hush-parsing/starquoted2.right +++ b/shell/hush_test/hush-parsing/starquoted2.right | |||
| @@ -1,2 +1,3 @@ | |||
| 1 | Should be printed | 1 | Should be printed |
| 2 | Should be printed | 2 | Should be printed |
| 3 | Empty: | ||
diff --git a/shell/hush_test/hush-parsing/starquoted2.tests b/shell/hush_test/hush-parsing/starquoted2.tests index 782d71b88..3475edeb2 100755 --- a/shell/hush_test/hush-parsing/starquoted2.tests +++ b/shell/hush_test/hush-parsing/starquoted2.tests | |||
| @@ -12,3 +12,6 @@ for a in "$@"""; do echo Should not be printed; done | |||
| 12 | for a in """$@"; do echo Should not be printed; done | 12 | for a in """$@"; do echo Should not be printed; done |
| 13 | for a in """$@"''"$@"''; do echo Should not be printed; done | 13 | for a in """$@"''"$@"''; do echo Should not be printed; done |
| 14 | for a in ""; do echo Should be printed; done | 14 | for a in ""; do echo Should be printed; done |
| 15 | |||
| 16 | # Bug 207: "$@" expands to nothing, and we erroneously glob "%s\\n" twice: | ||
| 17 | printf "Empty:%s\\n" "$@" | ||
