aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/Kbuild.src1
-rw-r--r--shell/ash.c2410
-rw-r--r--shell/math.h2
-rw-r--r--shell/random.c11
-rw-r--r--shell/random.h3
-rw-r--r--shell/shell_common.c78
6 files changed, 2469 insertions, 36 deletions
diff --git a/shell/Kbuild.src b/shell/Kbuild.src
index 6bba4989f..a287fce4e 100644
--- a/shell/Kbuild.src
+++ b/shell/Kbuild.src
@@ -9,3 +9,4 @@ lib-y:=
9INSERT 9INSERT
10 10
11lib-$(CONFIG_FEATURE_SH_MATH) += math.o 11lib-$(CONFIG_FEATURE_SH_MATH) += math.o
12lib-$(CONFIG_FEATURE_PRNG_SHELL) += random.o
diff --git a/shell/ash.c b/shell/ash.c
index 51b627fcc..09e8725bf 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -15,6 +15,21 @@
15 * 15 *
16 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 16 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
17 */ 17 */
18
19/*
20 * MinGW notes
21 *
22 * - Environment variables from Windows will all be turned to uppercase.
23 * - PATH accepts both ; and : as separator, but can't be mixed
24 * - command without ".exe" extension is still understood as executable
25 * - shell scripts on the path are detected by the presence of '#!'
26 * - both / and \ are supported in PATH. Usually you must use /
27 * - job control doesn't work, though the jobs builtin is available
28 * - trap doesn't work for signals, only EXIT
29 * - /dev/null is supported for redirection
30 * - fake $PPID
31 */
32
18//config:config SHELL_ASH 33//config:config SHELL_ASH
19//config: bool #hidden option 34//config: bool #hidden option
20//config: depends on !NOMMU 35//config: depends on !NOMMU
@@ -158,6 +173,25 @@
158//config: you to run the specified command or builtin, 173//config: you to run the specified command or builtin,
159//config: even when there is a function with the same name. 174//config: even when there is a function with the same name.
160//config: 175//config:
176//config:
177//config:config ASH_NOCONSOLE
178//config: bool "'noconsole' option"
179//config: default y
180//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
181//config: help
182//config: Enable support for the 'noconsole' option, which attempts to
183//config: hide the console normally associated with a command line
184//config: application. This may be useful when running a shell script
185//config: from a GUI application.
186//config:
187//config:config ASH_NOCASEGLOB
188//config: bool "'nocaseglob' option"
189//config: default y
190//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
191//config: help
192//config: Enable support for the 'nocaseglob' option, which allows
193//config: case-insensitive filename globbing.
194//config:
161//config:endif # ash options 195//config:endif # ash options
162 196
163//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 197//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
@@ -183,7 +217,17 @@
183 217
184#define PROFILE 0 218#define PROFILE 0
185 219
220/*
221 * Only one of JOBS or JOBS_WIN32 is enabled at a time (or neither).
222 * JOBS_WIN32 doesn't enable job control, just some job-related features.
223 */
224#if ENABLE_PLATFORM_MINGW32
225#define JOBS_WIN32 ENABLE_ASH_JOB_CONTROL
226#define JOBS 0
227#else
228#define JOBS_WIN32 0
186#define JOBS ENABLE_ASH_JOB_CONTROL 229#define JOBS ENABLE_ASH_JOB_CONTROL
230#endif
187 231
188#include <fnmatch.h> 232#include <fnmatch.h>
189#include <sys/times.h> 233#include <sys/times.h>
@@ -194,6 +238,9 @@
194#else 238#else
195# define NUM_SCRIPTS 0 239# define NUM_SCRIPTS 0
196#endif 240#endif
241#if ENABLE_PLATFORM_MINGW32
242# include <conio.h>
243#endif
197 244
198/* So far, all bash compat is controlled by one config option */ 245/* So far, all bash compat is controlled by one config option */
199/* Separate defines document which part of code implements what */ 246/* Separate defines document which part of code implements what */
@@ -304,10 +351,81 @@ typedef long arith_t;
304# define unlikely(cond) (cond) 351# define unlikely(cond) (cond)
305#endif 352#endif
306 353
354#if !ENABLE_PLATFORM_MINGW32
355# define is_relative_path(path) ((path)[0] != '/')
356#endif
357
307#if !BB_MMU 358#if !BB_MMU
308# error "Do not even bother, ash will not run on NOMMU machine" 359# error "Do not even bother, ash will not run on NOMMU machine"
309#endif 360#endif
310 361
362#if ENABLE_PLATFORM_MINGW32
363# define FORKSHELL_DEBUG 0
364
365union node;
366struct strlist;
367struct job;
368
369struct forkshell {
370 /* filled by forkshell_copy() */
371 struct globals_var *gvp;
372 struct globals_misc *gmp;
373 struct tblentry **cmdtable;
374#if ENABLE_ASH_ALIAS
375 struct alias **atab;
376#endif
377#if MAX_HISTORY
378 char **history;
379 int cnt_history;
380#endif
381 /* struct parsefile *g_parsefile; */
382 HANDLE hMapFile;
383 char *old_base;
384 int size;
385# if FORKSHELL_DEBUG
386 int funcblocksize;
387 int funcstringsize;
388# endif
389 int relocatesize;
390
391 /* type of forkshell */
392 int fpid;
393
394 /* generic data, used by forkshell_child */
395 int mode;
396 int nprocs;
397
398 /* optional data, used by forkshell_child */
399 int flags;
400 int fd[3];
401 union node *n;
402 char **argv;
403 char *path;
404};
405
406enum {
407 FS_OPENHERE,
408 FS_EVALBACKCMD,
409 FS_EVALSUBSHELL,
410 FS_EVALPIPE,
411 FS_SHELLEXEC
412};
413
414static struct forkshell* forkshell_prepare(struct forkshell *fs);
415static void forkshell_init(const char *idstr);
416static void *sticky_mem_start, *sticky_mem_end;
417static void sticky_free(void *p);
418# define free(p) sticky_free(p)
419#if !JOBS && !JOBS_WIN32
420#define spawn_forkshell(fs, jp, n, mode) spawn_forkshell(fs, jp, mode)
421#endif
422static void spawn_forkshell(struct forkshell *fs, struct job *jp,
423 union node *n, int mode);
424# if FORKSHELL_DEBUG
425static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes);
426# endif
427#endif
428
311/* ============ Hash table sizes. Configurable. */ 429/* ============ Hash table sizes. Configurable. */
312 430
313#define VTABSIZE 39 431#define VTABSIZE 39
@@ -335,7 +453,11 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
335 "m" "monitor", 453 "m" "monitor",
336 "n" "noexec", 454 "n" "noexec",
337/* Ditto: bash has no "set -s", "set -c" */ 455/* Ditto: bash has no "set -s", "set -c" */
456#if !ENABLE_PLATFORM_MINGW32
338 "s" "", 457 "s" "",
458#else
459 "s" "stdin",
460#endif
339 "c" "", 461 "c" "",
340 "x" "xtrace", 462 "x" "xtrace",
341 "v" "verbose", 463 "v" "verbose",
@@ -352,6 +474,15 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
352 ,"\0" "nolog" 474 ,"\0" "nolog"
353 ,"\0" "debug" 475 ,"\0" "debug"
354#endif 476#endif
477#if ENABLE_PLATFORM_MINGW32
478 ,"X" "winxp"
479#endif
480#if ENABLE_ASH_NOCONSOLE
481 ,"\0" "noconsole"
482#endif
483#if ENABLE_ASH_NOCASEGLOB
484 ,"\0" "nocaseglob"
485#endif
355}; 486};
356//bash 4.4.23 also has these opts (with these defaults): 487//bash 4.4.23 also has these opts (with these defaults):
357//braceexpand on 488//braceexpand on
@@ -394,28 +525,45 @@ struct jmploc {
394struct globals_misc { 525struct globals_misc {
395 uint8_t exitstatus; /* exit status of last command */ 526 uint8_t exitstatus; /* exit status of last command */
396 uint8_t back_exitstatus;/* exit status of backquoted command */ 527 uint8_t back_exitstatus;/* exit status of backquoted command */
528#if !ENABLE_PLATFORM_MINGW32
397 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ 529 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
530#endif
398 smallint inps4; /* Prevent PS4 nesting. */ 531 smallint inps4; /* Prevent PS4 nesting. */
399 int savestatus; /* exit status of last command outside traps */ 532 int savestatus; /* exit status of last command outside traps */
400 int rootpid; /* pid of main shell */ 533 int rootpid; /* pid of main shell */
401 /* shell level: 0 for the main shell, 1 for its children, and so on */ 534 /* shell level: 0 for the main shell, 1 for its children, and so on */
402 int shlvl; 535 int shlvl;
536#if ENABLE_PLATFORM_MINGW32
537 int loopnest; /* current loop nesting level */
538#endif
403#define rootshell (!shlvl) 539#define rootshell (!shlvl)
404 int errlinno; 540 int errlinno;
405 541
406 char *minusc; /* argument to -c option */ 542 char *minusc; /* argument to -c option */
543#if ENABLE_PLATFORM_MINGW32
544 char *dirarg; /* argument to -d option */
545 char *title; /* argument to -t option */
546#if ENABLE_SUW32
547 int delayexit; /* set by -N option */
548# endif
549#endif
407 550
408 char *curdir; // = nullstr; /* current working directory */ 551 char *curdir; // = nullstr; /* current working directory */
409 char *physdir; // = nullstr; /* physical working directory */ 552 char *physdir; // = nullstr; /* physical working directory */
410 553
411 char *arg0; /* value of $0 */ 554 char *arg0; /* value of $0 */
555#if ENABLE_PLATFORM_MINGW32
556 char *commandname;
557#endif
412 558
413 struct jmploc *exception_handler; 559 struct jmploc *exception_handler;
414 560
415 volatile int suppress_int; /* counter */ 561 volatile int suppress_int; /* counter */
416 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ 562 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
563#if !ENABLE_PLATFORM_MINGW32
417 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ 564 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
418 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ 565 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
566#endif
419 smallint exception_type; /* kind of exception: */ 567 smallint exception_type; /* kind of exception: */
420#define EXINT 0 /* SIGINT received */ 568#define EXINT 0 /* SIGINT received */
421#define EXERROR 1 /* a generic error */ 569#define EXERROR 1 /* a generic error */
@@ -450,8 +598,18 @@ struct globals_misc {
450# define nolog optlist[16 + BASH_PIPEFAIL] 598# define nolog optlist[16 + BASH_PIPEFAIL]
451# define debug optlist[17 + BASH_PIPEFAIL] 599# define debug optlist[17 + BASH_PIPEFAIL]
452#endif 600#endif
601#if ENABLE_PLATFORM_MINGW32
602# define winxp optlist[16 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
603# if ENABLE_ASH_NOCONSOLE
604# define noconsole optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
605# endif
606# if ENABLE_ASH_NOCASEGLOB
607# define nocaseglob optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0) + ENABLE_ASH_NOCONSOLE]
608# endif
609#endif
453 610
454 /* trap handler commands */ 611 /* trap handler commands */
612#if !ENABLE_PLATFORM_MINGW32
455 /* 613 /*
456 * Sigmode records the current value of the signal handlers for the various 614 * Sigmode records the current value of the signal handlers for the various
457 * modes. A value of zero means that the current handler is not known. 615 * modes. A value of zero means that the current handler is not known.
@@ -465,13 +623,16 @@ struct globals_misc {
465 623
466 /* indicates specified signal received */ 624 /* indicates specified signal received */
467 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ 625 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
626#endif
468 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ 627 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
469 char *trap[NSIG + 1]; 628 char *trap[NSIG + 1];
470/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */ 629/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
471#define NTRAP_ERR NSIG 630#define NTRAP_ERR NSIG
472#define NTRAP_LAST NSIG 631#define NTRAP_LAST NSIG
473 632
633#if !ENABLE_PLATFORM_MINGW32
474 char **trap_ptr; /* used only by "trap hack" */ 634 char **trap_ptr; /* used only by "trap hack" */
635#endif
475 636
476 /* Rarely referenced stuff */ 637 /* Rarely referenced stuff */
477#if ENABLE_ASH_RANDOM_SUPPORT 638#if ENABLE_ASH_RANDOM_SUPPORT
@@ -489,10 +650,21 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
489#define rootpid (G_misc.rootpid ) 650#define rootpid (G_misc.rootpid )
490#define shlvl (G_misc.shlvl ) 651#define shlvl (G_misc.shlvl )
491#define errlinno (G_misc.errlinno ) 652#define errlinno (G_misc.errlinno )
653#if ENABLE_PLATFORM_MINGW32
654#define loopnest (G_misc.loopnest )
655#endif
492#define minusc (G_misc.minusc ) 656#define minusc (G_misc.minusc )
657#if ENABLE_PLATFORM_MINGW32
658#define dirarg (G_misc.dirarg )
659#define title (G_misc.title )
660#define delayexit (G_misc.delayexit )
661#endif
493#define curdir (G_misc.curdir ) 662#define curdir (G_misc.curdir )
494#define physdir (G_misc.physdir ) 663#define physdir (G_misc.physdir )
495#define arg0 (G_misc.arg0 ) 664#define arg0 (G_misc.arg0 )
665#if ENABLE_PLATFORM_MINGW32
666#define commandname (G_misc.commandname)
667#endif
496#define exception_handler (G_misc.exception_handler) 668#define exception_handler (G_misc.exception_handler)
497#define exception_type (G_misc.exception_type ) 669#define exception_type (G_misc.exception_type )
498#define suppress_int (G_misc.suppress_int ) 670#define suppress_int (G_misc.suppress_int )
@@ -508,12 +680,21 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
508#define trap_ptr (G_misc.trap_ptr ) 680#define trap_ptr (G_misc.trap_ptr )
509#define random_gen (G_misc.random_gen ) 681#define random_gen (G_misc.random_gen )
510#define backgndpid (G_misc.backgndpid ) 682#define backgndpid (G_misc.backgndpid )
683
684#if ENABLE_PLATFORM_MINGW32
685#undef got_sigchld
686#undef pending_sig
687#undef trap_ptr
688#define pending_sig (0)
689#define trap_ptr trap
690#endif
691
511#define INIT_G_misc() do { \ 692#define INIT_G_misc() do { \
512 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \ 693 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \
513 savestatus = -1; \ 694 savestatus = -1; \
514 curdir = nullstr; \ 695 curdir = nullstr; \
515 physdir = nullstr; \ 696 physdir = nullstr; \
516 trap_ptr = trap; \ 697 IF_NOT_PLATFORM_MINGW32(trap_ptr = trap;) \
517} while (0) 698} while (0)
518 699
519 700
@@ -523,6 +704,9 @@ static void trace_printf(const char *fmt, ...);
523static void trace_vprintf(const char *fmt, va_list va); 704static void trace_vprintf(const char *fmt, va_list va);
524# define TRACE(param) trace_printf param 705# define TRACE(param) trace_printf param
525# define TRACEV(param) trace_vprintf param 706# define TRACEV(param) trace_vprintf param
707# if ENABLE_PLATFORM_MINGW32 && defined(close)
708# undef close
709# endif
526# define close(fd) do { \ 710# define close(fd) do { \
527 int dfd = (fd); \ 711 int dfd = (fd); \
528 if (close(dfd) < 0) \ 712 if (close(dfd) < 0) \
@@ -612,11 +796,18 @@ struct parsefile {
612 796
613 /* Number of outstanding calls to pungetc. */ 797 /* Number of outstanding calls to pungetc. */
614 int unget; 798 int unget;
799
800#if ENABLE_PLATFORM_MINGW32
801 /* True if a trailing CR from a previous read was left unprocessed. */
802 int cr;
803#endif
615}; 804};
616 805
617static struct parsefile basepf; /* top level input file */ 806static struct parsefile basepf; /* top level input file */
618static struct parsefile *g_parsefile = &basepf; /* current input file */ 807static struct parsefile *g_parsefile = &basepf; /* current input file */
808#if ENABLE_PLATFORM_POSIX
619static char *commandname; /* currently executing command */ 809static char *commandname; /* currently executing command */
810#endif
620 811
621 812
622/* ============ Interrupts / exceptions */ 813/* ============ Interrupts / exceptions */
@@ -673,21 +864,38 @@ raise_exception(int e)
673 * are held using the INT_OFF macro. (The test for iflag is just 864 * are held using the INT_OFF macro. (The test for iflag is just
674 * defensive programming.) 865 * defensive programming.)
675 */ 866 */
676static void raise_interrupt(void) NORETURN; 867static void raise_interrupt(void) IF_NOT_PLATFORM_MINGW32(NORETURN);
677static void 868static void
678raise_interrupt(void) 869raise_interrupt(void)
679{ 870{
871#if ENABLE_PLATFORM_MINGW32
872 /* Contrary to the comment above on Windows raise_interrupt() is
873 * called when SIGINT is trapped or ignored. We detect this here
874 * and return without doing anything. */
875 if (trap[SIGINT])
876 return;
877#endif
680 pending_int = 0; 878 pending_int = 0;
879#if !ENABLE_PLATFORM_MINGW32
681 /* Signal is not automatically unmasked after it is raised, 880 /* Signal is not automatically unmasked after it is raised,
682 * do it ourself - unmask all signals */ 881 * do it ourself - unmask all signals */
683 sigprocmask_allsigs(SIG_UNBLOCK); 882 sigprocmask_allsigs(SIG_UNBLOCK);
883#endif
684 /* pending_sig = 0; - now done in signal_handler() */ 884 /* pending_sig = 0; - now done in signal_handler() */
685 885
686 if (!(rootshell && iflag)) { 886 if (!(rootshell && iflag)) {
887#if !ENABLE_PLATFORM_MINGW32
687 /* Kill ourself with SIGINT */ 888 /* Kill ourself with SIGINT */
688 signal(SIGINT, SIG_DFL); 889 signal(SIGINT, SIG_DFL);
689 raise(SIGINT); 890 raise(SIGINT);
891#else
892 exit(SIGINT << 24);
893#endif
690 } 894 }
895#if ENABLE_PLATFORM_MINGW32
896 if (iflag)
897 write(STDOUT_FILENO, "^C", 2);
898#endif
691 /* bash: ^C even on empty command line sets $? */ 899 /* bash: ^C even on empty command line sets $? */
692 exitstatus = SIGINT + 128; 900 exitstatus = SIGINT + 128;
693 raise_exception(EXINT); 901 raise_exception(EXINT);
@@ -1972,6 +2180,18 @@ maybe_single_quote(const char *s)
1972 return single_quote(s); 2180 return single_quote(s);
1973} 2181}
1974 2182
2183#if ENABLE_PLATFORM_MINGW32
2184/* Copy path to a string on the stack long enough to allow a file extension
2185 * to be added. */
2186static char *
2187stack_add_ext_space(const char *path)
2188{
2189 char *p = growstackto(strlen(path) + 5);
2190 strcpy(p, path);
2191 return p;
2192}
2193#endif
2194
1975 2195
1976/* ============ nextopt */ 2196/* ============ nextopt */
1977 2197
@@ -2123,6 +2343,17 @@ static void change_seconds(const char *) FAST_FUNC;
2123static void change_realtime(const char *) FAST_FUNC; 2343static void change_realtime(const char *) FAST_FUNC;
2124#endif 2344#endif
2125 2345
2346#if ENABLE_PLATFORM_MINGW32
2347static void FAST_FUNC
2348change_terminal_mode(const char *newval UNUSED_PARAM)
2349{
2350 terminal_mode(TRUE);
2351}
2352
2353# define LINENO_INDEX (5 + 2 * ENABLE_ASH_MAIL + ENABLE_ASH_GETOPTS)
2354# define FUNCNAME_INDEX (LINENO_INDEX + 1)
2355#endif
2356
2126static const struct { 2357static const struct {
2127 int flags; 2358 int flags;
2128 const char *var_text; 2359 const char *var_text;
@@ -2160,6 +2391,10 @@ static const struct {
2160#if ENABLE_FEATURE_EDITING_SAVEHISTORY 2391#if ENABLE_FEATURE_EDITING_SAVEHISTORY
2161 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL }, 2392 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
2162#endif 2393#endif
2394#if ENABLE_PLATFORM_MINGW32
2395 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_SKIP_ANSI_EMULATION, change_terminal_mode },
2396 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_TERMINAL_MODE, change_terminal_mode },
2397#endif
2163}; 2398};
2164 2399
2165struct redirtab; 2400struct redirtab;
@@ -2405,6 +2640,65 @@ bltinlookup(const char *name)
2405 return lookupvar(name); 2640 return lookupvar(name);
2406} 2641}
2407 2642
2643#if ENABLE_PLATFORM_MINGW32
2644static char *
2645fix_pathvar(const char *path, int len)
2646{
2647 char *newpath = xstrdup(path);
2648 char *p;
2649 int modified = FALSE;
2650
2651 p = newpath + len;
2652 while (*p) {
2653 if (*p != ':' && *p != ';') {
2654 /* skip drive */
2655 if (isalpha(*p) && p[1] == ':')
2656 p += 2;
2657 /* skip through path component */
2658 for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2659 continue;
2660 }
2661 /* *p is ':', ';' or '\0' here */
2662 if (*p == ':') {
2663 *p++ = ';';
2664 modified = TRUE;
2665 }
2666 else if (*p == ';') {
2667 ++p;
2668 }
2669 }
2670
2671 if (!modified) {
2672 free(newpath);
2673 newpath = NULL;
2674 }
2675 return newpath;
2676}
2677
2678#define BB_VAR_EXACT 1 /* exact match for name */
2679#define BB_VAR_ASSIGN -1 /* matches name followed by '=' */
2680
2681/* Match variables that should be placed in the environment immediately
2682 * they're exported and removed immediately they're no longer exported */
2683static int
2684is_bb_var(const char *s)
2685{
2686 const char *p;
2687 int len;
2688
2689 for (p = bbvar; *p; p += len + 1) {
2690 len = strlen(p);
2691 if (strncmp(s, p, len) == 0) {
2692 if (s[len] == '\0')
2693 return BB_VAR_EXACT;
2694 else if (s[len] == '=')
2695 return BB_VAR_ASSIGN;
2696 }
2697 }
2698 return FALSE;
2699}
2700#endif
2701
2408/* 2702/*
2409 * Same as setvar except that the variable and value are passed in 2703 * Same as setvar except that the variable and value are passed in
2410 * the first argument as name=value. Since the first argument will 2704 * the first argument as name=value. Since the first argument will
@@ -2416,6 +2710,26 @@ static struct var *
2416setvareq(char *s, int flags) 2710setvareq(char *s, int flags)
2417{ 2711{
2418 struct var *vp, **vpp; 2712 struct var *vp, **vpp;
2713#if ENABLE_PLATFORM_MINGW32
2714 const char *paths = "PATH=\0""CDPATH=\0""MANPATH=\0";
2715 const char *p;
2716 int len;
2717
2718 for (p = paths; *p; p += len + 1) {
2719 len = strlen(p);
2720 if (strncmp(s, p, len) == 0) {
2721 char *newpath = fix_pathvar(s, len);
2722 if (newpath) {
2723 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2724 free(s);
2725 flags |= VNOSAVE;
2726 flags &= ~(VTEXTFIXED|VSTACK);
2727 s = newpath;
2728 }
2729 break;
2730 }
2731 }
2732#endif
2419 2733
2420 vpp = hashvar(s); 2734 vpp = hashvar(s);
2421 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2735 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
@@ -2441,6 +2755,11 @@ setvareq(char *s, int flags)
2441 if (!(vp->flags & (VTEXTFIXED|VSTACK))) 2755 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2442 free((char*)vp->var_text); 2756 free((char*)vp->var_text);
2443 2757
2758#if ENABLE_PLATFORM_MINGW32
2759 if ((flags & VUNSET) && (vp->flags & VEXPORT) &&
2760 is_bb_var(s) == BB_VAR_EXACT)
2761 unsetenv(s);
2762#endif
2444 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) { 2763 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2445 *vpp = vp->next; 2764 *vpp = vp->next;
2446 free(vp); 2765 free(vp);
@@ -2470,6 +2789,10 @@ setvareq(char *s, int flags)
2470 s = ckstrdup(s); 2789 s = ckstrdup(s);
2471 vp->var_text = s; 2790 vp->var_text = s;
2472 vp->flags = flags; 2791 vp->flags = flags;
2792#if ENABLE_PLATFORM_MINGW32
2793 if ((flags & VEXPORT) && is_bb_var(s) == BB_VAR_ASSIGN)
2794 putenv(s);
2795#endif
2473 2796
2474 out: 2797 out:
2475 return vp; 2798 return vp;
@@ -2634,7 +2957,7 @@ static const char *pathopt; /* set by padvance */
2634static int 2957static int
2635padvance_magic(const char **path, const char *name, int magic) 2958padvance_magic(const char **path, const char *name, int magic)
2636{ 2959{
2637 const char *term = "%:"; 2960 const char *term = "%"PATH_SEP_STR;
2638 const char *lpathopt; 2961 const char *lpathopt;
2639 const char *p; 2962 const char *p;
2640 char *q; 2963 char *q;
@@ -2651,14 +2974,14 @@ padvance_magic(const char **path, const char *name, int magic)
2651 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) { 2974 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2652 lpathopt = start + 1; 2975 lpathopt = start + 1;
2653 start = p; 2976 start = p;
2654 term = ":"; 2977 term = PATH_SEP_STR;
2655 } 2978 }
2656 2979
2657 len = strcspn(start, term); 2980 len = strcspn(start, term);
2658 p = start + len; 2981 p = start + len;
2659 2982
2660 if (*p == '%') { 2983 if (*p == '%') {
2661 size_t extra = strchrnul(p, ':') - p; 2984 size_t extra = strchrnul(p, PATH_SEP) - p;
2662 2985
2663 if (legal_pathopt(p + 1, term, magic)) 2986 if (legal_pathopt(p + 1, term, magic))
2664 lpathopt = p + 1; 2987 lpathopt = p + 1;
@@ -2669,14 +2992,18 @@ padvance_magic(const char **path, const char *name, int magic)
2669 } 2992 }
2670 2993
2671 pathopt = lpathopt; 2994 pathopt = lpathopt;
2672 *path = *p == ':' ? p + 1 : NULL; 2995 *path = *p == PATH_SEP ? p + 1 : NULL;
2673 2996
2674 /* "2" is for '/' and '\0' */ 2997 /* "2" is for '/' and '\0' */
2675 qlen = len + strlen(name) + 2; 2998 /* reserve space for suffix on WIN32 */
2999 qlen = len + strlen(name) + 2 IF_PLATFORM_MINGW32(+ 4);
2676 q = growstackto(qlen); 3000 q = growstackto(qlen);
2677 3001
2678 if (len) { 3002 if (len) {
2679 q = mempcpy(q, start, len); 3003 q = mempcpy(q, start, len);
3004#if ENABLE_PLATFORM_MINGW32
3005 if (q[-1] != '/' && q[-1] != '\\')
3006#endif
2680 *q++ = '/'; 3007 *q++ = '/';
2681 } 3008 }
2682 strcpy(q, name); 3009 strcpy(q, name);
@@ -2767,6 +3094,7 @@ setprompt_if(smallint do_set, int whichprompt)
2767 3094
2768#define CD_PHYSICAL 1 3095#define CD_PHYSICAL 1
2769#define CD_PRINT 2 3096#define CD_PRINT 2
3097#define CD_PRINT_ALL 4
2770 3098
2771static int 3099static int
2772cdopt(void) 3100cdopt(void)
@@ -2775,7 +3103,14 @@ cdopt(void)
2775 int i, j; 3103 int i, j;
2776 3104
2777 j = 'L'; 3105 j = 'L';
3106#if ENABLE_PLATFORM_MINGW32
3107 while ((i = nextopt("LPa")) != '\0') {
3108 if (i == 'a')
3109 flags |= CD_PRINT_ALL;
3110 else
3111#else
2778 while ((i = nextopt("LP")) != '\0') { 3112 while ((i = nextopt("LP")) != '\0') {
3113#endif
2779 if (i != j) { 3114 if (i != j) {
2780 flags ^= CD_PHYSICAL; 3115 flags ^= CD_PHYSICAL;
2781 j = i; 3116 j = i;
@@ -2792,6 +3127,129 @@ cdopt(void)
2792static const char * 3127static const char *
2793updatepwd(const char *dir) 3128updatepwd(const char *dir)
2794{ 3129{
3130#if ENABLE_PLATFORM_MINGW32
3131 /*
3132 * Due to Windows drive notion, getting pwd is a completely
3133 * different thing. Handle it in a separate routine
3134 */
3135
3136 char *new;
3137 char *p;
3138 char *cdcomppath;
3139 const char *lim;
3140 int len;
3141 char buffer[PATH_MAX];
3142 /*
3143 * There are five cases that make some kind of sense
3144 *
3145 * Absolute paths:
3146 * c:/path
3147 * //host/share
3148 *
3149 * Relative to current working directory of other drive:
3150 * c:path
3151 *
3152 * Relative to current root (drive/share):
3153 * /path
3154 *
3155 * Relative to current working directory of current root (drive/share):
3156 * path
3157 */
3158 enum {ABS_DRIVE, ABS_SHARE, REL_OTHER, REL_ROOT, REL_CWD} target;
3159
3160 /* skip multiple leading separators unless dir is a UNC path */
3161 if (is_dir_sep(*dir) && unc_root_len(dir) == 0) {
3162 while (is_dir_sep(dir[1]))
3163 ++dir;
3164 }
3165
3166 len = strlen(dir);
3167 if (len >= 2 && has_dos_drive_prefix(dir))
3168 target = len >= 3 && is_dir_sep(dir[2]) ? ABS_DRIVE : REL_OTHER;
3169 else if (unc_root_len(dir) != 0)
3170 target = ABS_SHARE;
3171 else if (is_dir_sep(*dir))
3172 target = REL_ROOT;
3173 else
3174 target = REL_CWD;
3175
3176 cdcomppath = sstrdup(dir);
3177 STARTSTACKSTR(new);
3178
3179 switch (target) {
3180 case REL_OTHER:
3181 /* c:path */
3182 if (get_drive_cwd(dir, buffer, PATH_MAX) == NULL)
3183 return 0;
3184 new = stack_putstr(buffer, new);
3185 len = 2;
3186 cdcomppath += len;
3187 dir += len;
3188 break;
3189 case REL_CWD:
3190 case REL_ROOT:
3191 /* path or /path */
3192 len = root_len(curdir);
3193 if (len == 0)
3194 return 0;
3195 new = target == REL_CWD ? stack_putstr(curdir, new) :
3196 stnputs(curdir, len, new);
3197 break;
3198 default:
3199 /* //host/share or c:/path */
3200 len = root_len(dir);
3201 if (len == 0)
3202 return 0;
3203 new = stnputs(dir, len, new);
3204 cdcomppath += len;
3205 dir += len;
3206 break;
3207 }
3208
3209 new = makestrspace(strlen(dir) + 2, new);
3210 lim = (char *)stackblock() + len + 1;
3211
3212 if (!is_dir_sep(*dir)) {
3213 if (!is_dir_sep(new[-1]))
3214 USTPUTC('/', new);
3215 if (new > lim && is_dir_sep(*lim))
3216 lim++;
3217 } else {
3218 USTPUTC('/', new);
3219 cdcomppath++;
3220 if (is_dir_sep(dir[1]) && !is_dir_sep(dir[2])) {
3221 USTPUTC('/', new);
3222 cdcomppath++;
3223 lim++;
3224 }
3225 }
3226 p = strtok(cdcomppath, "/\\");
3227 while (p) {
3228 switch (*p) {
3229 case '.':
3230 if (p[1] == '.' && p[2] == '\0') {
3231 while (new > lim) {
3232 STUNPUTC(new);
3233 if (is_dir_sep(new[-1]))
3234 break;
3235 }
3236 break;
3237 }
3238 if (p[1] == '\0')
3239 break;
3240 /* fall through */
3241 default:
3242 new = stack_putstr(p, new);
3243 USTPUTC('/', new);
3244 }
3245 p = strtok(NULL, "/\\");
3246 }
3247 if (new > lim)
3248 STUNPUTC(new);
3249 *new = 0;
3250 fix_path_case((char *)stackblock());
3251 return bs_to_slash((char *)stackblock());
3252#else
2795 char *new; 3253 char *new;
2796 char *p; 3254 char *p;
2797 char *cdcomppath; 3255 char *cdcomppath;
@@ -2845,6 +3303,7 @@ updatepwd(const char *dir)
2845 STUNPUTC(new); 3303 STUNPUTC(new);
2846 *new = 0; 3304 *new = 0;
2847 return stackblock(); 3305 return stackblock();
3306#endif
2848} 3307}
2849 3308
2850/* 3309/*
@@ -2940,7 +3399,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2940 } 3399 }
2941 if (!dest) 3400 if (!dest)
2942 dest = nullstr; 3401 dest = nullstr;
2943 if (*dest == '/') 3402 if (!is_relative_path(dest))
2944 goto step6; 3403 goto step6;
2945 if (*dest == '.') { 3404 if (*dest == '.') {
2946 c = dest[1]; 3405 c = dest[1];
@@ -2963,7 +3422,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2963 p = stalloc(len); 3422 p = stalloc(len);
2964 3423
2965 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 3424 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2966 if (c && c != ':') 3425 if (c && c != PATH_SEP)
2967 flags |= CD_PRINT; 3426 flags |= CD_PRINT;
2968 docd: 3427 docd:
2969 if (!docd(p, flags)) 3428 if (!docd(p, flags))
@@ -2985,6 +3444,26 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2985 return 0; 3444 return 0;
2986} 3445}
2987 3446
3447#if ENABLE_PLATFORM_MINGW32
3448static void
3449print_all_cwd(void)
3450{
3451 FILE *mnt;
3452 struct mntent *entry;
3453 char buffer[PATH_MAX];
3454
3455 mnt = setmntent(bb_path_mtab_file, "r");
3456 if (mnt) {
3457 while ((entry=getmntent(mnt)) != NULL) {
3458 entry->mnt_dir[2] = '\0';
3459 if (get_drive_cwd(entry->mnt_dir, buffer, PATH_MAX) != NULL)
3460 out1fmt("%s\n", buffer);
3461 }
3462 endmntent(mnt);
3463 }
3464}
3465#endif
3466
2988static int FAST_FUNC 3467static int FAST_FUNC
2989pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 3468pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2990{ 3469{
@@ -2992,6 +3471,12 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2992 const char *dir = curdir; 3471 const char *dir = curdir;
2993 3472
2994 flags = cdopt(); 3473 flags = cdopt();
3474#if ENABLE_PLATFORM_MINGW32
3475 if (flags & CD_PRINT_ALL) {
3476 print_all_cwd();
3477 return 0;
3478 }
3479#endif
2995 if (flags) { 3480 if (flags) {
2996 if (physdir == nullstr) 3481 if (physdir == nullstr)
2997 setpwd(dir, 0); 3482 setpwd(dir, 0);
@@ -3620,7 +4105,12 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3620struct procstat { 4105struct procstat {
3621 pid_t ps_pid; /* process id */ 4106 pid_t ps_pid; /* process id */
3622 int ps_status; /* last process status from wait() */ 4107 int ps_status; /* last process status from wait() */
4108#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3623 char *ps_cmd; /* text of command being run */ 4109 char *ps_cmd; /* text of command being run */
4110#endif
4111#if ENABLE_PLATFORM_MINGW32
4112 HANDLE ps_proc;
4113#endif
3624}; 4114};
3625 4115
3626struct job { 4116struct job {
@@ -3636,8 +4126,10 @@ struct job {
3636#define JOBDONE 2 /* all procs are completed */ 4126#define JOBDONE 2 /* all procs are completed */
3637 unsigned 4127 unsigned
3638 state: 8, 4128 state: 8,
3639#if JOBS 4129#if JOBS || ENABLE_PLATFORM_MINGW32
3640 sigint: 1, /* job was killed by SIGINT */ 4130 sigint: 1, /* job was killed by SIGINT */
4131#endif
4132#if JOBS
3641 jobctl: 1, /* job running under job control */ 4133 jobctl: 1, /* job running under job control */
3642#endif 4134#endif
3643 waited: 1, /* true if this entry has been waited for */ 4135 waited: 1, /* true if this entry has been waited for */
@@ -3647,17 +4139,23 @@ struct job {
3647}; 4139};
3648 4140
3649static struct job *makejob(/*union node *,*/ int); 4141static struct job *makejob(/*union node *,*/ int);
4142#if !ENABLE_PLATFORM_MINGW32
3650static int forkshell(struct job *, union node *, int); 4143static int forkshell(struct job *, union node *, int);
4144#endif
3651static int waitforjob(struct job *); 4145static int waitforjob(struct job *);
3652 4146
3653#if !JOBS 4147#if !JOBS && !JOBS_WIN32
3654enum { doing_jobctl = 0 }; 4148enum { doing_jobctl = 0 };
3655#define setjobctl(on) do {} while (0) 4149#define setjobctl(on) do {} while (0)
3656#else 4150#elif JOBS_WIN32
4151static smallint doing_jobctl; //references:8
4152#define setjobctl(on) do { if (rootshell) doing_jobctl = on; } while (0)
4153#else /* JOBS */
3657static smallint doing_jobctl; //references:8 4154static smallint doing_jobctl; //references:8
3658static void setjobctl(int); 4155static void setjobctl(int);
3659#endif 4156#endif
3660 4157
4158#if !ENABLE_PLATFORM_MINGW32
3661/* 4159/*
3662 * Ignore a signal. 4160 * Ignore a signal.
3663 */ 4161 */
@@ -3806,6 +4304,10 @@ setsignal(int signo)
3806 4304
3807 sigaction_set(signo, &act); 4305 sigaction_set(signo, &act);
3808} 4306}
4307#else
4308#define setsignal(s)
4309#define ignoresig(s)
4310#endif
3809 4311
3810/* mode flags for set_curjob */ 4312/* mode flags for set_curjob */
3811#define CUR_DELETE 2 4313#define CUR_DELETE 2
@@ -3939,7 +4441,7 @@ set_curjob(struct job *jp, unsigned mode)
3939 } 4441 }
3940} 4442}
3941 4443
3942#if JOBS || DEBUG 4444#if JOBS || ENABLE_PLATFORM_MINGW32 || DEBUG
3943static int 4445static int
3944jobno(const struct job *jp) 4446jobno(const struct job *jp)
3945{ 4447{
@@ -3957,7 +4459,9 @@ static struct job *
3957getjob(const char *name, int getctl) 4459getjob(const char *name, int getctl)
3958{ 4460{
3959 struct job *jp; 4461 struct job *jp;
4462#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3960 struct job *found; 4463 struct job *found;
4464#endif
3961 const char *err_msg = "%s: no such job"; 4465 const char *err_msg = "%s: no such job";
3962 unsigned num; 4466 unsigned num;
3963 int c; 4467 int c;
@@ -4002,6 +4506,7 @@ getjob(const char *name, int getctl)
4002 } 4506 }
4003 } 4507 }
4004 4508
4509#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4005 found = NULL; 4510 found = NULL;
4006 while (jp) { 4511 while (jp) {
4007 if (*p == '?' 4512 if (*p == '?'
@@ -4018,6 +4523,9 @@ getjob(const char *name, int getctl)
4018 if (!found) 4523 if (!found)
4019 goto err; 4524 goto err;
4020 jp = found; 4525 jp = found;
4526#else
4527 goto err;
4528#endif
4021 4529
4022 gotit: 4530 gotit:
4023#if JOBS 4531#if JOBS
@@ -4036,14 +4544,18 @@ getjob(const char *name, int getctl)
4036static void 4544static void
4037freejob(struct job *jp) 4545freejob(struct job *jp)
4038{ 4546{
4547#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4039 struct procstat *ps; 4548 struct procstat *ps;
4040 int i; 4549 int i;
4550#endif
4041 4551
4042 INT_OFF; 4552 INT_OFF;
4553#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4043 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { 4554 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
4044 if (ps->ps_cmd != nullstr) 4555 if (ps->ps_cmd != nullstr)
4045 free(ps->ps_cmd); 4556 free(ps->ps_cmd);
4046 } 4557 }
4558#endif
4047 if (jp->ps != &jp->ps0) 4559 if (jp->ps != &jp->ps0)
4048 free(jp->ps); 4560 free(jp->ps);
4049 jp->used = 0; 4561 jp->used = 0;
@@ -4136,7 +4648,9 @@ setjobctl(int on)
4136 ttyfd = fd; 4648 ttyfd = fd;
4137 doing_jobctl = on; 4649 doing_jobctl = on;
4138} 4650}
4651#endif
4139 4652
4653#if JOBS || JOBS_WIN32
4140static int FAST_FUNC 4654static int FAST_FUNC
4141killcmd(int argc, char **argv) 4655killcmd(int argc, char **argv)
4142{ 4656{
@@ -4166,8 +4680,10 @@ killcmd(int argc, char **argv)
4166 * sh -c 'true|sleep 1 & sleep 2; kill %1' 4680 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4167 */ 4681 */
4168 n = jp->nprocs; /* can't be 0 (I hope) */ 4682 n = jp->nprocs; /* can't be 0 (I hope) */
4683#if !ENABLE_PLATFORM_MINGW32
4169 if (jp->jobctl) 4684 if (jp->jobctl)
4170 n = 1; 4685 n = 1;
4686#endif
4171 dst = alloca(n * sizeof(int)*4); 4687 dst = alloca(n * sizeof(int)*4);
4172 argv[i] = dst; 4688 argv[i] = dst;
4173 for (j = 0; j < n; j++) { 4689 for (j = 0; j < n; j++) {
@@ -4182,7 +4698,11 @@ killcmd(int argc, char **argv)
4182 * leading space. Needed to not confuse 4698 * leading space. Needed to not confuse
4183 * negative pids with "kill -SIGNAL_NO" syntax 4699 * negative pids with "kill -SIGNAL_NO" syntax
4184 */ 4700 */
4701#if !ENABLE_PLATFORM_MINGW32
4185 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); 4702 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4703#else
4704 dst += sprintf(dst, " -%u", (int)ps->ps_pid);
4705#endif
4186 } 4706 }
4187 *dst = '\0'; 4707 *dst = '\0';
4188 } 4708 }
@@ -4190,7 +4710,9 @@ killcmd(int argc, char **argv)
4190 } 4710 }
4191 return kill_main(argc, argv); 4711 return kill_main(argc, argv);
4192} 4712}
4713#endif
4193 4714
4715#if JOBS
4194static void 4716static void
4195showpipe(struct job *jp /*, FILE *out*/) 4717showpipe(struct job *jp /*, FILE *out*/)
4196{ 4718{
@@ -4295,6 +4817,89 @@ sprint_status48(char *os, int status, int sigonly)
4295 return s - os; 4817 return s - os;
4296} 4818}
4297 4819
4820#if ENABLE_PLATFORM_MINGW32
4821static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
4822{
4823 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
4824# if ENABLE_FEATURE_EDITING
4825 bb_got_signal = SIGINT; /* for read_line_input: "we got a signal" */
4826# endif
4827 if (!suppress_int && !(rootshell && iflag))
4828 raise_interrupt();
4829 pending_int = 1;
4830 return TRUE;
4831 }
4832 return FALSE;
4833}
4834
4835/*
4836 * Windows does not know about parent-child relationship
4837 * They don't support waitpid(-1)
4838 */
4839static pid_t
4840waitpid_child(int *status, int wait_flags)
4841{
4842 struct job *jb;
4843 struct procstat *ps;
4844 int pid_nr = 0;
4845 pid_t *pidlist;
4846 HANDLE *proclist;
4847 pid_t pid = -1;
4848 DWORD win_status, idx;
4849 int i, sig;
4850
4851 for (jb = curjob; jb; jb = jb->prev_job) {
4852 if (jb->state != JOBDONE)
4853 pid_nr += jb->nprocs;
4854 }
4855 if (pid_nr == 0)
4856 return -1;
4857
4858 pidlist = ckmalloc(sizeof(*pidlist)*pid_nr);
4859 proclist = ckmalloc(sizeof(*proclist)*pid_nr);
4860
4861 pid_nr = 0;
4862 for (jb = curjob; jb; jb = jb->prev_job) {
4863 if (jb->state == JOBDONE)
4864 continue;
4865 ps = jb->ps;
4866 for (i = 0; i < jb->nprocs; ++i) {
4867 if (ps[i].ps_proc) {
4868 pidlist[pid_nr] = ps[i].ps_pid;
4869 proclist[pid_nr++] = ps[i].ps_proc;
4870 }
4871 }
4872 }
4873
4874 if (pid_nr == 0)
4875 goto done;
4876
4877 idx = WaitForMultipleObjects(pid_nr, proclist, FALSE,
4878 wait_flags&WNOHANG ? 1 : INFINITE);
4879 if (idx < pid_nr) {
4880 GetExitCodeProcess(proclist[idx], &win_status);
4881 if (win_status == 0xc0000005)
4882 win_status = SIGSEGV << 24;
4883 else if (win_status == 0xc000013a)
4884 win_status = SIGINT << 24;
4885
4886 // When a process is terminated as if by a signal the exit
4887 // code is zero apart from the signal in its topmost byte.
4888 sig = win_status >> 24;
4889 if (sig != 0 && win_status == sig << 24 && is_valid_signal(sig))
4890 *status = sig;
4891 else
4892 *status = (int)win_status << 8;
4893 pid = pidlist[idx];
4894 }
4895 done:
4896 free(pidlist);
4897 free(proclist);
4898 return pid;
4899}
4900#define waitpid(p, s, f) waitpid_child(s, f)
4901#endif
4902
4298#define DOWAIT_NONBLOCK 0 4903#define DOWAIT_NONBLOCK 0
4299#define DOWAIT_BLOCK 1 4904#define DOWAIT_BLOCK 1
4300#define DOWAIT_BLOCK_OR_SIG 2 4905#define DOWAIT_BLOCK_OR_SIG 2
@@ -4305,6 +4910,7 @@ sprint_status48(char *os, int status, int sigonly)
4305static int 4910static int
4306waitproc(int block, int *status) 4911waitproc(int block, int *status)
4307{ 4912{
4913#if !ENABLE_PLATFORM_MINGW32
4308 sigset_t oldmask; 4914 sigset_t oldmask;
4309 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; 4915 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4310 int err; 4916 int err;
@@ -4335,6 +4941,11 @@ waitproc(int block, int *status)
4335 } while (got_sigchld); 4941 } while (got_sigchld);
4336 4942
4337 return err; 4943 return err;
4944#else
4945 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4946 *status = 0;
4947 return waitpid(-1, status, flags);
4948#endif
4338} 4949}
4339 4950
4340static int 4951static int
@@ -4391,6 +5002,11 @@ waitone(int block, struct job *job)
4391 jobno(jp), pid, ps->ps_status, status)); 5002 jobno(jp), pid, ps->ps_status, status));
4392 ps->ps_status = status; 5003 ps->ps_status = status;
4393 thisjob = jp; 5004 thisjob = jp;
5005#if ENABLE_PLATFORM_MINGW32
5006 ps->ps_pid = -1;
5007 CloseHandle(ps->ps_proc);
5008 ps->ps_proc = NULL;
5009#endif
4394 } 5010 }
4395 if (ps->ps_status == -1) 5011 if (ps->ps_status == -1)
4396 jobstate = JOBRUNNING; 5012 jobstate = JOBRUNNING;
@@ -4453,6 +5069,7 @@ waitone(int block, struct job *job)
4453static int 5069static int
4454dowait(int block, struct job *jp) 5070dowait(int block, struct job *jp)
4455{ 5071{
5072#if !ENABLE_PLATFORM_MINGW32
4456 smallint gotchld = *(volatile smallint *)&got_sigchld; 5073 smallint gotchld = *(volatile smallint *)&got_sigchld;
4457 int rpid; 5074 int rpid;
4458 int pid; 5075 int pid;
@@ -4474,9 +5091,17 @@ dowait(int block, struct job *jp)
4474 } while (pid >= 0); 5091 } while (pid >= 0);
4475 5092
4476 return rpid; 5093 return rpid;
5094#else
5095 int pid = 1;
5096
5097 while (jp ? jp->state == JOBRUNNING : pid > 0)
5098 pid = waitone(block, jp);
5099
5100 return pid;
5101#endif
4477} 5102}
4478 5103
4479#if JOBS 5104#if JOBS || JOBS_WIN32
4480static void 5105static void
4481showjob(struct job *jp, int mode) 5106showjob(struct job *jp, int mode)
4482{ 5107{
@@ -4491,7 +5116,7 @@ showjob(struct job *jp, int mode)
4491 5116
4492 if (mode & SHOW_ONLY_PGID) { /* jobs -p */ 5117 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
4493 /* just output process (group) id of pipeline */ 5118 /* just output process (group) id of pipeline */
4494 fprintf(out, "%d\n", ps->ps_pid); 5119 fprintf(out, "%"PID_FMT"d\n", ps->ps_pid);
4495 return; 5120 return;
4496 } 5121 }
4497 5122
@@ -4504,7 +5129,7 @@ showjob(struct job *jp, int mode)
4504 s[col - 3] = '-'; 5129 s[col - 3] = '-';
4505 5130
4506 if (mode & SHOW_PIDS) 5131 if (mode & SHOW_PIDS)
4507 col += fmtstr(s + col, 16, "%d ", ps->ps_pid); 5132 col += fmtstr(s + col, 16, "%"PID_FMT"d ", ps->ps_pid);
4508 5133
4509 psend = ps + jp->nprocs; 5134 psend = ps + jp->nprocs;
4510 5135
@@ -4513,8 +5138,10 @@ showjob(struct job *jp, int mode)
4513 col += sizeof("Running") - 1; 5138 col += sizeof("Running") - 1;
4514 } else { 5139 } else {
4515 int status = psend[-1].ps_status; 5140 int status = psend[-1].ps_status;
5141#if !ENABLE_PLATFORM_MINGW32
4516 if (jp->state == JOBSTOPPED) 5142 if (jp->state == JOBSTOPPED)
4517 status = jp->stopstatus; 5143 status = jp->stopstatus;
5144#endif
4518 col += sprint_status48(s + col, status, 0); 5145 col += sprint_status48(s + col, status, 0);
4519 } 5146 }
4520 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ 5147 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
@@ -4533,14 +5160,18 @@ showjob(struct job *jp, int mode)
4533 s[0] = '\0'; 5160 s[0] = '\0';
4534 col = 33; 5161 col = 33;
4535 if (mode & SHOW_PIDS) 5162 if (mode & SHOW_PIDS)
4536 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; 5163 col = fmtstr(s, 48, "\n%*c%"PID_FMT"d ", indent_col, ' ', ps->ps_pid) - 1;
4537 start: 5164 start:
5165#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4538 fprintf(out, "%s%*c%s%s", 5166 fprintf(out, "%s%*c%s%s",
4539 s, 5167 s,
4540 33 - col >= 0 ? 33 - col : 0, ' ', 5168 33 - col >= 0 ? 33 - col : 0, ' ',
4541 ps == jp->ps ? "" : "| ", 5169 ps == jp->ps ? "" : "| ",
4542 ps->ps_cmd 5170 ps->ps_cmd
4543 ); 5171 );
5172#else
5173 fprintf(out, "%s", s);
5174#endif
4544 } while (++ps != psend); 5175 } while (++ps != psend);
4545 newline_and_flush(out); 5176 newline_and_flush(out);
4546 5177
@@ -4625,7 +5256,7 @@ getstatus(struct job *job)
4625 { 5256 {
4626 /* XXX: limits number of signals */ 5257 /* XXX: limits number of signals */
4627 retval = WTERMSIG(status); 5258 retval = WTERMSIG(status);
4628#if JOBS 5259#if JOBS || ENABLE_PLATFORM_MINGW32
4629 if (retval == SIGINT) 5260 if (retval == SIGINT)
4630 job->sigint = 1; 5261 job->sigint = 1;
4631#endif 5262#endif
@@ -4797,7 +5428,7 @@ makejob(/*union node *node,*/ int nprocs)
4797 break; 5428 break;
4798 if (jp->state != JOBDONE || !jp->waited) 5429 if (jp->state != JOBDONE || !jp->waited)
4799 continue; 5430 continue;
4800#if JOBS 5431#if JOBS || JOBS_WIN32
4801 if (doing_jobctl) 5432 if (doing_jobctl)
4802 continue; 5433 continue;
4803#endif 5434#endif
@@ -4823,7 +5454,7 @@ makejob(/*union node *node,*/ int nprocs)
4823 return jp; 5454 return jp;
4824} 5455}
4825 5456
4826#if JOBS 5457#if JOBS || JOBS_WIN32
4827/* 5458/*
4828 * Return a string identifying a command (to be printed by the 5459 * Return a string identifying a command (to be printed by the
4829 * jobs command). 5460 * jobs command).
@@ -5142,6 +5773,7 @@ commandtext(union node *n)
5142 * 5773 *
5143 * Called with interrupts off. 5774 * Called with interrupts off.
5144 */ 5775 */
5776#if !ENABLE_PLATFORM_MINGW32
5145/* 5777/*
5146 * Clear traps on a fork. 5778 * Clear traps on a fork.
5147 */ 5779 */
@@ -5291,14 +5923,22 @@ forkchild(struct job *jp, union node *n, int mode)
5291 for (jp = curjob; jp; jp = jp->prev_job) 5923 for (jp = curjob; jp; jp = jp->prev_job)
5292 freejob(jp); 5924 freejob(jp);
5293} 5925}
5926#endif
5294 5927
5295/* Called after fork(), in parent */ 5928/* Called after fork(), in parent */
5296#if !JOBS 5929#if !JOBS && !JOBS_WIN32
5297#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) 5930#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5298#endif 5931#endif
5299static void 5932static void
5933#if !ENABLE_PLATFORM_MINGW32
5300forkparent(struct job *jp, union node *n, int mode, pid_t pid) 5934forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5935#else
5936forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
5937#endif
5301{ 5938{
5939#if ENABLE_PLATFORM_MINGW32
5940 pid_t pid = GetProcessId(proc);
5941#endif
5302 TRACE(("In parent shell: child = %d\n", pid)); 5942 TRACE(("In parent shell: child = %d\n", pid));
5303 if (!jp) /* jp is NULL when called by openhere() for heredoc support */ 5943 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
5304 return; 5944 return;
@@ -5317,19 +5957,29 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5317 if (mode == FORK_BG) { 5957 if (mode == FORK_BG) {
5318 backgndpid = pid; /* set $! */ 5958 backgndpid = pid; /* set $! */
5319 set_curjob(jp, CUR_RUNNING); 5959 set_curjob(jp, CUR_RUNNING);
5960#if ENABLE_PLATFORM_MINGW32
5961 if (iflag && jp && jp->nprocs == 0)
5962 fprintf(stderr, "[%d] %"PID_FMT"d\n", jobno(jp), pid);
5963#endif
5320 } 5964 }
5321 if (jp) { 5965 if (jp) {
5322 struct procstat *ps = &jp->ps[jp->nprocs++]; 5966 struct procstat *ps = &jp->ps[jp->nprocs++];
5323 ps->ps_pid = pid; 5967 ps->ps_pid = pid;
5324 ps->ps_status = -1; 5968 ps->ps_status = -1;
5969#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
5325 ps->ps_cmd = nullstr; 5970 ps->ps_cmd = nullstr;
5326#if JOBS 5971#endif
5972#if ENABLE_PLATFORM_MINGW32
5973 ps->ps_proc = proc;
5974#endif
5975#if JOBS || JOBS_WIN32
5327 if (doing_jobctl && n) 5976 if (doing_jobctl && n)
5328 ps->ps_cmd = commandtext(n); 5977 ps->ps_cmd = commandtext(n);
5329#endif 5978#endif
5330 } 5979 }
5331} 5980}
5332 5981
5982#if !ENABLE_PLATFORM_MINGW32
5333/* jp and n are NULL when called by openhere() for heredoc support */ 5983/* jp and n are NULL when called by openhere() for heredoc support */
5334static int 5984static int
5335forkshell(struct job *jp, union node *n, int mode) 5985forkshell(struct job *jp, union node *n, int mode)
@@ -5352,6 +6002,7 @@ forkshell(struct job *jp, union node *n, int mode)
5352 } 6002 }
5353 return pid; 6003 return pid;
5354} 6004}
6005#endif
5355 6006
5356/* 6007/*
5357 * Wait for job to finish. 6008 * Wait for job to finish.
@@ -5415,6 +6066,10 @@ waitforjob(struct job *jp)
5415 return exitstatus; 6066 return exitstatus;
5416 6067
5417 st = getstatus(jp); 6068 st = getstatus(jp);
6069#if ENABLE_PLATFORM_MINGW32
6070 if (!jp->sigint && iflag && rootshell)
6071 pending_int = 0;
6072#endif
5418#if JOBS 6073#if JOBS
5419 if (jp->jobctl) { 6074 if (jp->jobctl) {
5420 xtcsetpgrp(ttyfd, rootpid); 6075 xtcsetpgrp(ttyfd, rootpid);
@@ -5440,6 +6095,7 @@ waitforjob(struct job *jp)
5440/* 6095/*
5441 * return 1 if there are stopped jobs, otherwise 0 6096 * return 1 if there are stopped jobs, otherwise 0
5442 */ 6097 */
6098#if !ENABLE_PLATFORM_MINGW32
5443static int 6099static int
5444stoppedjobs(void) 6100stoppedjobs(void)
5445{ 6101{
@@ -5458,6 +6114,17 @@ stoppedjobs(void)
5458 out: 6114 out:
5459 return retval; 6115 return retval;
5460} 6116}
6117#else
6118static int
6119stoppedjobs(void)
6120{
6121 if (iflag && curjob) {
6122 out2str("You have background jobs.\n");
6123 return 1;
6124 }
6125 return 0;
6126}
6127#endif
5461 6128
5462 6129
5463/* 6130/*
@@ -5482,6 +6149,7 @@ openhere(union node *redir)
5482 char *p; 6149 char *p;
5483 int pip[2]; 6150 int pip[2];
5484 size_t len = 0; 6151 size_t len = 0;
6152 IF_PLATFORM_MINGW32(struct forkshell fs);
5485 6153
5486 if (pipe(pip) < 0) 6154 if (pipe(pip) < 0)
5487 ash_msg_and_raise_perror("can't create pipe"); 6155 ash_msg_and_raise_perror("can't create pipe");
@@ -5498,6 +6166,14 @@ openhere(union node *redir)
5498 goto out; 6166 goto out;
5499 } 6167 }
5500 6168
6169#if ENABLE_PLATFORM_MINGW32
6170 memset(&fs, 0, sizeof(fs));
6171 fs.fpid = FS_OPENHERE;
6172 fs.fd[0] = pip[0];
6173 fs.fd[1] = pip[1];
6174 fs.path = p;
6175 spawn_forkshell(&fs, NULL, NULL, FORK_NOJOB);
6176#else
5501 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { 6177 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
5502 /* child */ 6178 /* child */
5503 close(pip[0]); 6179 close(pip[0]);
@@ -5509,6 +6185,7 @@ openhere(union node *redir)
5509 xwrite(pip[1], p, len); 6185 xwrite(pip[1], p, len);
5510 _exit_SUCCESS(); 6186 _exit_SUCCESS();
5511 } 6187 }
6188#endif
5512 out: 6189 out:
5513 close(pip[1]); 6190 close(pip[1]);
5514 return pip[0]; 6191 return pip[0];
@@ -5586,6 +6263,9 @@ openredirect(union node *redir)
5586 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); 6263 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5587 if (f < 0) 6264 if (f < 0)
5588 goto ecreate; 6265 goto ecreate;
6266#if ENABLE_PLATFORM_MINGW32
6267 lseek(f, 0, SEEK_END);
6268#endif
5589 break; 6269 break;
5590 } 6270 }
5591 6271
@@ -6137,6 +6817,9 @@ ifsbreakup(char *string, struct arglist *arglist)
6137 const char *ifs, *realifs; 6817 const char *ifs, *realifs;
6138 int ifsspc; 6818 int ifsspc;
6139 int nulonly; 6819 int nulonly;
6820#if ENABLE_PLATFORM_MINGW32
6821 int lshift = 0;
6822#endif
6140 6823
6141 start = string; 6824 start = string;
6142 if (ifslastp != NULL) { 6825 if (ifslastp != NULL) {
@@ -6147,7 +6830,33 @@ ifsbreakup(char *string, struct arglist *arglist)
6147 do { 6830 do {
6148 int afternul; 6831 int afternul;
6149 6832
6833#if ENABLE_PLATFORM_MINGW32
6834 /* Adjust region offsets for left-shifted string. */
6835 ifsp->begoff -= lshift;
6836 ifsp->endoff -= lshift;
6837#endif
6150 p = string + ifsp->begoff; 6838 p = string + ifsp->begoff;
6839#if ENABLE_PLATFORM_MINGW32
6840 if (ifsp->endoff > ifsp->begoff + 1) {
6841 /* Transform CRLF to LF. Skip regions having zero or
6842 * one characters: they can't contain CRLF. If the
6843 * region shrinks shift the rest of the string left. */
6844 int oldlen = ifsp->endoff - ifsp->begoff;
6845 int newlen = remove_cr(p, oldlen);
6846 int delta = oldlen - newlen;
6847
6848 if (delta > 0) {
6849 char *t = string + ifsp->endoff;
6850 char *s = string + ifsp->endoff - delta;
6851
6852 while (*t)
6853 *s++ = *t++;
6854 *s = '\0';
6855 lshift += delta;
6856 ifsp->endoff -= delta;
6857 }
6858 }
6859#endif
6151 afternul = nulonly; 6860 afternul = nulonly;
6152 nulonly = ifsp->nulonly; 6861 nulonly = ifsp->nulonly;
6153 ifs = nulonly ? nullstr : realifs; 6862 ifs = nulonly ? nullstr : realifs;
@@ -6592,6 +7301,7 @@ evalbackcmd(union node *n, struct backcmd *result
6592 const int ip = 0; 7301 const int ip = 0;
6593 const int ic = 1; 7302 const int ic = 1;
6594#endif 7303#endif
7304 IF_PLATFORM_MINGW32(struct forkshell fs);
6595 7305
6596 result->fd = -1; 7306 result->fd = -1;
6597 result->buf = NULL; 7307 result->buf = NULL;
@@ -6605,6 +7315,15 @@ evalbackcmd(union node *n, struct backcmd *result
6605 ash_msg_and_raise_perror("can't create pipe"); 7315 ash_msg_and_raise_perror("can't create pipe");
6606 /* process substitution uses NULL job, like openhere() */ 7316 /* process substitution uses NULL job, like openhere() */
6607 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; 7317 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;
7318#if ENABLE_PLATFORM_MINGW32
7319 memset(&fs, 0, sizeof(fs));
7320 fs.fpid = FS_EVALBACKCMD;
7321 fs.n = n;
7322 fs.fd[0] = pip[0];
7323 fs.fd[1] = pip[1];
7324 fs.fd[2] = ctl;
7325 spawn_forkshell(&fs, jp, n, FORK_NOJOB);
7326#else
6608 if (forkshell(jp, n, FORK_NOJOB) == 0) { 7327 if (forkshell(jp, n, FORK_NOJOB) == 0) {
6609 /* child */ 7328 /* child */
6610 FORCE_INT_ON; 7329 FORCE_INT_ON;
@@ -6628,6 +7347,7 @@ evalbackcmd(union node *n, struct backcmd *result
6628 evaltreenr(n, EV_EXIT); 7347 evaltreenr(n, EV_EXIT);
6629 /* NOTREACHED */ 7348 /* NOTREACHED */
6630 } 7349 }
7350#endif
6631 /* parent */ 7351 /* parent */
6632#if BASH_PROCESS_SUBST 7352#if BASH_PROCESS_SUBST
6633 if (ctl != CTLBACKQ) { 7353 if (ctl != CTLBACKQ) {
@@ -6706,8 +7426,14 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
6706 7426
6707 /* Eat all trailing newlines */ 7427 /* Eat all trailing newlines */
6708 dest = expdest; 7428 dest = expdest;
6709 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) 7429 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) {
6710 STUNPUTC(dest); 7430 STUNPUTC(dest);
7431#if ENABLE_PLATFORM_MINGW32
7432 if (dest > ((char *)stackblock() + startloc) && dest[-1] == '\r') {
7433 STUNPUTC(dest);
7434 }
7435#endif
7436 }
6711 expdest = dest; 7437 expdest = dest;
6712 7438
6713 if (!(flag & EXP_QUOTED)) 7439 if (!(flag & EXP_QUOTED))
@@ -7876,6 +8602,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7876 8602
7877 metaflag = 0; 8603 metaflag = 0;
7878 start = name; 8604 start = name;
8605#if ENABLE_PLATFORM_MINGW32
8606 if (expdir_len == 0 && has_dos_drive_prefix(start) && start[2] != '/')
8607 start += 2;
8608#endif
7879 for (p = name; esc = 0, *p; p += esc + 1) { 8609 for (p = name; esc = 0, *p; p += esc + 1) {
7880 if (*p == '*' || *p == '?') 8610 if (*p == '*' || *p == '?')
7881 metaflag = 1; 8611 metaflag = 1;
@@ -7950,6 +8680,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7950 while (!pending_int && (dp = readdir(dirp)) != NULL) { 8680 while (!pending_int && (dp = readdir(dirp)) != NULL) {
7951 if (dp->d_name[0] == '.' && !matchdot) 8681 if (dp->d_name[0] == '.' && !matchdot)
7952 continue; 8682 continue;
8683#if ENABLE_ASH_NOCASEGLOB
8684# undef pmatch
8685# define pmatch(a, b) !fnmatch((a), (b), nocaseglob ? FNM_CASEFOLD : 0)
8686#endif
7953 if (pmatch(start, dp->d_name)) { 8687 if (pmatch(start, dp->d_name)) {
7954 if (atend) { 8688 if (atend) {
7955 strcpy(enddir, dp->d_name); 8689 strcpy(enddir, dp->d_name);
@@ -7979,6 +8713,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7979 endname[-esc - 1] = esc ? '\\' : '/'; 8713 endname[-esc - 1] = esc ? '\\' : '/';
7980#undef expdir 8714#undef expdir
7981#undef expdir_max 8715#undef expdir_max
8716#if ENABLE_ASH_NOCASEGLOB
8717# undef pmatch
8718# define pmatch(a, b) !fnmatch((a), (b), 0)
8719#endif
7982} 8720}
7983 8721
7984static struct strlist * 8722static struct strlist *
@@ -8255,10 +8993,34 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8255{ 8993{
8256#if ENABLE_FEATURE_SH_STANDALONE 8994#if ENABLE_FEATURE_SH_STANDALONE
8257 if (applet_no >= 0) { 8995 if (applet_no >= 0) {
8996# if ENABLE_PLATFORM_MINGW32
8997 /* Treat all applets as NOEXEC, including the shell itself if
8998 * this is a FS_SHELLEXEC shell. */
8999 struct forkshell *fs = (struct forkshell *)sticky_mem_start;
9000 if (applet_main[applet_no] != ash_main ||
9001 (fs && fs->fpid == FS_SHELLEXEC)) {
9002 /* mingw-w64's getopt() uses __argv[0] as the program name */
9003 __argv[0] = (char *)cmd;
9004 /* 'which' wants to know if it was invoked from a standalone
9005 * shell. Use the spare element of argv to add a flag, but
9006 * not if the first argument is '--help', that's a special
9007 * case. */
9008 if (strcmp(argv[0], "which") == 0 &&
9009 (argv[1] == NULL || strcmp(argv[1], "--help") != 0)) {
9010 --argv;
9011 argv[0] = argv[1];
9012 argv[1] = (char *)"-s";
9013 }
9014# else
8258 if (APPLET_IS_NOEXEC(applet_no)) { 9015 if (APPLET_IS_NOEXEC(applet_no)) {
9016# endif
9017#if ENABLE_PLATFORM_MINGW32 && !defined(_UCRT)
9018 /* If building for UCRT move this up into shellexec() to
9019 * work around a bug. */
8259 clearenv(); 9020 clearenv();
8260 while (*envp) 9021 while (*envp)
8261 putenv(*envp++); 9022 putenv(*envp++);
9023#endif
8262 popredir(/*drop:*/ 1); 9024 popredir(/*drop:*/ 1);
8263 run_noexec_applet_and_exit(applet_no, cmd, argv); 9025 run_noexec_applet_and_exit(applet_no, cmd, argv);
8264 } 9026 }
@@ -8269,6 +9031,12 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8269 } 9031 }
8270#endif 9032#endif
8271 9033
9034#if ENABLE_PLATFORM_MINGW32
9035 /* cmd was allocated on the stack with room for an extension */
9036 add_win32_extension((char *)cmd);
9037 execve(cmd, argv, envp);
9038 /* skip POSIX-mandated retry on ENOEXEC */
9039#else /* !ENABLE_PLATFORM_MINGW32 */
8272 repeat: 9040 repeat:
8273#ifdef SYSV 9041#ifdef SYSV
8274 do { 9042 do {
@@ -8304,6 +9072,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8304 argv[0] = (char*) "ash"; 9072 argv[0] = (char*) "ash";
8305 goto repeat; 9073 goto repeat;
8306 } 9074 }
9075#endif /* !ENABLE_PLATFORM_MINGW32 */
8307} 9076}
8308 9077
8309/* 9078/*
@@ -8311,6 +9080,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8311 * have to change the find_command routine as well. 9080 * have to change the find_command routine as well.
8312 * argv[-1] must exist and be writable! See tryexec() for why. 9081 * argv[-1] must exist and be writable! See tryexec() for why.
8313 */ 9082 */
9083static struct builtincmd *find_builtin(const char *name);
8314static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; 9084static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
8315static void shellexec(char *prog, char **argv, const char *path, int idx) 9085static void shellexec(char *prog, char **argv, const char *path, int idx)
8316{ 9086{
@@ -8321,12 +9091,29 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8321 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ 9091 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
8322 9092
8323 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 9093 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
9094#if ENABLE_PLATFORM_MINGW32 && defined(_UCRT)
9095 /* Avoid UCRT bug by updating parent's environment and passing a
9096 * NULL environment pointer to execve(). */
9097 clearenv();
9098 while (*envp)
9099 putenv(*envp++);
9100 envp = NULL;
9101#endif
9102#if !ENABLE_PLATFORM_MINGW32
8324 if (strchr(prog, '/') != NULL 9103 if (strchr(prog, '/') != NULL
9104#else
9105 if (has_path(prog)
9106#endif
8325#if ENABLE_FEATURE_SH_STANDALONE 9107#if ENABLE_FEATURE_SH_STANDALONE
8326 || (applet_no = find_applet_by_name(prog)) >= 0 9108 || (applet_no = find_applet_by_name(prog)) >= 0
8327#endif 9109#endif
8328 ) { 9110 ) {
9111#if ENABLE_PLATFORM_MINGW32
9112 char *progext = stack_add_ext_space(prog);
9113 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) progext, argv, envp);
9114#else
8329 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp); 9115 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
9116#endif
8330 if (applet_no >= 0) { 9117 if (applet_no >= 0) {
8331 /* We tried execing ourself, but it didn't work. 9118 /* We tried execing ourself, but it didn't work.
8332 * Maybe /proc/self/exe doesn't exist? 9119 * Maybe /proc/self/exe doesn't exist?
@@ -8335,6 +9122,21 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8335 goto try_PATH; 9122 goto try_PATH;
8336 } 9123 }
8337 e = errno; 9124 e = errno;
9125#if ENABLE_PLATFORM_MINGW32
9126 if (unix_path(prog)) {
9127 const char *name = bb_basename(prog);
9128# if ENABLE_FEATURE_SH_STANDALONE
9129 if ((applet_no = find_applet_by_name(name)) >= 0) {
9130 tryexec(applet_no, name, argv, envp);
9131 e = errno;
9132 }
9133# endif
9134 if (!find_builtin(name)) {
9135 argv[0] = (char *)name;
9136 goto try_PATH;
9137 }
9138 }
9139#endif
8338 } else { 9140 } else {
8339 try_PATH: 9141 try_PATH:
8340 e = ENOENT; 9142 e = ENOENT;
@@ -8380,6 +9182,9 @@ printentry(struct tblentry *cmdp)
8380 padvance(&path, cmdp->cmdname); 9182 padvance(&path, cmdp->cmdname);
8381 } while (--idx >= 0); 9183 } while (--idx >= 0);
8382 name = stackblock(); 9184 name = stackblock();
9185#if ENABLE_PLATFORM_MINGW32
9186 add_win32_extension(name);
9187#endif
8383 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); 9188 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8384} 9189}
8385 9190
@@ -8580,7 +9385,7 @@ changepath(const char *newval)
8580 bltin = idx; 9385 bltin = idx;
8581 break; 9386 break;
8582 } 9387 }
8583 new = strchr(new, ':'); 9388 new = strchr(new, PATH_SEP);
8584 if (!new) 9389 if (!new)
8585 break; 9390 break;
8586 idx++; 9391 idx++;
@@ -8757,14 +9562,37 @@ describe_command(char *command, const char *path, int describe_command_verbose)
8757 case CMDNORMAL: { 9562 case CMDNORMAL: {
8758 int j = entry.u.index; 9563 int j = entry.u.index;
8759 char *p; 9564 char *p;
9565#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
9566 if (j < -1) {
9567 p = (char *)bb_basename(command);
9568 if (describe_command_verbose) {
9569 out1fmt(" is a builtin applet");
9570 } else {
9571 out1str(applet_to_exe(p));
9572 }
9573 break;
9574 }
9575#endif
8760 if (j < 0) { 9576 if (j < 0) {
9577#if ENABLE_PLATFORM_MINGW32
9578 p = stack_add_ext_space(command);
9579#else
8761 p = command; 9580 p = command;
9581#endif
8762 } else { 9582 } else {
9583#if ENABLE_PLATFORM_MINGW32
9584 if (unix_path(command))
9585 command = (char *)bb_basename(command);
9586#endif
8763 do { 9587 do {
8764 padvance(&path, command); 9588 padvance(&path, command);
8765 } while (--j >= 0); 9589 } while (--j >= 0);
8766 p = stackblock(); 9590 p = stackblock();
8767 } 9591 }
9592#if ENABLE_PLATFORM_MINGW32
9593 add_win32_extension(p);
9594 bs_to_slash(p);
9595#endif
8768 if (describe_command_verbose) { 9596 if (describe_command_verbose) {
8769 out1fmt(" is %s", p); 9597 out1fmt(" is %s", p);
8770 } else { 9598 } else {
@@ -8920,6 +9748,13 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8920/*static int funcstringsize; // size of strings in node */ 9748/*static int funcstringsize; // size of strings in node */
8921static void *funcblock; /* block to allocate function from */ 9749static void *funcblock; /* block to allocate function from */
8922static char *funcstring_end; /* end of block to allocate strings from */ 9750static char *funcstring_end; /* end of block to allocate strings from */
9751#if ENABLE_PLATFORM_MINGW32
9752static int fs_size;
9753# if FORKSHELL_DEBUG
9754static void *fs_start;
9755static const char **annot;
9756# endif
9757#endif
8923 9758
8924static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 9759static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8925 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 9760 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -9052,14 +9887,60 @@ calcsize(int funcblocksize, union node *n)
9052} 9887}
9053 9888
9054static char * 9889static char *
9055nodeckstrdup(char *s) 9890nodeckstrdup(const char *s)
9056{ 9891{
9892#if ENABLE_PLATFORM_MINGW32
9893 if(!s)
9894 return NULL;
9895#endif
9057 funcstring_end -= SHELL_ALIGN(strlen(s) + 1); 9896 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
9058 return strcpy(funcstring_end, s); 9897 return strcpy(funcstring_end, s);
9059} 9898}
9060 9899
9061static union node *copynode(union node *); 9900static union node *copynode(union node *);
9062 9901
9902#if ENABLE_PLATFORM_MINGW32
9903# if FORKSHELL_DEBUG
9904# define FREE 1
9905# define NO_FREE 2
9906# define ANNOT(dst,note) { \
9907 if (annot) \
9908 annot[(char *)&dst - (char *)fs_start] = note; \
9909 }
9910# else
9911# define FREE 1
9912# define NO_FREE 1
9913# define ANNOT(dst,note)
9914# endif
9915
9916/* The relocation map is offset from the start of the forkshell data
9917 * block by 'fs_size' bytes. The flag relating to a particular destination
9918 * pointer is thus at (dst+fs_size). */
9919# define MARK_PTR(dst,flag) {*((char *)&dst + fs_size) = flag;}
9920
9921# define SAVE_PTR(dst,note,flag) { \
9922 if (fs_size) { \
9923 MARK_PTR(dst,flag); ANNOT(dst,note); \
9924 } \
9925}
9926# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2) { \
9927 if (fs_size) { \
9928 MARK_PTR(dst1,flag1); MARK_PTR(dst2,flag2); \
9929 ANNOT(dst1,note1); ANNOT(dst2,note2); \
9930 } \
9931}
9932# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3) { \
9933 if (fs_size) { \
9934 MARK_PTR(dst1,flag1); MARK_PTR(dst2,flag2); MARK_PTR(dst3,flag3); \
9935 ANNOT(dst1,note1); ANNOT(dst2,note2); ANNOT(dst3,note3); \
9936 } \
9937}
9938#else
9939# define SAVE_PTR(dst,note,flag)
9940# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2)
9941# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3)
9942#endif
9943
9063static struct nodelist * 9944static struct nodelist *
9064copynodelist(struct nodelist *lp) 9945copynodelist(struct nodelist *lp)
9065{ 9946{
@@ -9071,6 +9952,8 @@ copynodelist(struct nodelist *lp)
9071 *lpp = funcblock; 9952 *lpp = funcblock;
9072 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); 9953 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
9073 (*lpp)->n = copynode(lp->n); 9954 (*lpp)->n = copynode(lp->n);
9955 SAVE_PTR2((*lpp)->n, "(*lpp)->next", NO_FREE,
9956 (*lpp)->next, "(*lpp)->next", NO_FREE);
9074 lp = lp->next; 9957 lp = lp->next;
9075 lpp = &(*lpp)->next; 9958 lpp = &(*lpp)->next;
9076 } 9959 }
@@ -9094,10 +9977,14 @@ copynode(union node *n)
9094 new->ncmd.args = copynode(n->ncmd.args); 9977 new->ncmd.args = copynode(n->ncmd.args);
9095 new->ncmd.assign = copynode(n->ncmd.assign); 9978 new->ncmd.assign = copynode(n->ncmd.assign);
9096 new->ncmd.linno = n->ncmd.linno; 9979 new->ncmd.linno = n->ncmd.linno;
9980 SAVE_PTR3(new->ncmd.redirect, "ncmd.redirect", NO_FREE,
9981 new->ncmd.args, "ncmd.args", NO_FREE,
9982 new->ncmd.assign, "ncmd.assign", NO_FREE);
9097 break; 9983 break;
9098 case NPIPE: 9984 case NPIPE:
9099 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); 9985 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
9100 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; 9986 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
9987 SAVE_PTR(new->npipe.cmdlist, "npipe.cmdlist", NO_FREE);
9101 break; 9988 break;
9102 case NREDIR: 9989 case NREDIR:
9103 case NBACKGND: 9990 case NBACKGND:
@@ -9105,6 +9992,8 @@ copynode(union node *n)
9105 new->nredir.redirect = copynode(n->nredir.redirect); 9992 new->nredir.redirect = copynode(n->nredir.redirect);
9106 new->nredir.n = copynode(n->nredir.n); 9993 new->nredir.n = copynode(n->nredir.n);
9107 new->nredir.linno = n->nredir.linno; 9994 new->nredir.linno = n->nredir.linno;
9995 SAVE_PTR2(new->nredir.redirect, "nredir.redirect", NO_FREE,
9996 new->nredir.n, "nredir.n", NO_FREE);
9108 break; 9997 break;
9109 case NAND: 9998 case NAND:
9110 case NOR: 9999 case NOR:
@@ -9113,37 +10002,58 @@ copynode(union node *n)
9113 case NUNTIL: 10002 case NUNTIL:
9114 new->nbinary.ch2 = copynode(n->nbinary.ch2); 10003 new->nbinary.ch2 = copynode(n->nbinary.ch2);
9115 new->nbinary.ch1 = copynode(n->nbinary.ch1); 10004 new->nbinary.ch1 = copynode(n->nbinary.ch1);
10005 SAVE_PTR2(new->nbinary.ch1, "nbinary.ch1", NO_FREE,
10006 new->nbinary.ch2, "nbinary.ch2", NO_FREE);
9116 break; 10007 break;
9117 case NIF: 10008 case NIF:
9118 new->nif.elsepart = copynode(n->nif.elsepart); 10009 new->nif.elsepart = copynode(n->nif.elsepart);
9119 new->nif.ifpart = copynode(n->nif.ifpart); 10010 new->nif.ifpart = copynode(n->nif.ifpart);
9120 new->nif.test = copynode(n->nif.test); 10011 new->nif.test = copynode(n->nif.test);
10012 SAVE_PTR3(new->nif.elsepart, "nif.elsepart", NO_FREE,
10013 new->nif.ifpart, "nif.ifpart", NO_FREE,
10014 new->nif.test, "nif.test", NO_FREE);
9121 break; 10015 break;
9122 case NFOR: 10016 case NFOR:
9123 new->nfor.var = nodeckstrdup(n->nfor.var); 10017 new->nfor.var = nodeckstrdup(n->nfor.var);
9124 new->nfor.body = copynode(n->nfor.body); 10018 new->nfor.body = copynode(n->nfor.body);
9125 new->nfor.args = copynode(n->nfor.args); 10019 new->nfor.args = copynode(n->nfor.args);
9126 new->nfor.linno = n->nfor.linno; 10020 new->nfor.linno = n->nfor.linno;
10021 SAVE_PTR3(new->nfor.var,
10022 xasprintf("nfor.var '%s'", n->nfor.var ?: "NULL"), FREE,
10023 new->nfor.body, "nfor.body", NO_FREE,
10024 new->nfor.args, "nfor.args", NO_FREE);
9127 break; 10025 break;
9128 case NCASE: 10026 case NCASE:
9129 new->ncase.cases = copynode(n->ncase.cases); 10027 new->ncase.cases = copynode(n->ncase.cases);
9130 new->ncase.expr = copynode(n->ncase.expr); 10028 new->ncase.expr = copynode(n->ncase.expr);
9131 new->ncase.linno = n->ncase.linno; 10029 new->ncase.linno = n->ncase.linno;
10030 SAVE_PTR2(new->ncase.cases, "ncase.cases", NO_FREE,
10031 new->ncase.expr, "ncase.expr", NO_FREE);
9132 break; 10032 break;
9133 case NCLIST: 10033 case NCLIST:
9134 new->nclist.body = copynode(n->nclist.body); 10034 new->nclist.body = copynode(n->nclist.body);
9135 new->nclist.pattern = copynode(n->nclist.pattern); 10035 new->nclist.pattern = copynode(n->nclist.pattern);
9136 new->nclist.next = copynode(n->nclist.next); 10036 new->nclist.next = copynode(n->nclist.next);
10037 SAVE_PTR3(new->nclist.body, "nclist.body", NO_FREE,
10038 new->nclist.pattern, "nclist.pattern", NO_FREE,
10039 new->nclist.next, "nclist.next", NO_FREE);
9137 break; 10040 break;
9138 case NDEFUN: 10041 case NDEFUN:
9139 new->ndefun.body = copynode(n->ndefun.body); 10042 new->ndefun.body = copynode(n->ndefun.body);
9140 new->ndefun.text = nodeckstrdup(n->ndefun.text); 10043 new->ndefun.text = nodeckstrdup(n->ndefun.text);
9141 new->ndefun.linno = n->ndefun.linno; 10044 new->ndefun.linno = n->ndefun.linno;
10045 SAVE_PTR2(new->ndefun.body, "ndefun.body", NO_FREE,
10046 new->ndefun.text,
10047 xasprintf("ndefun.text '%s'", n->ndefun.text ?: "NULL"), FREE);
9142 break; 10048 break;
9143 case NARG: 10049 case NARG:
9144 new->narg.backquote = copynodelist(n->narg.backquote); 10050 new->narg.backquote = copynodelist(n->narg.backquote);
9145 new->narg.text = nodeckstrdup(n->narg.text); 10051 new->narg.text = nodeckstrdup(n->narg.text);
9146 new->narg.next = copynode(n->narg.next); 10052 new->narg.next = copynode(n->narg.next);
10053 SAVE_PTR3(new->narg.backquote, "narg.backquote", NO_FREE,
10054 new->narg.text,
10055 xasprintf("narg.text '%s'", n->narg.text ?: "NULL"), FREE,
10056 new->narg.next, "narg.next", NO_FREE);
9147 break; 10057 break;
9148 case NTO: 10058 case NTO:
9149#if BASH_REDIR_OUTPUT 10059#if BASH_REDIR_OUTPUT
@@ -9156,6 +10066,8 @@ copynode(union node *n)
9156 new->nfile.fname = copynode(n->nfile.fname); 10066 new->nfile.fname = copynode(n->nfile.fname);
9157 new->nfile.fd = n->nfile.fd; 10067 new->nfile.fd = n->nfile.fd;
9158 new->nfile.next = copynode(n->nfile.next); 10068 new->nfile.next = copynode(n->nfile.next);
10069 SAVE_PTR2(new->nfile.fname, "nfile.fname", NO_FREE,
10070 new->nfile.next, "nfile.next", NO_FREE);
9159 break; 10071 break;
9160 case NTOFD: 10072 case NTOFD:
9161 case NFROMFD: 10073 case NFROMFD:
@@ -9163,15 +10075,20 @@ copynode(union node *n)
9163 new->ndup.dupfd = n->ndup.dupfd; 10075 new->ndup.dupfd = n->ndup.dupfd;
9164 new->ndup.fd = n->ndup.fd; 10076 new->ndup.fd = n->ndup.fd;
9165 new->ndup.next = copynode(n->ndup.next); 10077 new->ndup.next = copynode(n->ndup.next);
10078 SAVE_PTR2(new->ndup.vname, "ndup.vname", NO_FREE,
10079 new->ndup.next, "ndup.next", NO_FREE);
9166 break; 10080 break;
9167 case NHERE: 10081 case NHERE:
9168 case NXHERE: 10082 case NXHERE:
9169 new->nhere.doc = copynode(n->nhere.doc); 10083 new->nhere.doc = copynode(n->nhere.doc);
9170 new->nhere.fd = n->nhere.fd; 10084 new->nhere.fd = n->nhere.fd;
9171 new->nhere.next = copynode(n->nhere.next); 10085 new->nhere.next = copynode(n->nhere.next);
10086 SAVE_PTR2(new->nhere.doc, "nhere.doc", NO_FREE,
10087 new->nhere.next, "nhere.next", NO_FREE);
9172 break; 10088 break;
9173 case NNOT: 10089 case NNOT:
9174 new->nnot.com = copynode(n->nnot.com); 10090 new->nnot.com = copynode(n->nnot.com);
10091 SAVE_PTR(new->nnot.com, "nnot.com", NO_FREE);
9175 break; 10092 break;
9176 }; 10093 };
9177 new->type = n->type; 10094 new->type = n->type;
@@ -9192,6 +10109,7 @@ copyfunc(union node *n)
9192 f = ckzalloc(blocksize /* + funcstringsize */); 10109 f = ckzalloc(blocksize /* + funcstringsize */);
9193 funcblock = (char *) f + offsetof(struct funcnode, n); 10110 funcblock = (char *) f + offsetof(struct funcnode, n);
9194 funcstring_end = (char *) f + blocksize; 10111 funcstring_end = (char *) f + blocksize;
10112 IF_PLATFORM_MINGW32(fs_size = 0);
9195 copynode(n); 10113 copynode(n);
9196 /* f->count = 0; - ckzalloc did it */ 10114 /* f->count = 0; - ckzalloc did it */
9197 return f; 10115 return f;
@@ -9219,12 +10137,15 @@ defun(union node *func)
9219#define SKIPFUNCDEF (1 << 3) 10137#define SKIPFUNCDEF (1 << 3)
9220static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 10138static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
9221static int skipcount; /* number of levels to skip */ 10139static int skipcount; /* number of levels to skip */
10140#if ENABLE_PLATFORM_POSIX
9222static int loopnest; /* current loop nesting level */ 10141static int loopnest; /* current loop nesting level */
10142#endif
9223static int funcline; /* starting line number of current function, or 0 if not in a function */ 10143static int funcline; /* starting line number of current function, or 0 if not in a function */
9224 10144
9225/* Forward decl way out to parsing code - dotrap needs it */ 10145/* Forward decl way out to parsing code - dotrap needs it */
9226static int evalstring(char *s, int flags); 10146static int evalstring(char *s, int flags);
9227 10147
10148#if !ENABLE_PLATFORM_MINGW32
9228/* Called to execute a trap. 10149/* Called to execute a trap.
9229 * Single callsite - at the end of evaltree(). 10150 * Single callsite - at the end of evaltree().
9230 * If we return non-zero, evaltree raises EXEXIT exception. 10151 * If we return non-zero, evaltree raises EXEXIT exception.
@@ -9283,6 +10204,45 @@ dotrap(void)
9283 savestatus = last_status; 10204 savestatus = last_status;
9284 TRACE(("dotrap returns\n")); 10205 TRACE(("dotrap returns\n"));
9285} 10206}
10207#else
10208static void
10209dotrap(void)
10210{
10211 int status, last_status;
10212 char *p;
10213
10214 if (!pending_int)
10215 return;
10216
10217 status = savestatus;
10218 last_status = status;
10219 if (status < 0) {
10220 status = exitstatus;
10221 savestatus = status;
10222 }
10223 pending_int = 0;
10224 barrier();
10225
10226 TRACE(("dotrap entered\n"));
10227 if (evalskip) {
10228 pending_int = 1;
10229 return;
10230 }
10231
10232 p = trap[SIGINT];
10233 if (p) {
10234 TRACE(("sig %d is active, will run handler '%s'\n", SIGINT, p));
10235 trap_depth++;
10236 evalstring(p, 0);
10237 trap_depth--;
10238 if (evalskip != SKIPFUNC)
10239 exitstatus = status;
10240 }
10241
10242 savestatus = last_status;
10243 TRACE(("dotrap returns\n"));
10244}
10245#endif
9286 10246
9287/* forward declarations - evaluation is fairly recursive business... */ 10247/* forward declarations - evaluation is fairly recursive business... */
9288static int evalloop(union node *, int); 10248static int evalloop(union node *, int);
@@ -9568,19 +10528,36 @@ evalcase(union node *n, int flags)
9568static int 10528static int
9569evalsubshell(union node *n, int flags) 10529evalsubshell(union node *n, int flags)
9570{ 10530{
10531 IF_PLATFORM_MINGW32(struct forkshell fs;)
9571 struct job *jp; 10532 struct job *jp;
9572 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ 10533 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9573 int status; 10534 int status;
9574 10535
9575 errlinno = lineno = n->nredir.linno; 10536 errlinno = lineno = n->nredir.linno;
9576 10537
10538#if ENABLE_PLATFORM_MINGW32
10539 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) {
10540 expredir(n->nredir.redirect);
10541 redirect(n->nredir.redirect, 0);
10542 evaltreenr(n->nredir.n, flags);
10543 /* never returns */
10544 }
10545#else
9577 expredir(n->nredir.redirect); 10546 expredir(n->nredir.redirect);
9578 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) 10547 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
9579 goto nofork; 10548 goto nofork;
10549#endif
9580 INT_OFF; 10550 INT_OFF;
9581 if (backgnd == FORK_FG) 10551 if (backgnd == FORK_FG)
9582 get_tty_state(); 10552 get_tty_state();
9583 jp = makejob(/*n,*/ 1); 10553 jp = makejob(/*n,*/ 1);
10554#if ENABLE_PLATFORM_MINGW32
10555 memset(&fs, 0, sizeof(fs));
10556 fs.fpid = FS_EVALSUBSHELL;
10557 fs.n = n;
10558 fs.flags = flags;
10559 spawn_forkshell(&fs, jp, n, backgnd);
10560#else
9584 if (forkshell(jp, n, backgnd) == 0) { 10561 if (forkshell(jp, n, backgnd) == 0) {
9585 /* child */ 10562 /* child */
9586 INT_ON; 10563 INT_ON;
@@ -9592,6 +10569,7 @@ evalsubshell(union node *n, int flags)
9592 evaltreenr(n->nredir.n, flags); 10569 evaltreenr(n->nredir.n, flags);
9593 /* never returns */ 10570 /* never returns */
9594 } 10571 }
10572#endif
9595 /* parent */ 10573 /* parent */
9596 status = 0; 10574 status = 0;
9597 if (backgnd == FORK_FG) 10575 if (backgnd == FORK_FG)
@@ -9672,6 +10650,7 @@ expredir(union node *n)
9672static int 10650static int
9673evalpipe(union node *n, int flags) 10651evalpipe(union node *n, int flags)
9674{ 10652{
10653 IF_PLATFORM_MINGW32(struct forkshell fs;)
9675 struct job *jp; 10654 struct job *jp;
9676 struct nodelist *lp; 10655 struct nodelist *lp;
9677 int pipelen; 10656 int pipelen;
@@ -9698,6 +10677,16 @@ evalpipe(union node *n, int flags)
9698 ash_msg_and_raise_perror("can't create pipe"); 10677 ash_msg_and_raise_perror("can't create pipe");
9699 } 10678 }
9700 } 10679 }
10680#if ENABLE_PLATFORM_MINGW32
10681 memset(&fs, 0, sizeof(fs));
10682 fs.fpid = FS_EVALPIPE;
10683 fs.flags = flags;
10684 fs.n = lp->n;
10685 fs.fd[0] = pip[0];
10686 fs.fd[1] = pip[1];
10687 fs.fd[2] = prevfd;
10688 spawn_forkshell(&fs, jp, lp->n, n->npipe.pipe_backgnd);
10689#else
9701 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 10690 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
9702 /* child */ 10691 /* child */
9703 INT_ON; 10692 INT_ON;
@@ -9715,6 +10704,7 @@ evalpipe(union node *n, int flags)
9715 evaltreenr(lp->n, flags); 10704 evaltreenr(lp->n, flags);
9716 /* never returns */ 10705 /* never returns */
9717 } 10706 }
10707#endif
9718 /* parent */ 10708 /* parent */
9719 if (prevfd >= 0) 10709 if (prevfd >= 0)
9720 close(prevfd); 10710 close(prevfd);
@@ -9860,6 +10850,10 @@ poplocalvars(int keep)
9860 free((char*)vp->var_text); 10850 free((char*)vp->var_text);
9861 vp->flags = lvp->flags; 10851 vp->flags = lvp->flags;
9862 vp->var_text = lvp->text; 10852 vp->var_text = lvp->text;
10853#if ENABLE_PLATFORM_MINGW32
10854 if (is_bb_var(lvp->text) == BB_VAR_ASSIGN)
10855 putenv(lvp->text);
10856#endif
9863 } 10857 }
9864 free(lvp); 10858 free(lvp);
9865 } 10859 }
@@ -10209,7 +11203,7 @@ static const struct builtincmd builtintab[] = {
10209#if MAX_HISTORY 11203#if MAX_HISTORY
10210 { BUILTIN_NOSPEC "history" , historycmd }, 11204 { BUILTIN_NOSPEC "history" , historycmd },
10211#endif 11205#endif
10212#if JOBS 11206#if JOBS || JOBS_WIN32
10213 { BUILTIN_REGULAR "jobs" , jobscmd }, 11207 { BUILTIN_REGULAR "jobs" , jobscmd },
10214 { BUILTIN_REGULAR "kill" , killcmd }, 11208 { BUILTIN_REGULAR "kill" , killcmd },
10215#endif 11209#endif
@@ -10254,7 +11248,7 @@ static const struct builtincmd builtintab[] = {
10254 /* [ */ 1 * ENABLE_ASH_TEST + \ 11248 /* [ */ 1 * ENABLE_ASH_TEST + \
10255 /* [[ */ 1 * BASH_TEST2 + \ 11249 /* [[ */ 1 * BASH_TEST2 + \
10256 /* alias */ 1 * ENABLE_ASH_ALIAS + \ 11250 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10257 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ 11251 /* bg */ 1 * JOBS + \
10258 /* break cd cddir */ 3) 11252 /* break cd cddir */ 3)
10259#define EVALCMD (COMMANDCMD + \ 11253#define EVALCMD (COMMANDCMD + \
10260 /* command */ 1 * ENABLE_ASH_CMDCMD + \ 11254 /* command */ 1 * ENABLE_ASH_CMDCMD + \
@@ -10335,6 +11329,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10335 * as POSIX mandates */ 11329 * as POSIX mandates */
10336 return back_exitstatus; 11330 return back_exitstatus;
10337} 11331}
11332
10338static int 11333static int
10339evalcommand(union node *cmd, int flags) 11334evalcommand(union node *cmd, int flags)
10340{ 11335{
@@ -10588,6 +11583,22 @@ evalcommand(union node *cmd, int flags)
10588 * in a script or a subshell does not need forking, 11583 * in a script or a subshell does not need forking,
10589 * we can just exec it. 11584 * we can just exec it.
10590 */ 11585 */
11586#if ENABLE_PLATFORM_MINGW32
11587 if (!(flags & EV_EXIT) || may_have_traps IF_SUW32(|| delayexit)) {
11588 /* No, forking off a child is necessary */
11589 struct forkshell fs;
11590
11591 INT_OFF;
11592 memset(&fs, 0, sizeof(fs));
11593 fs.fpid = FS_SHELLEXEC;
11594 fs.argv = argv;
11595 fs.path = (char*)path;
11596 fs.fd[0] = cmdentry.u.index;
11597 jp = makejob(/*cmd,*/ 1);
11598 spawn_forkshell(&fs, jp, cmd, FORK_FG);
11599 break;
11600 }
11601#else
10591 if (!(flags & EV_EXIT) || may_have_traps) { 11602 if (!(flags & EV_EXIT) || may_have_traps) {
10592 /* No, forking off a child is necessary */ 11603 /* No, forking off a child is necessary */
10593 INT_OFF; 11604 INT_OFF;
@@ -10601,6 +11612,7 @@ evalcommand(union node *cmd, int flags)
10601 FORCE_INT_ON; 11612 FORCE_INT_ON;
10602 /* fall through to exec'ing external program */ 11613 /* fall through to exec'ing external program */
10603 } 11614 }
11615#endif
10604 shellexec(argv[0], argv, path, cmdentry.u.index); 11616 shellexec(argv[0], argv, path, cmdentry.u.index);
10605 /* NOTREACHED */ 11617 /* NOTREACHED */
10606 } /* default */ 11618 } /* default */
@@ -10813,6 +11825,54 @@ static void popstring(void)
10813 INT_ON; 11825 INT_ON;
10814} 11826}
10815 11827
11828#if ENABLE_PLATFORM_MINGW32
11829/*
11830 * Wrapper around nonblock_immune_read() to remove CRs, but only from
11831 * CRLF pairs. The tricky part is handling a CR at the end of the buffer.
11832 */
11833static inline ssize_t
11834nonblock_immune_wrapper(struct parsefile *pf, char *buffer, size_t count)
11835{
11836 int nr, injected_cr;
11837
11838 // Inject unprocessed CR from previous read into the buffer.
11839 if (pf->cr)
11840 *buffer = '\r';
11841 retry:
11842 nr = nonblock_immune_read(pf->pf_fd, buffer + pf->cr, count - pf->cr);
11843 if (nr < 0)
11844 return nr;
11845
11846 injected_cr = pf->cr;
11847 nr += pf->cr;
11848 pf->cr = 0;
11849
11850 if (nr > 0) {
11851 nr = remove_cr(buffer, nr);
11852 // remove_cr() won't reduce size to zero, so [nr - 1] is OK.
11853 if (buffer[nr - 1] == '\r') {
11854 if (nr > 1) {
11855 // Ignore trailing CR for now: we'll deal with it later.
11856 pf->cr = 1;
11857 --nr;
11858 } else if (injected_cr) { // nr == 1
11859 // Buffer only contains an injected CR. This means the
11860 // read returned EOF. Return the buffer as-is. The
11861 // next call will detect EOF.
11862 } else {
11863 // Buffer only contains a CR from the most recent read.
11864 // Try another read, treating the CR as injected. We'll
11865 // either get more characters or EOF. Either way we
11866 // won't end up here again.
11867 pf->cr = 1;
11868 goto retry;
11869 }
11870 }
11871 }
11872 return nr;
11873}
11874#endif
11875
10816static int 11876static int
10817preadfd(void) 11877preadfd(void)
10818{ 11878{
@@ -10823,7 +11883,11 @@ preadfd(void)
10823#if ENABLE_FEATURE_EDITING 11883#if ENABLE_FEATURE_EDITING
10824 /* retry: */ 11884 /* retry: */
10825 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 11885 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
11886#if ENABLE_PLATFORM_MINGW32
11887 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
11888#else
10826 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 11889 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
11890#endif
10827 else { 11891 else {
10828# if ENABLE_ASH_IDLE_TIMEOUT 11892# if ENABLE_ASH_IDLE_TIMEOUT
10829 int timeout = -1; 11893 int timeout = -1;
@@ -10862,12 +11926,21 @@ preadfd(void)
10862 INT_ON; /* here non-blocked SIGINT will longjmp */ 11926 INT_ON; /* here non-blocked SIGINT will longjmp */
10863 if (nr == 0) { 11927 if (nr == 0) {
10864 /* ^C pressed, "convert" to SIGINT */ 11928 /* ^C pressed, "convert" to SIGINT */
11929# if !ENABLE_PLATFORM_MINGW32
10865 write(STDOUT_FILENO, "^C\n", 3); 11930 write(STDOUT_FILENO, "^C\n", 3);
10866 raise(SIGINT); /* here non-blocked SIGINT will longjmp */ 11931 raise(SIGINT); /* here non-blocked SIGINT will longjmp */
10867 /* raise(SIGINT) did not work! (e.g. if SIGINT 11932 /* raise(SIGINT) did not work! (e.g. if SIGINT
10868 * is SIG_IGNed on startup, it stays SIG_IGNed) 11933 * is SIG_IGNed on startup, it stays SIG_IGNed)
10869 */ 11934 */
11935# else
11936 raise_interrupt();
11937 write(STDOUT_FILENO, "^C\n", 3);
11938# endif
10870 if (trap[SIGINT]) { 11939 if (trap[SIGINT]) {
11940# if ENABLE_PLATFORM_MINGW32
11941 pending_int = 1;
11942 dotrap();
11943# endif
10871 empty_line_input: 11944 empty_line_input:
10872 buf[0] = '\n'; 11945 buf[0] = '\n';
10873 buf[1] = '\0'; 11946 buf[1] = '\0';
@@ -10896,7 +11969,11 @@ preadfd(void)
10896 } 11969 }
10897 } 11970 }
10898#else 11971#else
11972# if ENABLE_PLATFORM_MINGW32
11973 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
11974# else
10899 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 11975 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
11976# endif
10900#endif 11977#endif
10901 11978
10902#if 0 /* disabled: nonblock_immune_read() handles this problem */ 11979#if 0 /* disabled: nonblock_immune_read() handles this problem */
@@ -11195,6 +12272,7 @@ popallfiles(void)
11195 unwindfiles(&basepf); 12272 unwindfiles(&basepf);
11196} 12273}
11197 12274
12275#if !ENABLE_PLATFORM_MINGW32
11198/* 12276/*
11199 * Close the file(s) that the shell is reading commands from. Called 12277 * Close the file(s) that the shell is reading commands from. Called
11200 * after a fork is done. 12278 * after a fork is done.
@@ -11208,6 +12286,7 @@ closescript(void)
11208 g_parsefile->pf_fd = 0; 12286 g_parsefile->pf_fd = 0;
11209 } 12287 }
11210} 12288}
12289#endif
11211 12290
11212/* 12291/*
11213 * Like setinputfile, but takes an open file descriptor. Call this with 12292 * Like setinputfile, but takes an open file descriptor. Call this with
@@ -11444,8 +12523,16 @@ options(int *login_sh)
11444 int val; 12523 int val;
11445 int c; 12524 int c;
11446 12525
11447 if (login_sh != NULL) /* if we came from startup code */ 12526 if (login_sh != NULL) { /* if we came from startup code */
11448 minusc = NULL; 12527 minusc = NULL;
12528#if ENABLE_PLATFORM_MINGW32
12529 dirarg = NULL;
12530 title = NULL;
12531# if ENABLE_SUW32
12532 delayexit = 0;
12533# endif
12534#endif
12535 }
11449 while ((p = *argptr) != NULL) { 12536 while ((p = *argptr) != NULL) {
11450 c = *p++; 12537 c = *p++;
11451 if (c != '-' && c != '+') 12538 if (c != '-' && c != '+')
@@ -11475,6 +12562,31 @@ options(int *login_sh)
11475 cflag = 1; 12562 cflag = 1;
11476 continue; 12563 continue;
11477 } 12564 }
12565#if ENABLE_PLATFORM_MINGW32
12566 /* Undocumented flags;
12567 * -d force current directory
12568 * -t title to display in console window
12569 * -N prompt user before exit
12570 * Must appear before -s or -c. */
12571 if (c == 'd' && val == 1) {
12572 if (*argptr == NULL)
12573 ash_msg_and_raise_error(bb_msg_requires_arg, "-d");
12574 dirarg = *argptr++;
12575 continue;
12576 }
12577 if (c == 't' && val == 1) {
12578 if (*argptr == NULL)
12579 ash_msg_and_raise_error(bb_msg_requires_arg, "-t");
12580 title = *argptr++;
12581 continue;
12582 }
12583# if ENABLE_SUW32
12584 if (c == 'N' && val == 1) {
12585 delayexit = 1;
12586 continue;
12587 }
12588# endif
12589#endif
11478 if (c == 's') { /* -s, +s */ 12590 if (c == 's') { /* -s, +s */
11479 sflag = 1; 12591 sflag = 1;
11480 continue; 12592 continue;
@@ -13501,6 +14613,9 @@ evalstring(char *s, int flags)
13501 int status; 14613 int status;
13502 14614
13503 s = sstrdup(s); 14615 s = sstrdup(s);
14616#if ENABLE_PLATFORM_MINGW32
14617 remove_cr(s, strlen(s)+1);
14618#endif
13504 setinputstring(s); 14619 setinputstring(s);
13505 setstackmark(&smark); 14620 setstackmark(&smark);
13506 14621
@@ -13588,7 +14703,7 @@ cmdloop(int top)
13588 int skip; 14703 int skip;
13589 14704
13590 setstackmark(&smark); 14705 setstackmark(&smark);
13591#if JOBS 14706#if JOBS || JOBS_WIN32
13592 if (doing_jobctl) 14707 if (doing_jobctl)
13593 showjobs(SHOW_CHANGED|SHOW_STDERR); 14708 showjobs(SHOW_CHANGED|SHOW_STDERR);
13594#endif 14709#endif
@@ -13596,6 +14711,9 @@ cmdloop(int top)
13596 if (iflag && top) { 14711 if (iflag && top) {
13597 inter++; 14712 inter++;
13598 chkmail(); 14713 chkmail();
14714#if ENABLE_PLATFORM_MINGW32
14715 terminal_mode(TRUE);
14716#endif
13599 } 14717 }
13600 n = parsecmd(inter); 14718 n = parsecmd(inter);
13601#if DEBUG 14719#if DEBUG
@@ -13619,8 +14737,10 @@ cmdloop(int top)
13619 } else { 14737 } else {
13620 int i; 14738 int i;
13621 14739
14740#if !ENABLE_PLATFORM_MINGW32
13622 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ 14741 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13623 job_warning >>= 1; 14742 job_warning >>= 1;
14743#endif
13624 numeof = 0; 14744 numeof = 0;
13625 i = evaltree(n, 0); 14745 i = evaltree(n, 0);
13626 if (n) 14746 if (n)
@@ -13650,7 +14770,7 @@ find_dot_file(char *basename)
13650 int len; 14770 int len;
13651 14771
13652 /* don't try this for absolute or relative paths */ 14772 /* don't try this for absolute or relative paths */
13653 if (strchr(basename, '/')) 14773 if (strchr(basename, '/') IF_PLATFORM_MINGW32(|| strchr(basename, '\\')))
13654 return basename; 14774 return basename;
13655 14775
13656 path = pathval(); 14776 path = pathval();
@@ -13779,6 +14899,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13779 struct builtincmd *bcmd; 14899 struct builtincmd *bcmd;
13780 int len; 14900 int len;
13781 14901
14902#if !ENABLE_PLATFORM_MINGW32
13782 /* If name contains a slash, don't use PATH or hash table */ 14903 /* If name contains a slash, don't use PATH or hash table */
13783 if (strchr(name, '/') != NULL) { 14904 if (strchr(name, '/') != NULL) {
13784 entry->u.index = -1; 14905 entry->u.index = -1;
@@ -13795,6 +14916,35 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13795 entry->cmdtype = CMDNORMAL; 14916 entry->cmdtype = CMDNORMAL;
13796 return; 14917 return;
13797 } 14918 }
14919#else /* ENABLE_PLATFORM_MINGW32 */
14920 /* If name contains a slash or drive prefix, don't use PATH or hash table */
14921 if (has_path(name)) {
14922 entry->u.index = -1;
14923 entry->cmdtype = CMDNORMAL;
14924 fullname = stack_add_ext_space(name);
14925 if (add_win32_extension(fullname) || file_is_executable(fullname)) {
14926 return;
14927 } else if (unix_path(name)) {
14928 name = (char *)bb_basename(name);
14929 if (
14930# if ENABLE_FEATURE_SH_STANDALONE
14931 find_applet_by_name(name) >= 0 ||
14932# endif
14933 !find_builtin(bb_basename(name))
14934 ) {
14935 act |= DO_NOFUNC;
14936 } else if (act & DO_ABS) {
14937 entry->cmdtype = CMDUNKNOWN;
14938 return;
14939 }
14940 } else if (act & DO_ABS) {
14941 entry->cmdtype = CMDUNKNOWN;
14942 return;
14943 } else {
14944 return;
14945 }
14946 }
14947#endif /* ENABLE_PLATFORM_MINGW32 */
13798 14948
13799/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ 14949/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
13800 14950
@@ -13888,12 +15038,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13888 } 15038 }
13889 } 15039 }
13890 /* if rehash, don't redo absolute path names */ 15040 /* if rehash, don't redo absolute path names */
13891 if (fullname[0] == '/' && idx <= prev) { 15041 if (!is_relative_path(fullname) && idx <= prev) {
13892 if (idx < prev) 15042 if (idx < prev)
13893 continue; 15043 continue;
13894 TRACE(("searchexec \"%s\": no change\n", name)); 15044 TRACE(("searchexec \"%s\": no change\n", name));
13895 goto success; 15045 goto success;
13896 } 15046 }
15047#if ENABLE_PLATFORM_MINGW32
15048 add_win32_extension(fullname);
15049#endif
13897 while (stat(fullname, &statb) < 0) { 15050 while (stat(fullname, &statb) < 0) {
13898#ifdef SYSV 15051#ifdef SYSV
13899 if (errno == EINTR) 15052 if (errno == EINTR)
@@ -14032,19 +15185,41 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14032 if (LONE_DASH(action)) 15185 if (LONE_DASH(action))
14033 action = NULL; 15186 action = NULL;
14034 else { 15187 else {
15188#if !ENABLE_PLATFORM_MINGW32
14035 if (action[0]) /* not NULL and not "" and not "-" */ 15189 if (action[0]) /* not NULL and not "" and not "-" */
14036 may_have_traps = 1; 15190 may_have_traps = 1;
15191#endif
14037 action = ckstrdup(action); 15192 action = ckstrdup(action);
14038 } 15193 }
14039 } 15194 }
14040 free(trap[signo]); 15195 free(trap[signo]);
14041 trap[signo] = action; 15196 trap[signo] = action;
15197#if ENABLE_PLATFORM_MINGW32
15198 if (signo == SIGINT) {
15199 // trap '' INT disables Ctrl-C, anything else enables it
15200 if (action && action[0] == '\0') {
15201 SetConsoleCtrlHandler(NULL, TRUE);
15202 if (line_input_state) {
15203 line_input_state->flags |= IGNORE_CTRL_C;
15204 }
15205 } else {
15206 SetConsoleCtrlHandler(NULL, FALSE);
15207 if (line_input_state) {
15208 line_input_state->flags &= ~IGNORE_CTRL_C;
15209 }
15210 }
15211 }
15212#else
14042 if (signo != 0 && signo < NSIG) 15213 if (signo != 0 && signo < NSIG)
14043 setsignal(signo); 15214 setsignal(signo);
15215#endif
14044 INT_ON; 15216 INT_ON;
14045 next: 15217 next:
14046 ap++; 15218 ap++;
14047 } 15219 }
15220#if ENABLE_PLATFORM_MINGW32
15221 may_have_traps = trap[SIGINT] && trap[SIGINT][0] != '\0';
15222#endif
14048 return exitcode; 15223 return exitcode;
14049} 15224}
14050 15225
@@ -14073,10 +15248,12 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14073 { 15248 {
14074 const char *a = applet_names; 15249 const char *a = applet_names;
14075 while (*a) { 15250 while (*a) {
14076 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a); 15251 if (is_applet_preferred(a)) {
14077 if (col > 60) { 15252 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
14078 out1fmt("\n"); 15253 if (col > 60) {
14079 col = 0; 15254 out1fmt("\n");
15255 col = 0;
15256 }
14080 } 15257 }
14081 while (*a++ != '\0') 15258 while (*a++ != '\0')
14082 continue; 15259 continue;
@@ -14138,6 +15315,14 @@ exportcmd(int argc UNUSED_PARAM, char **argv)
14138 } else { 15315 } else {
14139 vp = *findvar(hashvar(name), name); 15316 vp = *findvar(hashvar(name), name);
14140 if (vp) { 15317 if (vp) {
15318#if ENABLE_PLATFORM_MINGW32
15319 if (is_bb_var(name) == BB_VAR_EXACT) {
15320 if (flag_off == ~VEXPORT)
15321 unsetenv(name);
15322 else if (flag == VEXPORT && !(vp->flags & VUNSET))
15323 putenv(vp->var_text);
15324 }
15325#endif
14141 vp->flags = ((vp->flags | flag) & flag_off); 15326 vp->flags = ((vp->flags | flag) & flag_off);
14142 continue; 15327 continue;
14143 } 15328 }
@@ -14329,6 +15514,27 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14329 goto again; 15514 goto again;
14330 } 15515 }
14331 15516
15517#if ENABLE_PLATFORM_MINGW32
15518 if ((uintptr_t)r == 2) {
15519 /* ^C pressed, propagate event */
15520 if (trap[SIGINT]) {
15521 write(STDOUT_FILENO, "^C", 2);
15522 pending_int = 1;
15523 dotrap();
15524 if (!(rootshell && iflag))
15525 return (uintptr_t)0;
15526 else
15527 goto again;
15528 } else if (iflag) {
15529 raise_interrupt();
15530 } else {
15531 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
15532 exitshell();
15533 }
15534 return (uintptr_t)r;
15535 }
15536#endif
15537
14332 if ((uintptr_t)r > 1) 15538 if ((uintptr_t)r > 1)
14333 ash_msg_and_raise_error(r); 15539 ash_msg_and_raise_error(r);
14334 15540
@@ -14389,6 +15595,9 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14389 if (!isdigit(modestr[0])) 15595 if (!isdigit(modestr[0]))
14390 mask ^= 0777; 15596 mask ^= 0777;
14391 umask(mask); 15597 umask(mask);
15598#if ENABLE_PLATFORM_MINGW32
15599 setvareq(xasprintf("BB_UMASK=0%o", mask), VEXPORT|VNOSAVE);
15600#endif
14392 } 15601 }
14393 return 0; 15602 return 0;
14394} 15603}
@@ -14472,6 +15681,13 @@ exitshell(void)
14472 /*free(p); - we'll exit soon */ 15681 /*free(p); - we'll exit soon */
14473 } 15682 }
14474 out: 15683 out:
15684#if ENABLE_SUW32
15685 if (delayexit) {
15686 freopen("CONOUT$", "w", stdout);
15687 fputs_stdout("Press any key to exit...");
15688 _getch();
15689 }
15690#endif
14475 exitreset(); 15691 exitreset();
14476 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}". 15692 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14477 * our setjobctl(0) does not panic if tcsetpgrp fails inside it. 15693 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
@@ -14482,22 +15698,113 @@ exitshell(void)
14482 /* NOTREACHED */ 15698 /* NOTREACHED */
14483} 15699}
14484 15700
15701#if ENABLE_PLATFORM_MINGW32
15702/* We need to see if HOME is *really* unset */
15703# undef getenv
15704static void xsetenv_if_unset(const char *key, const char *value)
15705{
15706 if (!getenv(key) || getuid() == 0)
15707 xsetenv(key, value);
15708}
15709#endif
15710
14485/* Don't inline: conserve stack of caller from having our locals too */ 15711/* Don't inline: conserve stack of caller from having our locals too */
14486static NOINLINE void 15712static NOINLINE void
14487init(void) 15713init(void)
14488{ 15714{
15715#if !ENABLE_PLATFORM_MINGW32
14489 /* we will never free this */ 15716 /* we will never free this */
14490 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); 15717 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
14491 basepf.linno = 1; 15718 basepf.linno = 1;
14492 15719
14493 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 15720 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
14494 setsignal(SIGCHLD); 15721 setsignal(SIGCHLD);
15722#endif
14495 15723
14496 { 15724 {
14497 char **envp; 15725 char **envp;
14498 const char *p; 15726 const char *p;
14499 15727
14500 initvar(); 15728 initvar();
15729
15730#if ENABLE_PLATFORM_MINGW32
15731 /*
15732 * case insensitive env names from Windows world
15733 *
15734 * Some standard env names such as PATH is named Path and so on
15735 * ash itself is case sensitive, so "Path" will confuse it, as
15736 * MSVC getenv() is case insensitive.
15737 *
15738 * We may end up having both Path and PATH. Then Path will be chosen
15739 * because it appears first.
15740 */
15741 for (envp = environ; envp && *envp; envp++) {
15742 if (is_prefixed_with_case(*envp, "PATH=") &&
15743 !is_prefixed_with(*envp, "PATH=")) {
15744 break;
15745 }
15746 }
15747
15748 if (envp && *envp) {
15749 /*
15750 * If we get here it's because the environment contains a path
15751 * variable called something other than PATH. This suggests we
15752 * haven't been invoked from an earlier instance of BusyBox.
15753 */
15754 char *start, *end;
15755 struct passwd *pw;
15756
15757 for (envp = environ; envp && *envp; envp++) {
15758 if (!(end=strchr(*envp, '=')))
15759 continue;
15760
15761 /* make all variable names uppercase */
15762 for (start = *envp;start < end;start++)
15763 *start = toupper(*start);
15764
15765 /* Convert backslashes to forward slashes in value but
15766 * not if we're on Windows XP or for variables known to
15767 * cause problems */
15768 if (!winxp && !is_prefixed_with(*envp, "SYSTEMROOT=") &&
15769 !is_prefixed_with(*envp, "COMSPEC=")) {
15770 bs_to_slash(end+1);
15771 }
15772
15773 /* check for invalid characters in name */
15774 for (start = *envp;start < end;start++) {
15775 if (!isdigit(*start) && !isalpha(*start) && *start != '_') {
15776 break;
15777 }
15778 }
15779
15780 if (start != end) {
15781 /*
15782 * Make a copy of the variable, replacing invalid
15783 * characters in the name with underscores.
15784 */
15785 char *var = xstrdup(*envp);
15786
15787 for (start = var;*start != '=';start++) {
15788 if (!isdigit(*start) && !isalpha(*start)) {
15789 *start = '_';
15790 }
15791 }
15792 setvareq(var, VEXPORT|VNOSAVE);
15793 }
15794 }
15795
15796 /* Initialise some variables normally set at login, but
15797 * only if someone hasn't already set them or we're root. */
15798 pw = getpwuid(getuid());
15799 if (pw) {
15800 xsetenv_if_unset("USER", pw->pw_name);
15801 xsetenv_if_unset("USERNAME", pw->pw_name);
15802 xsetenv_if_unset("LOGNAME", pw->pw_name);
15803 xsetenv_if_unset("HOME", pw->pw_dir);
15804 }
15805 xsetenv_if_unset("SHELL", DEFAULT_SHELL);
15806 }
15807#endif
14501 for (envp = environ; envp && *envp; envp++) { 15808 for (envp = environ; envp && *envp; envp++) {
14502/* Used to have 15809/* Used to have
14503 * p = endofname(*envp); 15810 * p = endofname(*envp);
@@ -14511,7 +15818,11 @@ init(void)
14511 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this 15818 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14512 */ 15819 */
14513 if (strchr(*envp, '=')) { 15820 if (strchr(*envp, '=')) {
15821#if !ENABLE_PLATFORM_MINGW32
14514 setvareq(*envp, VEXPORT|VTEXTFIXED); 15822 setvareq(*envp, VEXPORT|VTEXTFIXED);
15823#else
15824 setvareq(*envp, VEXPORT);
15825#endif
14515 } 15826 }
14516 } 15827 }
14517 15828
@@ -14586,7 +15897,9 @@ procargs(char **argv)
14586 } 15897 }
14587 if (iflag == 2 /* no explicit -i given */ 15898 if (iflag == 2 /* no explicit -i given */
14588 && sflag == 1 /* -s given (or implied) */ 15899 && sflag == 1 /* -s given (or implied) */
15900#if !ENABLE_PLATFORM_MINGW32
14589 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */ 15901 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
15902#endif
14590 && isatty(0) && isatty(1) /* we are on tty */ 15903 && isatty(0) && isatty(1) /* we are on tty */
14591 ) { 15904 ) {
14592 iflag = 1; 15905 iflag = 1;
@@ -14606,6 +15919,9 @@ procargs(char **argv)
14606 goto setarg0; 15919 goto setarg0;
14607 } else if (!sflag) { 15920 } else if (!sflag) {
14608 setinputfile(*xargv, 0); 15921 setinputfile(*xargv, 0);
15922#if ENABLE_PLATFORM_MINGW32
15923 bs_to_slash(*xargv);
15924#endif
14609 setarg0: 15925 setarg0:
14610 arg0 = *xargv++; 15926 arg0 = *xargv++;
14611 commandname = arg0; 15927 commandname = arg0;
@@ -14629,8 +15945,10 @@ procargs(char **argv)
14629 * NB: must do it before setting up signals (in optschanged()) 15945 * NB: must do it before setting up signals (in optschanged())
14630 * and reading .profile etc (after we return from here): 15946 * and reading .profile etc (after we return from here):
14631 */ 15947 */
15948#if !ENABLE_PLATFORM_MINGW32
14632 if (iflag) 15949 if (iflag)
14633 signal(SIGHUP, SIG_DFL); 15950 signal(SIGHUP, SIG_DFL);
15951#endif
14634 15952
14635 optschanged(); 15953 optschanged();
14636 15954
@@ -14675,9 +15993,25 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14675 struct stackmark smark; 15993 struct stackmark smark;
14676 int login_sh; 15994 int login_sh;
14677 15995
15996#if ENABLE_PLATFORM_MINGW32
15997 INIT_G_memstack();
15998
15999 /* from init() */
16000 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
16001 basepf.linno = 1;
16002
16003 if (argc == 3 && !strcmp(argv[1], "--fs")) {
16004 forkshell_init(argv[2]);
16005 /* only reached in case of error */
16006 bb_error_msg_and_die("forkshell failed");
16007 }
16008#endif
16009
14678 /* Initialize global data */ 16010 /* Initialize global data */
14679 INIT_G_misc(); 16011 INIT_G_misc();
16012#if !ENABLE_PLATFORM_MINGW32
14680 INIT_G_memstack(); 16013 INIT_G_memstack();
16014#endif
14681 INIT_G_var(); 16015 INIT_G_var();
14682#if ENABLE_ASH_ALIAS 16016#if ENABLE_ASH_ALIAS
14683 INIT_G_alias(); 16017 INIT_G_alias();
@@ -14720,9 +16054,16 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14720 exception_handler = &jmploc; 16054 exception_handler = &jmploc;
14721 rootpid = getpid(); 16055 rootpid = getpid();
14722 16056
16057#if ENABLE_PLATFORM_MINGW32
16058 winxp = (argv[1] != NULL && strcmp(argv[1], "-X") == 0);
16059#endif
14723 init(); 16060 init();
14724 setstackmark(&smark); 16061 setstackmark(&smark);
14725 16062
16063#if ENABLE_PLATFORM_MINGW32
16064 SetConsoleCtrlHandler(ctrl_handler, TRUE);
16065#endif
16066
14726#if NUM_SCRIPTS > 0 16067#if NUM_SCRIPTS > 0
14727 if (argc < 0) 16068 if (argc < 0)
14728 /* Non-NULL minusc tells procargs that an embedded script is being run */ 16069 /* Non-NULL minusc tells procargs that an embedded script is being run */
@@ -14734,11 +16075,50 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14734 trace_puts_args(argv); 16075 trace_puts_args(argv);
14735#endif 16076#endif
14736 16077
16078#if ENABLE_ASH_NOCONSOLE
16079 if (noconsole)
16080 hide_console();
16081#endif
16082
16083#if ENABLE_PLATFORM_MINGW32
16084 if (dirarg) {
16085 chdir(dirarg);
16086 setpwd(NULL, 0);
16087 }
16088 else if (!login_sh && iflag) {
16089 char *cwd = getcwd(NULL, 0);
16090 if (cwd) {
16091 chdir(cwd);
16092 setpwd(NULL, 0);
16093 free(cwd);
16094 }
16095 }
16096
16097 if (title)
16098 set_title(title);
16099#endif
16100
14737 if (login_sh) { 16101 if (login_sh) {
14738 const char *hp; 16102 const char *hp;
14739 16103
16104#if ENABLE_PLATFORM_MINGW32
16105 if (!dirarg) {
16106 hp = lookupvar("HOME");
16107 if (hp == NULL || *hp == '\0')
16108 hp = xgetpwuid(getuid())->pw_dir;
16109 chdir(hp);
16110 setpwd(NULL, 0);
16111 }
16112#endif
16113
14740 state = 1; 16114 state = 1;
16115#if ENABLE_PLATFORM_MINGW32
16116 hp = xasprintf("%s/etc/profile", get_system_drive() ?: "");
16117 read_profile(hp);
16118 free((void *)hp);
16119#else
14741 read_profile("/etc/profile"); 16120 read_profile("/etc/profile");
16121#endif
14742 state1: 16122 state1:
14743 state = 2; 16123 state = 2;
14744 hp = lookupvar("HOME"); 16124 hp = lookupvar("HOME");
@@ -14748,9 +16128,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14748 state2: 16128 state2:
14749 state = 3; 16129 state = 3;
14750 if (iflag 16130 if (iflag
16131#if ENABLE_PLATFORM_POSIX
14751#ifndef linux 16132#ifndef linux
14752 && getuid() == geteuid() && getgid() == getegid() 16133 && getuid() == geteuid() && getgid() == getegid()
14753#endif 16134#endif
16135#endif
14754 ) { 16136 ) {
14755 const char *shinit = lookupvar("ENV"); 16137 const char *shinit = lookupvar("ENV");
14756 if (shinit != NULL && *shinit != '\0') 16138 if (shinit != NULL && *shinit != '\0')
@@ -14775,7 +16157,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14775 // ash -sc 'echo $-' 16157 // ash -sc 'echo $-'
14776 // continue reading input from stdin after running 'echo'. 16158 // continue reading input from stdin after running 'echo'.
14777 // bash does not do this: it prints "hBcs" and exits. 16159 // bash does not do this: it prints "hBcs" and exits.
16160#if !ENABLE_PLATFORM_MINGW32
14778 evalstring(minusc, EV_EXIT); 16161 evalstring(minusc, EV_EXIT);
16162#else
16163 evalstring(minusc, sflag ? 0 : EV_EXIT);
16164#endif
14779 } 16165 }
14780 16166
14781 if (sflag || minusc == NULL) { 16167 if (sflag || minusc == NULL) {
@@ -14818,6 +16204,960 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14818 /* NOTREACHED */ 16204 /* NOTREACHED */
14819} 16205}
14820 16206
16207#if ENABLE_PLATFORM_MINGW32
16208static void
16209forkshell_openhere(struct forkshell *fs)
16210{
16211 const char *p = fs->path;
16212 size_t len = strlen(p);
16213 int pip[2];
16214
16215 pip[0] = fs->fd[0];
16216 pip[1] = fs->fd[1];
16217
16218 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16219
16220 close(pip[0]);
16221 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
16222 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
16223 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
16224 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
16225 signal(SIGPIPE, SIG_DFL);
16226 xwrite(pip[1], p, len);
16227 _exit_SUCCESS();
16228}
16229
16230static void
16231forkshell_evalbackcmd(struct forkshell *fs)
16232{
16233#if BASH_PROCESS_SUBST
16234 /* determine end of pipe used by parent (ip) and child (ic) */
16235 const int ctl = fs->fd[2];
16236 const int ip = (ctl == CTLTOPROC);
16237 const int ic = !(ctl == CTLTOPROC);
16238#else
16239 const int ip = 0;
16240 const int ic = 1;
16241#endif
16242 union node *n = fs->n;
16243 int pip[2];
16244
16245 pip[ip] = fs->fd[ip];
16246 pip[ic] = fs->fd[ic];
16247
16248 FORCE_INT_ON;
16249 close(pip[ip]);
16250 if (pip[ic] != ic) {
16251 /*close(ic);*/
16252 dup2_or_raise(pip[ic], ic);
16253 close(pip[ic]);
16254 }
16255 eflag = 0;
16256 ifsfree();
16257 evaltreenr(n, EV_EXIT);
16258 /* NOTREACHED */
16259}
16260
16261static void
16262forkshell_evalsubshell(struct forkshell *fs)
16263{
16264 union node *n = fs->n;
16265 int flags = fs->flags;
16266
16267 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16268 INT_ON;
16269 flags |= EV_EXIT;
16270 if (fs->mode)
16271 flags &= ~EV_TESTED;
16272 expredir(n->nredir.redirect);
16273 redirect(n->nredir.redirect, 0);
16274 evaltreenr(n->nredir.n, flags);
16275 /* never returns */
16276}
16277
16278static void
16279forkshell_evalpipe(struct forkshell *fs)
16280{
16281 union node *n = fs->n;
16282 int flags = fs->flags;
16283 int prevfd = fs->fd[2];
16284 int pip[2] = {fs->fd[0], fs->fd[1]};
16285
16286 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16287 INT_ON;
16288 if (pip[1] >= 0) {
16289 close(pip[0]);
16290 }
16291 if (prevfd > 0) {
16292 dup2(prevfd, 0);
16293 close(prevfd);
16294 }
16295 if (pip[1] > 1) {
16296 dup2(pip[1], 1);
16297 close(pip[1]);
16298 }
16299 evaltreenr(n, flags);
16300}
16301
16302static void
16303forkshell_shellexec(struct forkshell *fs)
16304{
16305 int idx = fs->fd[0];
16306 char **argv = fs->argv;
16307 char *path = fs->path;
16308
16309 FORCE_INT_ON;
16310 shellexec(argv[0], argv, path, idx);
16311}
16312
16313static void
16314forkshell_child(struct forkshell *fs)
16315{
16316 switch ( fs->fpid ) {
16317 case FS_OPENHERE:
16318 forkshell_openhere(fs);
16319 break;
16320 case FS_EVALBACKCMD:
16321 forkshell_evalbackcmd(fs);
16322 break;
16323 case FS_EVALSUBSHELL:
16324 forkshell_evalsubshell(fs);
16325 break;
16326 case FS_EVALPIPE:
16327 forkshell_evalpipe(fs);
16328 break;
16329 case FS_SHELLEXEC:
16330 forkshell_shellexec(fs);
16331 break;
16332 }
16333}
16334
16335/*
16336 * Reinitialise the builtin environment variables in varinit. Their
16337 * current settings have been copied from the parent in vartab. Look
16338 * these up using the names from varinit_data, copy the details from
16339 * vartab to varinit and replace the old copy in vartab with the new
16340 * one in varinit.
16341 *
16342 * Also reinitialise the function pointers and line number variable.
16343 */
16344static void
16345reinitvar(void)
16346{
16347 int i;
16348 const char *name;
16349 struct var **vpp, **old;
16350
16351 for (i=0; i<ARRAY_SIZE(varinit); ++i) {
16352 if (i == LINENO_INDEX)
16353 name = "LINENO=";
16354 else if (i == FUNCNAME_INDEX)
16355 name = "FUNCNAME=";
16356 else
16357 name = varinit_data[i].var_text;
16358 vpp = hashvar(name);
16359 if ( (old=findvar(vpp, name)) != NULL ) {
16360 varinit[i] = **old;
16361 *old = varinit+i;
16362 }
16363 varinit[i].var_func = varinit_data[i].var_func;
16364 }
16365 vlineno.var_text = linenovar;
16366 vfuncname.var_text = funcnamevar;
16367}
16368
16369static void
16370spawn_forkshell(struct forkshell *fs, struct job *jp, union node *n, int mode)
16371{
16372 struct forkshell *new;
16373 char buf[32];
16374 const char *argv[] = { "sh", "--fs", NULL, NULL };
16375 intptr_t ret;
16376
16377 new = forkshell_prepare(fs);
16378 if (new == NULL)
16379 goto fail;
16380
16381 new->mode = mode;
16382 new->nprocs = jp == NULL ? 0 : jp->nprocs;
16383 sprintf(buf, "%p", new->hMapFile);
16384 argv[2] = buf;
16385 ret = spawnve(P_NOWAIT, bb_busybox_exec_path, (char *const *)argv, NULL);
16386 CloseHandle(new->hMapFile);
16387 UnmapViewOfFile(new);
16388 if (ret == -1) {
16389 fail:
16390 if (jp)
16391 freejob(jp);
16392 ash_msg_and_raise_error("unable to spawn shell");
16393 }
16394 forkparent(jp, n, mode, (HANDLE)ret);
16395}
16396
16397/*
16398 * forkshell_prepare() and friends
16399 *
16400 * The sequence is as follows:
16401 * - funcblocksize is initialized
16402 * - forkshell_size(fs) is called to calculate the exact memory needed
16403 * - a new struct is allocated
16404 * - funcblock, funcstring, relocate are initialized from the new block
16405 * - forkshell_copy(fs) is called to copy recursively everything over
16406 * it will record all relocations along the way
16407 *
16408 * When this memory is mapped elsewhere, pointer fixup will be needed
16409 */
16410
16411/* redefine without test that fs_size is nonzero */
16412#undef SAVE_PTR
16413#undef SAVE_PTR2
16414#undef SAVE_PTR3
16415#define SAVE_PTR(dst,note,flag) {MARK_PTR(dst,flag); ANNOT(dst,note);}
16416
16417static int align_len(const char *s)
16418{
16419 return s ? SHELL_ALIGN(strlen(s)+1) : 0;
16420}
16421
16422struct datasize {
16423 int funcblocksize;
16424 int funcstringsize;
16425};
16426
16427#define SLIST_SIZE_BEGIN(name,type) \
16428static struct datasize \
16429name(struct datasize ds, type *p) \
16430{ \
16431 while (p) { \
16432 ds.funcblocksize += sizeof(type);
16433 /* do something here with p */
16434#define SLIST_SIZE_END() \
16435 p = p->next; \
16436 } \
16437 return ds; \
16438}
16439
16440#define SLIST_COPY_BEGIN(name,type) \
16441static type * \
16442name(type *vp) \
16443{ \
16444 type *start; \
16445 type **vpp; \
16446 vpp = &start; \
16447 while (vp) { \
16448 *vpp = funcblock; \
16449 funcblock = (char *) funcblock + sizeof(type);
16450 /* do something here with vpp and vp */
16451#define SLIST_COPY_END() \
16452 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE); \
16453 vp = vp->next; \
16454 vpp = &(*vpp)->next; \
16455 } \
16456 *vpp = NULL; \
16457 return start; \
16458}
16459
16460/*
16461 * struct var
16462 */
16463SLIST_SIZE_BEGIN(var_size,struct var)
16464ds.funcstringsize += align_len(p->var_text);
16465SLIST_SIZE_END()
16466
16467SLIST_COPY_BEGIN(var_copy,struct var)
16468(*vpp)->var_text = nodeckstrdup(vp->var_text);
16469(*vpp)->flags = vp->flags;
16470(*vpp)->var_func = NULL;
16471SAVE_PTR((*vpp)->var_text, xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"), FREE);
16472SLIST_COPY_END()
16473
16474/*
16475 * struct tblentry
16476 */
16477static struct datasize
16478tblentry_size(struct datasize ds, struct tblentry *tep)
16479{
16480 while (tep) {
16481 ds.funcblocksize += sizeof(struct tblentry) + align_len(tep->cmdname);
16482 /* CMDBUILTIN, e->param.cmd needs no pointer relocation */
16483 if (tep->cmdtype == CMDFUNCTION) {
16484 ds.funcblocksize += offsetof(struct funcnode, n);
16485 ds.funcblocksize = calcsize(ds.funcblocksize, &tep->param.func->n);
16486 }
16487 tep = tep->next;
16488 }
16489 return ds;
16490}
16491
16492static struct tblentry *
16493tblentry_copy(struct tblentry *tep)
16494{
16495 struct tblentry *start;
16496 struct tblentry **newp;
16497 int size;
16498
16499 newp = &start;
16500 while (tep) {
16501 *newp = funcblock;
16502 size = sizeof(struct tblentry) + align_len(tep->cmdname);
16503
16504 funcblock = (char *) funcblock + size;
16505 memcpy(*newp, tep, sizeof(struct tblentry)+strlen(tep->cmdname));
16506 switch (tep->cmdtype) {
16507 case CMDBUILTIN:
16508 /* Save index of builtin, not pointer; fixed by forkshell_init() */
16509 (*newp)->param.index = tep->param.cmd - builtintab;
16510 break;
16511 case CMDFUNCTION:
16512 (*newp)->param.func = funcblock;
16513 funcblock = (char *) funcblock + offsetof(struct funcnode, n);
16514 copynode(&tep->param.func->n);
16515 SAVE_PTR((*newp)->param.func, "param.func", NO_FREE);
16516 break;
16517 default:
16518 break;
16519 }
16520 SAVE_PTR((*newp)->next, xasprintf("cmdname '%s'", tep->cmdname), FREE);
16521 tep = tep->next;
16522 newp = &(*newp)->next;
16523 }
16524 *newp = NULL;
16525 return start;
16526}
16527
16528static struct datasize
16529cmdtable_size(struct datasize ds)
16530{
16531 int i;
16532 ds.funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE;
16533 for (i = 0; i < CMDTABLESIZE; i++)
16534 ds = tblentry_size(ds, cmdtable[i]);
16535 return ds;
16536}
16537
16538static struct tblentry **
16539cmdtable_copy(void)
16540{
16541 struct tblentry **new = funcblock;
16542 int i;
16543
16544 funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE;
16545 for (i = 0; i < CMDTABLESIZE; i++) {
16546 new[i] = tblentry_copy(cmdtable[i]);
16547 SAVE_PTR(new[i], xasprintf("cmdtable[%d]", i), FREE);
16548 }
16549 return new;
16550}
16551
16552#if ENABLE_ASH_ALIAS
16553/*
16554 * struct alias
16555 */
16556SLIST_SIZE_BEGIN(alias_size,struct alias)
16557ds.funcstringsize += align_len(p->name);
16558ds.funcstringsize += align_len(p->val);
16559SLIST_SIZE_END()
16560
16561SLIST_COPY_BEGIN(alias_copy,struct alias)
16562(*vpp)->name = nodeckstrdup(vp->name);
16563(*vpp)->val = nodeckstrdup(vp->val);
16564(*vpp)->flag = vp->flag;
16565SAVE_PTR((*vpp)->name, xasprintf("(*vpp)->name '%s'", vp->name ?: "NULL"), FREE);
16566SAVE_PTR((*vpp)->val, xasprintf("(*vpp)->val '%s'", vp->val ?: "NULL"), FREE);
16567SLIST_COPY_END()
16568
16569static struct datasize
16570atab_size(struct datasize ds)
16571{
16572 int i;
16573 ds.funcblocksize += sizeof(struct alias *)*ATABSIZE;
16574 for (i = 0; i < ATABSIZE; i++)
16575 ds = alias_size(ds, atab[i]);
16576 return ds;
16577}
16578
16579static struct alias **
16580atab_copy(void)
16581{
16582 struct alias **new = funcblock;
16583 int i;
16584
16585 funcblock = (char *) funcblock + sizeof(struct alias *)*ATABSIZE;
16586 for (i = 0; i < ATABSIZE; i++) {
16587 new[i] = alias_copy(atab[i]);
16588 SAVE_PTR(new[i], xasprintf("atab[%d]", i), FREE);
16589 }
16590 return new;
16591}
16592#endif
16593
16594/*
16595 * char **
16596 */
16597static struct datasize
16598argv_size(struct datasize ds, char **p)
16599{
16600 if (p) {
16601 while (*p) {
16602 ds.funcblocksize += sizeof(char *);
16603 ds.funcstringsize += align_len(*p);
16604 p++;
16605 }
16606 ds.funcblocksize += sizeof(char *);
16607 }
16608 return ds;
16609}
16610
16611static char **
16612argv_copy(char **p)
16613{
16614 char **new, **start = funcblock;
16615#if FORKSHELL_DEBUG
16616 int i = 0;
16617#endif
16618
16619 if (p) {
16620 while (*p) {
16621 new = funcblock;
16622 funcblock = (char *) funcblock + sizeof(char *);
16623 *new = nodeckstrdup(*p);
16624 SAVE_PTR(*new, xasprintf("argv[%d] '%s'", i++, *p), FREE);
16625 p++;
16626 }
16627 new = funcblock;
16628 funcblock = (char *) funcblock + sizeof(char *);
16629 *new = NULL;
16630 return start;
16631 }
16632 return NULL;
16633}
16634
16635#if MAX_HISTORY
16636static struct datasize
16637history_size(struct datasize ds)
16638{
16639 int i;
16640 line_input_t *st = line_input_state;
16641
16642 ds.funcblocksize += sizeof(char *) * st->cnt_history;
16643 for (i = 0; i < st->cnt_history; i++) {
16644 ds.funcstringsize += align_len(st->history[i]);
16645 }
16646 return ds;
16647}
16648
16649static char **
16650history_copy(void)
16651{
16652 line_input_t *st = line_input_state;
16653 char **new = funcblock;
16654 int i;
16655
16656 funcblock = (char *)funcblock + sizeof(char *) * st->cnt_history;
16657 for (i = 0; i < st->cnt_history; i++) {
16658 new[i] = nodeckstrdup(st->history[i]);
16659 SAVE_PTR(new[i],
16660 xasprintf("history[%d] '%s'", i, st->history[i]), FREE);
16661 }
16662 return new;
16663}
16664#endif
16665
16666/*
16667 * struct redirtab
16668 */
16669static int
16670redirtab_size(int funcblocksize, struct redirtab *rdtp)
16671{
16672 while (rdtp) {
16673 funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
16674 rdtp = rdtp->next;
16675 }
16676 return funcblocksize;
16677}
16678
16679static struct redirtab *
16680redirtab_copy(struct redirtab *rdtp)
16681{
16682 struct redirtab *start;
16683 struct redirtab **vpp;
16684
16685 vpp = &start;
16686 while (rdtp) {
16687 int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
16688 *vpp = funcblock;
16689 funcblock = (char *) funcblock + size;
16690 memcpy(*vpp, rdtp, size);
16691 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE);
16692 rdtp = rdtp->next;
16693 vpp = &(*vpp)->next;
16694 }
16695 *vpp = NULL;
16696 return start;
16697}
16698
16699static struct datasize
16700globals_var_size(struct datasize ds)
16701{
16702 int i;
16703
16704 ds.funcblocksize += sizeof(struct globals_var);
16705 ds.funcstringsize += align_len(funcname);
16706 ds = argv_size(ds, shellparam.p);
16707 ds.funcblocksize = redirtab_size(ds.funcblocksize, redirlist);
16708 for (i = 0; i < VTABSIZE; i++)
16709 ds = var_size(ds, vartab[i]);
16710 return ds;
16711}
16712
16713#undef funcname
16714#undef shellparam
16715#undef redirlist
16716#undef vartab
16717static struct globals_var *
16718globals_var_copy(void)
16719{
16720 int i;
16721 struct globals_var *gvp, *new;
16722
16723 gvp = ash_ptr_to_globals_var;
16724 new = funcblock;
16725 funcblock = (char *) funcblock + sizeof(struct globals_var);
16726 memcpy(new, gvp, sizeof(struct globals_var));
16727
16728 new->funcname = nodeckstrdup(gvp->funcname);
16729 SAVE_PTR(new->funcname, xasprintf("funcname '%s'", gvp->funcname ?: "NULL"), FREE);
16730
16731 /* shparam */
16732 new->shellparam.malloced = 0;
16733 new->shellparam.p = argv_copy(gvp->shellparam.p);
16734 SAVE_PTR(new->shellparam.p, "shellparam.p", NO_FREE);
16735
16736 new->redirlist = redirtab_copy(gvp->redirlist);
16737 SAVE_PTR(new->redirlist, "redirlist", NO_FREE);
16738
16739 for (i = 0; i < VTABSIZE; i++) {
16740 new->vartab[i] = var_copy(gvp->vartab[i]);
16741 SAVE_PTR(new->vartab[i], xasprintf("vartab[%d]", i), FREE);
16742 }
16743
16744 return new;
16745}
16746
16747static struct datasize
16748globals_misc_size(struct datasize ds)
16749{
16750 ds.funcblocksize += sizeof(struct globals_misc);
16751 ds.funcstringsize += align_len(minusc);
16752 if (curdir != nullstr)
16753 ds.funcstringsize += align_len(curdir);
16754 if (physdir != nullstr)
16755 ds.funcstringsize += align_len(physdir);
16756 ds.funcstringsize += align_len(arg0);
16757 ds.funcstringsize += align_len(commandname);
16758 return ds;
16759}
16760
16761#undef minusc
16762#undef curdir
16763#undef physdir
16764#undef arg0
16765#undef commandname
16766#undef nullstr
16767static struct globals_misc *
16768globals_misc_copy(void)
16769{
16770 struct globals_misc *p = ash_ptr_to_globals_misc;
16771 struct globals_misc *new = funcblock;
16772
16773 funcblock = (char *) funcblock + sizeof(struct globals_misc);
16774 memcpy(new, p, sizeof(struct globals_misc));
16775
16776 new->minusc = nodeckstrdup(p->minusc);
16777 new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr;
16778 new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr;
16779 new->arg0 = nodeckstrdup(p->arg0);
16780 new->commandname = nodeckstrdup(p->commandname);
16781 SAVE_PTR(new->minusc, xasprintf("minusc '%s'", p->minusc ?: "NULL"), FREE);
16782 SAVE_PTR(new->curdir,
16783 xasprintf("curdir '%s'", new->curdir ?: "NULL"), FREE);
16784 SAVE_PTR(new->physdir,
16785 xasprintf("physdir '%s'", new->physdir ?: "NULL"), FREE);
16786 SAVE_PTR(new->arg0, xasprintf("arg0 '%s'", p->arg0 ?: "NULL"), FREE);
16787 SAVE_PTR(new->commandname,
16788 xasprintf("commandname '%s'", p->commandname ?: "NULL"), FREE);
16789 return new;
16790}
16791
16792static struct datasize
16793forkshell_size(struct forkshell *fs)
16794{
16795 struct datasize ds = {0, 0};
16796
16797 ds.funcstringsize += align_len(fs->path);
16798 if (fs->fpid == FS_OPENHERE)
16799 return ds;
16800
16801 ds = globals_var_size(ds);
16802 ds = globals_misc_size(ds);
16803 ds = cmdtable_size(ds);
16804
16805 ds.funcblocksize = calcsize(ds.funcblocksize, fs->n);
16806 ds = argv_size(ds, fs->argv);
16807
16808 if ((ENABLE_ASH_ALIAS || MAX_HISTORY) && fs->fpid != FS_SHELLEXEC) {
16809#if ENABLE_ASH_ALIAS
16810 ds = atab_size(ds);
16811#endif
16812#if MAX_HISTORY
16813 if (line_input_state)
16814 ds = history_size(ds);
16815#endif
16816 }
16817 return ds;
16818}
16819
16820static void
16821forkshell_copy(struct forkshell *fs, struct forkshell *new)
16822{
16823 memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */
16824
16825 new->path = nodeckstrdup(fs->path);
16826 SAVE_PTR(new->path, xasprintf("path '%s'", fs->path ?: "NULL"), FREE);
16827 if (fs->fpid == FS_OPENHERE)
16828 return;
16829
16830 new->gvp = globals_var_copy();
16831 new->gmp = globals_misc_copy();
16832 new->cmdtable = cmdtable_copy();
16833 SAVE_PTR(new->gvp, "gvp", NO_FREE);
16834 SAVE_PTR(new->gmp, "gmp", NO_FREE);
16835 SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE);
16836
16837 new->n = copynode(fs->n);
16838 new->argv = argv_copy(fs->argv);
16839 SAVE_PTR(new->n, "n", NO_FREE);
16840 SAVE_PTR(new->argv, "argv", NO_FREE);
16841
16842 if ((ENABLE_ASH_ALIAS || MAX_HISTORY) && fs->fpid != FS_SHELLEXEC) {
16843#if ENABLE_ASH_ALIAS
16844 new->atab = atab_copy();
16845 SAVE_PTR(new->atab, "atab", NO_FREE);
16846#endif
16847#if MAX_HISTORY
16848 if (line_input_state) {
16849 new->history = history_copy();
16850 SAVE_PTR(new->history, "history", NO_FREE);
16851 new->cnt_history = line_input_state->cnt_history;
16852 }
16853#endif
16854 }
16855}
16856
16857#if FORKSHELL_DEBUG
16858/* fp and notes can each be NULL */
16859#define NUM_BLOCKS 7
16860static void
16861forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
16862{
16863 FILE *fp;
16864 void *lfuncblock;
16865 char *lfuncstring;
16866 char *lrelocate;
16867 char *s;
16868 int count, i, total;
16869 int size[NUM_BLOCKS];
16870 char *lptr[NUM_BLOCKS+1];
16871 enum {GVP, GMP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, FUNCSTRING};
16872 const char *fsname[] = {
16873 "FS_OPENHERE",
16874 "FS_EVALBACKCMD",
16875 "FS_EVALSUBSHELL",
16876 "FS_EVALPIPE",
16877 "FS_SHELLEXEC"
16878 };
16879
16880 if (fp0 != NULL) {
16881 fp = fp0;
16882 }
16883 else {
16884 char name[64];
16885 static int num = 0;
16886
16887 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
16888 if ((fp=fopen(name, "w")) == NULL)
16889 return;
16890 }
16891
16892 total = sizeof(struct forkshell) + fs->funcblocksize +
16893 fs->funcstringsize + fs->relocatesize;
16894 fprintf(fp, "total size %6d = %d + %d + %d + %d = %d\n",
16895 fs->size + fs->relocatesize,
16896 (int)sizeof(struct forkshell), fs->funcblocksize,
16897 fs->funcstringsize, fs->relocatesize, total);
16898
16899 lfuncblock = (char *)(fs + 1);
16900 lfuncstring = (char *)lfuncblock + fs->funcblocksize;
16901 lrelocate = (char *)lfuncstring + fs->funcstringsize;
16902
16903 /* funcblocksize is zero for FS_OPENHERE */
16904 if (fs->funcblocksize != 0) {
16905 /* Depending on the configuration and the type of forkshell
16906 * some items may not be present. */
16907 lptr[FUNCSTRING] = lfuncstring;
16908#if MAX_HISTORY
16909 lptr[HISTORY] = fs->history ? (char *)fs->history : lptr[FUNCSTRING];
16910#else
16911 lptr[HISTORY] = lptr[FUNCSTRING];
16912#endif
16913 lptr[ATAB] = IF_ASH_ALIAS(fs->atab ? (char *)fs->atab :) lptr[HISTORY];
16914 lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB];
16915 lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV];
16916 lptr[CMDTABLE] = (char *)fs->cmdtable;
16917 lptr[GMP] = (char *)fs->gmp;
16918 lptr[GVP] = (char *)fs->gvp;
16919
16920 fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize);
16921 total = 0;
16922 for (i=0; i<NUM_BLOCKS; ++i) {
16923 size[i] = (int)(lptr[i+1] - lptr[i]);
16924 total += size[i];
16925 fprintf(fp, "%d %c ", size[i], i == NUM_BLOCKS - 1 ? '=' : '+');
16926 }
16927 fprintf(fp, "%d\n\n", total);
16928 }
16929 else {
16930 fprintf(fp, "\n");
16931 }
16932
16933 fprintf(fp, "%s\n\n", fsname[fs->fpid]);
16934 fprintf(fp, "--- relocate ---\n");
16935 count = 0;
16936 for (i = 0; i < fs->relocatesize; ++i) {
16937 if (lrelocate[i]) {
16938 char **ptr = (char **)((char *)fs + i);
16939 fprintf(fp, "%p %p %s\n", ptr, *ptr,
16940 notes && notes[i] ? notes[i] : "");
16941 ++count;
16942 }
16943 }
16944 fprintf(fp, "--- %d relocations ---\n\n", count);
16945
16946 fprintf(fp, "--- funcstring ---\n");
16947 count = 0;
16948 s = lfuncstring;
16949 while (s-lfuncstring < fs->funcstringsize) {
16950 if (!*s) {
16951 ++s;
16952 continue;
16953 }
16954 fprintf(fp, "%p '%s'\n", s, s);
16955 s += strlen(s)+1;
16956 ++count;
16957 }
16958 fprintf(fp, "--- %d strings ---\n", count);
16959
16960 if (fp0 == NULL)
16961 fclose(fp);
16962}
16963#endif
16964
16965static struct forkshell *
16966forkshell_prepare(struct forkshell *fs)
16967{
16968 struct forkshell *new;
16969 struct datasize ds;
16970 int size, relocatesize;
16971 HANDLE h;
16972 SECURITY_ATTRIBUTES sa;
16973#if FORKSHELL_DEBUG
16974 char *relocate;
16975 char name[64];
16976 FILE *fp;
16977 static int num = 0;
16978#endif
16979
16980 /* calculate size of structure, funcblock and funcstring */
16981 ds = forkshell_size(fs);
16982 size = sizeof(struct forkshell) + ds.funcblocksize + ds.funcstringsize;
16983 relocatesize = sizeof(struct forkshell) + ds.funcblocksize;
16984
16985 /* Allocate shared memory region */
16986 memset(&sa, 0, sizeof(sa));
16987 sa.nLength = sizeof(sa);
16988 sa.lpSecurityDescriptor = NULL;
16989 sa.bInheritHandle = TRUE;
16990 h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0,
16991 size+relocatesize, NULL);
16992
16993 /* Initialise pointers */
16994 new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
16995 if (new == NULL)
16996 return NULL;
16997 fs_size = size;
16998 funcblock = (char *)(new + 1);
16999 funcstring_end = (char *)new + size;
17000#if FORKSHELL_DEBUG
17001 fs_start = new;
17002 relocate = (char *)new + size;
17003 annot = (const char **)xzalloc(sizeof(char *)*relocatesize);
17004#endif
17005
17006 /* Now pack them all */
17007 forkshell_copy(fs, new);
17008
17009 /* Finish it up */
17010 new->size = size;
17011 new->relocatesize = relocatesize;
17012 new->old_base = (char *)new;
17013 new->hMapFile = h;
17014#if FORKSHELL_DEBUG
17015 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
17016 if ((fp=fopen(name, "w")) != NULL) {
17017 int i;
17018
17019 new->funcblocksize = (char *)funcblock - (char *)(new + 1);
17020 new->funcstringsize = (char *)new + size - funcstring_end;
17021
17022 /* perform some sanity checks on pointers */
17023 fprintf(fp, "forkshell %p %6d\n", new, (int)sizeof(*new));
17024 fprintf(fp, "funcblock %p %6d\n", new+1, new->funcblocksize);
17025 fprintf(fp, "funcstring %p %6d\n", funcstring_end,
17026 new->funcstringsize);
17027 if ((char *)funcblock != funcstring_end)
17028 fprintf(fp, " funcstring != end funcblock + 1 %p\n", funcblock);
17029 fprintf(fp, "relocate %p %6d\n\n", relocate, new->relocatesize);
17030
17031 forkshell_print(fp, new, annot);
17032
17033 for (i = 0; i < relocatesize; ++i) {
17034 /* check relocations are only present for structure and funcblock */
17035 if (i >= sizeof(*new)+new->funcblocksize && annot[i] != NULL) {
17036 fprintf(fp, "\nnon-NULL annotation at offset %d (> %d) %s\n",
17037 i, (int)sizeof(*new)+new->funcblocksize, annot[i]);
17038 break;
17039 }
17040 if (relocate[i] == FREE) {
17041 free((void *)annot[i]);
17042 }
17043 }
17044 free(annot);
17045 annot = NULL;
17046 fclose(fp);
17047 }
17048#endif
17049 return new;
17050}
17051
17052#undef trap
17053#undef trap_ptr
17054static void
17055forkshell_init(const char *idstr)
17056{
17057 struct forkshell *fs;
17058 void *map_handle;
17059 HANDLE h;
17060 struct globals_var **gvpp;
17061 struct globals_misc **gmpp;
17062 int i;
17063 char **ptr;
17064 char *lrelocate;
17065 struct jmploc jmploc;
17066
17067 if (sscanf(idstr, "%p", &map_handle) != 1)
17068 return;
17069
17070 h = (HANDLE)map_handle;
17071 fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
17072 if (!fs)
17073 return;
17074
17075 /* this memory can't be freed */
17076 sticky_mem_start = fs;
17077 sticky_mem_end = (char *) fs + fs->size;
17078
17079 /* pointer fixup */
17080 lrelocate = (char *)fs + fs->size;
17081 for (i = 0; i < fs->relocatesize; i++) {
17082 if (lrelocate[i]) {
17083 ptr = (char **)((char *)fs + i);
17084 if (*ptr)
17085 *ptr = (char *)fs + (*ptr - fs->old_base);
17086 }
17087 }
17088
17089 if (fs->fpid == FS_OPENHERE)
17090 goto end;
17091
17092 /* Now fix up stuff that can't be transferred */
17093 for (i = 0; i < CMDTABLESIZE; i++) {
17094 struct tblentry *e = fs->cmdtable[i];
17095 while (e) {
17096 if (e->cmdtype == CMDBUILTIN)
17097 e->param.cmd = builtintab + e->param.index;
17098 e = e->next;
17099 }
17100 }
17101 memset(fs->gmp->trap, 0, sizeof(fs->gmp->trap[0])*NSIG);
17102 /* fs->gmp->trap_ptr = fs->gmp->trap; */
17103
17104 /* Set global variables */
17105 gvpp = (struct globals_var **)&ash_ptr_to_globals_var;
17106 *gvpp = fs->gvp;
17107 gmpp = (struct globals_misc **)&ash_ptr_to_globals_misc;
17108 *gmpp = fs->gmp;
17109 cmdtable = fs->cmdtable;
17110#if ENABLE_ASH_ALIAS
17111 atab = fs->atab; /* will be NULL for FS_SHELLEXEC */
17112#endif
17113#if MAX_HISTORY
17114 if (fs->cnt_history) {
17115 line_input_state = new_line_input_t(FOR_SHELL);
17116 line_input_state->cnt_history = fs->cnt_history;
17117 for (i = 0; i < line_input_state->cnt_history; i++)
17118 line_input_state->history[i] = fs->history[i];
17119 }
17120#endif
17121
17122 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
17123
17124 reinitvar();
17125
17126 if (setjmp(jmploc.loc)) {
17127 exitreset();
17128 exitshell();
17129 }
17130 exception_handler = &jmploc;
17131
17132 shlvl++;
17133 if (fs->mode == FORK_BG) {
17134 SetConsoleCtrlHandler(NULL, TRUE);
17135 if (fs->nprocs == 0) {
17136 close(0);
17137 if (open(bb_dev_null, O_RDONLY) != 0)
17138 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
17139 }
17140 }
17141 else {
17142 SetConsoleCtrlHandler(ctrl_handler, TRUE);
17143 }
17144#if JOBS_WIN32
17145 /* do job control only in root shell */
17146 doing_jobctl = 0;
17147#endif
17148 end:
17149 forkshell_child(fs);
17150}
17151
17152#undef free
17153static void
17154sticky_free(void *base)
17155{
17156 if (base >= sticky_mem_start && base < sticky_mem_end)
17157 return;
17158 free(base);
17159}
17160#endif
14821 17161
14822/*- 17162/*-
14823 * Copyright (c) 1989, 1991, 1993, 1994 17163 * Copyright (c) 1989, 1991, 1993, 1994
diff --git a/shell/math.h b/shell/math.h
index 41ef6e8df..a3fe51b6b 100644
--- a/shell/math.h
+++ b/shell/math.h
@@ -65,7 +65,7 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
65 65
66#if ENABLE_FEATURE_SH_MATH_64 66#if ENABLE_FEATURE_SH_MATH_64
67typedef long long arith_t; 67typedef long long arith_t;
68# define ARITH_FMT "%lld" 68#define ARITH_FMT "%"LL_FMT"d"
69#else 69#else
70typedef long arith_t; 70typedef long arith_t;
71# define ARITH_FMT "%ld" 71# define ARITH_FMT "%ld"
diff --git a/shell/random.c b/shell/random.c
index 56c7c5a3c..ffe0cc937 100644
--- a/shell/random.c
+++ b/shell/random.c
@@ -19,6 +19,17 @@
19# include "random.h" 19# include "random.h"
20# define RAND_BASH_MASK 0x7fff 20# define RAND_BASH_MASK 0x7fff
21 21
22# if ENABLE_FEATURE_PRNG_SHELL
23uint32_t FAST_FUNC
24next_random(random_t *rnd)
25{
26 return full_random(rnd) & RAND_BASH_MASK;
27}
28# undef RAND_BASH_MASK
29# define RAND_BASH_MASK 0xffffffff
30# define next_random full_random
31# endif
32
22#else 33#else
23# include <stdint.h> 34# include <stdint.h>
24# include <unistd.h> 35# include <unistd.h>
diff --git a/shell/random.h b/shell/random.h
index c4eb44c13..75fe0f69f 100644
--- a/shell/random.h
+++ b/shell/random.h
@@ -35,6 +35,9 @@ typedef struct random_t {
35 ((rnd)->galois_LFSR = 0) 35 ((rnd)->galois_LFSR = 0)
36 36
37uint32_t next_random(random_t *rnd) FAST_FUNC; 37uint32_t next_random(random_t *rnd) FAST_FUNC;
38#if ENABLE_FEATURE_PRNG_SHELL
39uint32_t full_random(random_t *rnd) FAST_FUNC;
40#endif
38 41
39POP_SAVED_FUNCTION_VISIBILITY 42POP_SAVED_FUNCTION_VISIBILITY
40 43
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 13163acdf..716d3aebb 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -43,7 +43,9 @@ shell_builtin_read(struct builtin_read_params *params)
43 char **pp; 43 char **pp;
44 char *buffer; 44 char *buffer;
45 char delim; 45 char delim;
46#if !ENABLE_PLATFORM_MINGW32
46 struct termios tty, old_tty; 47 struct termios tty, old_tty;
48#endif
47 const char *retval; 49 const char *retval;
48 int bufpos; /* need to be able to hold -1 */ 50 int bufpos; /* need to be able to hold -1 */
49 int startword; 51 int startword;
@@ -139,6 +141,7 @@ shell_builtin_read(struct builtin_read_params *params)
139 ifs = defifs; 141 ifs = defifs;
140 142
141 read_flags = params->read_flags; 143 read_flags = params->read_flags;
144#if !ENABLE_PLATFORM_MINGW32
142 if (nchars || (read_flags & BUILTIN_READ_SILENT)) { 145 if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
143 tcgetattr(fd, &tty); 146 tcgetattr(fd, &tty);
144 old_tty = tty; 147 old_tty = tty;
@@ -161,6 +164,7 @@ shell_builtin_read(struct builtin_read_params *params)
161 * Ignoring, it's harmless. */ 164 * Ignoring, it's harmless. */
162 tcsetattr(fd, TCSANOW, &tty); 165 tcsetattr(fd, TCSANOW, &tty);
163 } 166 }
167#endif
164 168
165 retval = (const char *)(uintptr_t)0; 169 retval = (const char *)(uintptr_t)0;
166 startword = 1; 170 startword = 1;
@@ -177,6 +181,7 @@ shell_builtin_read(struct builtin_read_params *params)
177 if ((bufpos & 0xff) == 0) 181 if ((bufpos & 0xff) == 0)
178 buffer = xrealloc(buffer, bufpos + 0x101); 182 buffer = xrealloc(buffer, bufpos + 0x101);
179 183
184 IF_PLATFORM_MINGW32(loop:)
180 timeout = -1; 185 timeout = -1;
181 if (params->opt_t) { 186 if (params->opt_t) {
182 timeout = end_ms - (unsigned)monotonic_ms(); 187 timeout = end_ms - (unsigned)monotonic_ms();
@@ -190,6 +195,7 @@ shell_builtin_read(struct builtin_read_params *params)
190 } 195 }
191 } 196 }
192 197
198#if !ENABLE_PLATFORM_MINGW32
193 /* We must poll even if timeout is -1: 199 /* We must poll even if timeout is -1:
194 * we want to be interrupted if signal arrives, 200 * we want to be interrupted if signal arrives,
195 * regardless of SA_RESTART-ness of that signal! 201 * regardless of SA_RESTART-ness of that signal!
@@ -208,10 +214,73 @@ shell_builtin_read(struct builtin_read_params *params)
208 retval = (const char *)(uintptr_t)1; 214 retval = (const char *)(uintptr_t)1;
209 break; 215 break;
210 } 216 }
217#else
218 errno = 0;
219 if (isatty(fd)) {
220 int64_t key;
221
222 key = windows_read_key(fd, NULL, timeout);
223 if (key == 0x03) {
224 /* ^C pressed */
225 retval = (const char *)(uintptr_t)2;
226 goto ret;
227 }
228 else if (key == -1 || (key == 0x1a && bufpos == 0)) {
229 /* timeout or ^Z at start of buffer */
230 retval = (const char *)(uintptr_t)1;
231 goto ret;
232 }
233 else if (key == '\b') {
234 if (bufpos > 0) {
235 --bufpos;
236 ++nchars;
237 if (!(read_flags & BUILTIN_READ_SILENT)) {
238 printf("\b \b");
239 }
240 }
241 goto loop;
242 }
243 buffer[bufpos] = key == '\r' ? '\n' : key;
244 if (!(read_flags & BUILTIN_READ_SILENT)) {
245 /* echo input if not in silent mode */
246 putchar(buffer[bufpos]);
247 }
248 }
249 else {
250 if (read(fd, &buffer[bufpos], 1) != 1) {
251 err = errno;
252 retval = (const char *)(uintptr_t)1;
253 break;
254 }
255 }
256#endif
211 257
212 c = buffer[bufpos]; 258 c = buffer[bufpos];
259#if ENABLE_PLATFORM_MINGW32
260 if (c == '\n') {
261 if (backslash == 2 || (bufpos > 0 && buffer[bufpos - 1] == '\r')) {
262 /* We saw either:
263 * - BS CR LF: remove CR, fall through to ignore escaped LF
264 * and exit BS context.
265 * - CR LF not in BS context: replace CR with LF */
266 buffer[--bufpos] = c;
267 ++nchars;
268 }
269 } else if (backslash == 2) {
270 /* We saw BS CR ??, keep escaped CR, exit BS context,
271 * process ?? */
272 backslash = 0;
273 }
274#endif
213 if (!(read_flags & BUILTIN_READ_RAW)) { 275 if (!(read_flags & BUILTIN_READ_RAW)) {
214 if (backslash) { 276 if (backslash) {
277#if ENABLE_PLATFORM_MINGW32
278 if (c == '\r') {
279 /* We have BS CR, keep CR for now, might see LF next */
280 backslash = 2;
281 goto put;
282 }
283#endif
215 backslash = 0; 284 backslash = 0;
216 if (c != '\n') 285 if (c != '\n')
217 goto put; 286 goto put;
@@ -310,8 +379,10 @@ shell_builtin_read(struct builtin_read_params *params)
310 379
311 ret: 380 ret:
312 free(buffer); 381 free(buffer);
382#if !ENABLE_PLATFORM_MINGW32
313 if (read_flags & BUILTIN_READ_SILENT) 383 if (read_flags & BUILTIN_READ_SILENT)
314 tcsetattr(fd, TCSANOW, &old_tty); 384 tcsetattr(fd, TCSANOW, &old_tty);
385#endif
315 386
316 errno = err; 387 errno = err;
317 return retval; 388 return retval;
@@ -320,6 +391,7 @@ shell_builtin_read(struct builtin_read_params *params)
320 391
321/* ulimit builtin */ 392/* ulimit builtin */
322 393
394#if !ENABLE_PLATFORM_MINGW32
323struct limits { 395struct limits {
324 uint8_t cmd; /* RLIMIT_xxx fit into it */ 396 uint8_t cmd; /* RLIMIT_xxx fit into it */
325 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ 397 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
@@ -688,3 +760,9 @@ shell_builtin_ulimit(char **argv)
688 760
689 return EXIT_SUCCESS; 761 return EXIT_SUCCESS;
690} 762}
763#else
764int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM)
765{
766 return 1;
767}
768#endif