aboutsummaryrefslogtreecommitdiff
path: root/shell/ash.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--shell/ash.c2808
1 files changed, 2753 insertions, 55 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 1a85eec71..605215e41 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -15,6 +15,20 @@
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 */
31
18//config:config SHELL_ASH 32//config:config SHELL_ASH
19//config: bool #hidden option 33//config: bool #hidden option
20//config: depends on !NOMMU 34//config: depends on !NOMMU
@@ -170,11 +184,36 @@
170//config: you to run the specified command or builtin, 184//config: you to run the specified command or builtin,
171//config: even when there is a function with the same name. 185//config: even when there is a function with the same name.
172//config: 186//config:
187//config:
188//config:config ASH_NOCONSOLE
189//config: bool "'noconsole' option"
190//config: default y
191//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
192//config: help
193//config: Enable support for the 'noconsole' option, which attempts to
194//config: conceal the console normally associated with a command line
195//config: application. This may be useful when running a shell script
196//config: from a GUI application. Also the 'noiconify' option, which
197//config: controls whether the console is iconified or hidden.
198//config:
199//config:config ASH_GLOB_OPTIONS
200//config: bool "Globbing options"
201//config: default y
202//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
203//config: help
204//config: Enable support for options to control globbing:
205//config: - 'nocaseglob' allows case-insensitive filename globbing
206//config: - 'nohiddenglob' allows hidden files to be omitted from globbing
207//config: - 'nohidsysglob' allows hidden system files to be omitted
208//config:
173//config:endif # ash options 209//config:endif # ash options
174 210
175//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 211//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
176// APPLET_ODDNAME:name main location suid_type help 212// APPLET_ODDNAME:name main location suid_type help
177//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash)) 213//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
214//applet:IF_PLATFORM_MINGW32(
215//applet:IF_SH_IS_ASH( APPLET_ODDNAME(lash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
216//applet:)
178//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash)) 217//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
179 218
180//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o 219//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o
@@ -195,7 +234,17 @@
195 234
196#define PROFILE 0 235#define PROFILE 0
197 236
237/*
238 * Only one of JOBS or JOBS_WIN32 is enabled at a time (or neither).
239 * JOBS_WIN32 doesn't enable job control, just some job-related features.
240 */
241#if ENABLE_PLATFORM_MINGW32
242#define JOBS_WIN32 ENABLE_ASH_JOB_CONTROL
243#define JOBS 0
244#else
245#define JOBS_WIN32 0
198#define JOBS ENABLE_ASH_JOB_CONTROL 246#define JOBS ENABLE_ASH_JOB_CONTROL
247#endif
199 248
200#include <fnmatch.h> 249#include <fnmatch.h>
201#include <sys/times.h> 250#include <sys/times.h>
@@ -206,6 +255,10 @@
206#else 255#else
207# define NUM_SCRIPTS 0 256# define NUM_SCRIPTS 0
208#endif 257#endif
258#if ENABLE_PLATFORM_MINGW32
259# include <conio.h>
260# include "lazyload.h"
261#endif
209 262
210/* So far, all bash compat is controlled by one config option */ 263/* So far, all bash compat is controlled by one config option */
211/* Separate defines document which part of code implements what */ 264/* Separate defines document which part of code implements what */
@@ -316,10 +369,89 @@ typedef long arith_t;
316# define unlikely(cond) (cond) 369# define unlikely(cond) (cond)
317#endif 370#endif
318 371
372#if !ENABLE_PLATFORM_MINGW32
373# define is_relative_path(path) ((path)[0] != '/')
374#endif
375
319#if !BB_MMU 376#if !BB_MMU
320# error "Do not even bother, ash will not run on NOMMU machine" 377# error "Do not even bother, ash will not run on NOMMU machine"
321#endif 378#endif
322 379
380#if ENABLE_PLATFORM_MINGW32
381# define FORKSHELL_DEBUG 0
382
383union node;
384struct strlist;
385struct job;
386
387struct forkshell {
388 /* filled by forkshell_copy() */
389 struct globals_var *gvp;
390 struct globals_misc *gmp;
391 struct tblentry **cmdtable;
392#if ENABLE_ASH_ALIAS
393 struct alias **atab;
394#endif
395#if MAX_HISTORY
396 char **history;
397 int cnt_history;
398#endif
399#if JOBS_WIN32
400 struct job *jobtab;
401 unsigned njobs;
402 struct job *curjob;
403#endif
404 /* struct parsefile *g_parsefile; */
405 HANDLE hMapFile;
406 char *old_base;
407 int size;
408# if FORKSHELL_DEBUG
409 int funcblocksize;
410 int funcstringsize;
411# endif
412 int relocatesize;
413
414 /* type of forkshell */
415 int fpid;
416
417 /* generic data, used by forkshell_child */
418 int mode;
419 int nprocs;
420#if JOBS_WIN32
421 int jpnull;
422#endif
423
424 /* optional data, used by forkshell_child */
425 int flags;
426 int fd[3];
427 union node *n;
428 char **argv;
429 char *path;
430};
431
432enum {
433 FS_OPENHERE,
434 FS_EVALBACKCMD,
435 FS_EVALSUBSHELL,
436 FS_EVALPIPE,
437 FS_SHELLEXEC
438};
439
440static struct forkshell* forkshell_prepare(struct forkshell *fs);
441static void forkshell_init(const char *idstr);
442static void *sticky_mem_start, *sticky_mem_end;
443static void sticky_free(void *p);
444# define free(p) sticky_free(p)
445#if !JOBS && !JOBS_WIN32
446#define spawn_forkshell(fs, jp, n, mode) spawn_forkshell(fs, jp, mode)
447#endif
448static void spawn_forkshell(struct forkshell *fs, struct job *jp,
449 union node *n, int mode);
450# if FORKSHELL_DEBUG
451static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes);
452# endif
453#endif
454
323/* ============ Hash table sizes. Configurable. */ 455/* ============ Hash table sizes. Configurable. */
324 456
325#define VTABSIZE 39 457#define VTABSIZE 39
@@ -329,7 +461,7 @@ typedef long arith_t;
329 461
330/* ============ Shell options */ 462/* ============ Shell options */
331 463
332/* If you add/change options hare, update --help text too */ 464/* If you add/change options here, update --help text too */
333static const char *const optletters_optnames[] ALIGN_PTR = { 465static const char *const optletters_optnames[] ALIGN_PTR = {
334 "e" "errexit", 466 "e" "errexit",
335 "f" "noglob", 467 "f" "noglob",
@@ -347,7 +479,11 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
347 "m" "monitor", 479 "m" "monitor",
348 "n" "noexec", 480 "n" "noexec",
349/* Ditto: bash has no "set -s", "set -c" */ 481/* Ditto: bash has no "set -s", "set -c" */
482#if !ENABLE_PLATFORM_MINGW32
350 "s" "", 483 "s" "",
484#else
485 "s" "stdin",
486#endif
351 "c" "", 487 "c" "",
352 "x" "xtrace", 488 "x" "xtrace",
353 "v" "verbose", 489 "v" "verbose",
@@ -364,6 +500,18 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
364 ,"\0" "nolog" 500 ,"\0" "nolog"
365 ,"\0" "debug" 501 ,"\0" "debug"
366#endif 502#endif
503#if ENABLE_PLATFORM_MINGW32
504 ,"X" "winxp"
505#endif
506#if ENABLE_ASH_NOCONSOLE
507 ,"\0" "noconsole"
508 ,"\0" "noiconify"
509#endif
510#if ENABLE_ASH_GLOB_OPTIONS
511 ,"\0" "nocaseglob"
512 ,"\0" "nohiddenglob"
513 ,"\0" "nohidsysglob"
514#endif
367}; 515};
368//bash 4.4.23 also has these opts (with these defaults): 516//bash 4.4.23 also has these opts (with these defaults):
369//braceexpand on 517//braceexpand on
@@ -406,21 +554,36 @@ struct jmploc {
406struct globals_misc { 554struct globals_misc {
407 uint8_t exitstatus; /* exit status of last command */ 555 uint8_t exitstatus; /* exit status of last command */
408 uint8_t back_exitstatus;/* exit status of backquoted command */ 556 uint8_t back_exitstatus;/* exit status of backquoted command */
557#if !ENABLE_PLATFORM_MINGW32
409 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ 558 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
559#endif
410 smallint inps4; /* Prevent PS4 nesting. */ 560 smallint inps4; /* Prevent PS4 nesting. */
411 int savestatus; /* exit status of last command outside traps */ 561 int savestatus; /* exit status of last command outside traps */
412 int rootpid; /* pid of main shell */ 562 int rootpid; /* pid of main shell */
413 /* shell level: 0 for the main shell, 1 for its children, and so on */ 563 /* shell level: 0 for the main shell, 1 for its children, and so on */
414 int shlvl; 564 int shlvl;
565#if ENABLE_PLATFORM_MINGW32
566 int loopnest; /* current loop nesting level */
567#endif
415#define rootshell (!shlvl) 568#define rootshell (!shlvl)
416 int errlinno; 569 int errlinno;
417 570
418 char *minusc; /* argument to -c option */ 571 char *minusc; /* argument to -c option */
572#if ENABLE_PLATFORM_MINGW32
573 char *dirarg; /* argument to -d option */
574 char *title; /* argument to -t option */
575#if ENABLE_SUW32
576 int delayexit; /* set by -N option */
577# endif
578#endif
419 579
420 char *curdir; // = nullstr; /* current working directory */ 580 char *curdir; // = nullstr; /* current working directory */
421 char *physdir; // = nullstr; /* physical working directory */ 581 char *physdir; // = nullstr; /* physical working directory */
422 582
423 char *arg0; /* value of $0 */ 583 char *arg0; /* value of $0 */
584#if ENABLE_PLATFORM_MINGW32
585 char *commandname;
586#endif
424 587
425 struct jmploc *exception_handler; 588 struct jmploc *exception_handler;
426 589
@@ -431,8 +594,10 @@ struct globals_misc {
431 * but we do read it async. 594 * but we do read it async.
432 */ 595 */
433 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ 596 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
597#if !ENABLE_PLATFORM_MINGW32
434 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ 598 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
435 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ 599 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
600#endif
436 smallint exception_type; /* kind of exception: */ 601 smallint exception_type; /* kind of exception: */
437#define EXINT 0 /* SIGINT received */ 602#define EXINT 0 /* SIGINT received */
438#define EXERROR 1 /* a generic error */ 603#define EXERROR 1 /* a generic error */
@@ -467,8 +632,21 @@ struct globals_misc {
467# define nolog optlist[16 + BASH_PIPEFAIL] 632# define nolog optlist[16 + BASH_PIPEFAIL]
468# define debug optlist[17 + BASH_PIPEFAIL] 633# define debug optlist[17 + BASH_PIPEFAIL]
469#endif 634#endif
635#if ENABLE_PLATFORM_MINGW32
636# define winxp optlist[16 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
637# if ENABLE_ASH_NOCONSOLE
638# define noconsole optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
639# define noiconify optlist[18 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
640# endif
641# if ENABLE_ASH_GLOB_OPTIONS
642# define nocaseglob optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
643# define nohiddenglob optlist[18 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
644# define nohidsysglob optlist[19 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
645# endif
646#endif
470 647
471 /* trap handler commands */ 648 /* trap handler commands */
649#if !ENABLE_PLATFORM_MINGW32
472 /* 650 /*
473 * Sigmode records the current value of the signal handlers for the various 651 * Sigmode records the current value of the signal handlers for the various
474 * modes. A value of zero means that the current handler is not known. 652 * modes. A value of zero means that the current handler is not known.
@@ -482,6 +660,7 @@ struct globals_misc {
482 660
483 /* indicates specified signal received */ 661 /* indicates specified signal received */
484 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ 662 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
663#endif
485 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ 664 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
486 char *trap[NSIG + 1]; 665 char *trap[NSIG + 1];
487/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */ 666/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
@@ -510,10 +689,21 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
510#define rootpid (G_misc.rootpid ) 689#define rootpid (G_misc.rootpid )
511#define shlvl (G_misc.shlvl ) 690#define shlvl (G_misc.shlvl )
512#define errlinno (G_misc.errlinno ) 691#define errlinno (G_misc.errlinno )
692#if ENABLE_PLATFORM_MINGW32
693#define loopnest (G_misc.loopnest )
694#endif
513#define minusc (G_misc.minusc ) 695#define minusc (G_misc.minusc )
696#if ENABLE_PLATFORM_MINGW32
697#define dirarg (G_misc.dirarg )
698#define title (G_misc.title )
699#define delayexit (G_misc.delayexit )
700#endif
514#define curdir (G_misc.curdir ) 701#define curdir (G_misc.curdir )
515#define physdir (G_misc.physdir ) 702#define physdir (G_misc.physdir )
516#define arg0 (G_misc.arg0 ) 703#define arg0 (G_misc.arg0 )
704#if ENABLE_PLATFORM_MINGW32
705#define commandname (G_misc.commandname)
706#endif
517#define exception_handler (G_misc.exception_handler) 707#define exception_handler (G_misc.exception_handler)
518#define exception_type (G_misc.exception_type ) 708#define exception_type (G_misc.exception_type )
519#define suppress_int (G_misc.suppress_int ) 709#define suppress_int (G_misc.suppress_int )
@@ -530,6 +720,13 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
530#define groupinfo (G_misc.groupinfo ) 720#define groupinfo (G_misc.groupinfo )
531#define random_gen (G_misc.random_gen ) 721#define random_gen (G_misc.random_gen )
532#define backgndpid (G_misc.backgndpid ) 722#define backgndpid (G_misc.backgndpid )
723
724#if ENABLE_PLATFORM_MINGW32
725#undef got_sigchld
726#undef pending_sig
727#define pending_sig (0)
728#endif
729
533#define INIT_G_misc() do { \ 730#define INIT_G_misc() do { \
534 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \ 731 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \
535 savestatus = -1; \ 732 savestatus = -1; \
@@ -547,6 +744,9 @@ static void trace_printf(const char *fmt, ...);
547static void trace_vprintf(const char *fmt, va_list va); 744static void trace_vprintf(const char *fmt, va_list va);
548# define TRACE(param) trace_printf param 745# define TRACE(param) trace_printf param
549# define TRACEV(param) trace_vprintf param 746# define TRACEV(param) trace_vprintf param
747# if ENABLE_PLATFORM_MINGW32 && defined(close)
748# undef close
749# endif
550# define close(fd) do { \ 750# define close(fd) do { \
551 int dfd = (fd); \ 751 int dfd = (fd); \
552 if (close(dfd) < 0) \ 752 if (close(dfd) < 0) \
@@ -635,11 +835,18 @@ struct parsefile {
635 835
636 /* Number of outstanding calls to pungetc. */ 836 /* Number of outstanding calls to pungetc. */
637 int unget; 837 int unget;
838
839#if ENABLE_PLATFORM_MINGW32
840 /* True if a trailing CR from a previous read was left unprocessed. */
841 int cr;
842#endif
638}; 843};
639 844
640static struct parsefile basepf; /* top level input file */ 845static struct parsefile basepf; /* top level input file */
641static struct parsefile *g_parsefile = &basepf; /* current input file */ 846static struct parsefile *g_parsefile = &basepf; /* current input file */
847#if ENABLE_PLATFORM_POSIX
642static char *commandname; /* currently executing command */ 848static char *commandname; /* currently executing command */
849#endif
643 850
644 851
645/* ============ Interrupts / exceptions */ 852/* ============ Interrupts / exceptions */
@@ -696,21 +903,40 @@ raise_exception(int e)
696 * are held using the INT_OFF macro. (The test for iflag is just 903 * are held using the INT_OFF macro. (The test for iflag is just
697 * defensive programming.) 904 * defensive programming.)
698 */ 905 */
699static void raise_interrupt(void) NORETURN; 906static void raise_interrupt(void) IF_NOT_PLATFORM_MINGW32(NORETURN);
700static void 907static void
701raise_interrupt(void) 908raise_interrupt(void)
702{ 909{
910#if ENABLE_PLATFORM_MINGW32
911 /* Contrary to the comment above on Windows raise_interrupt() is
912 * called when SIGINT is trapped or ignored. We detect this here
913 * and return without doing anything. */
914 if (trap[SIGINT])
915 return;
916#endif
703 pending_int = 0; 917 pending_int = 0;
918#if !ENABLE_PLATFORM_MINGW32
704 /* Signal is not automatically unmasked after it is raised, 919 /* Signal is not automatically unmasked after it is raised,
705 * do it ourself - unmask all signals */ 920 * do it ourself - unmask all signals */
706 sigprocmask_allsigs(SIG_UNBLOCK); 921 sigprocmask_allsigs(SIG_UNBLOCK);
922#endif
707 /* pending_sig = 0; - now done in signal_handler() */ 923 /* pending_sig = 0; - now done in signal_handler() */
708 924
709 if (!(rootshell && iflag)) { 925 if (!(rootshell && iflag)) {
926#if !ENABLE_PLATFORM_MINGW32
710 /* Kill ourself with SIGINT */ 927 /* Kill ourself with SIGINT */
711 signal(SIGINT, SIG_DFL); 928 signal(SIGINT, SIG_DFL);
712 raise(SIGINT); 929 raise(SIGINT);
930#else
931 fflush_all();
932 kill(-getpid(), SIGINT);
933 _exit(SIGINT << 24);
934#endif
713 } 935 }
936#if ENABLE_PLATFORM_MINGW32
937 if (iflag)
938 write(STDOUT_FILENO, "^C", 2);
939#endif
714 /* bash: ^C even on empty command line sets $? */ 940 /* bash: ^C even on empty command line sets $? */
715 exitstatus = SIGINT + 128; 941 exitstatus = SIGINT + 128;
716 raise_exception(EXINT); 942 raise_exception(EXINT);
@@ -1596,7 +1822,6 @@ struct stackmark {
1596 size_t stacknleft; 1822 size_t stacknleft;
1597}; 1823};
1598 1824
1599
1600struct globals_memstack { 1825struct globals_memstack {
1601 struct stack_block *g_stackp; // = &stackbase; 1826 struct stack_block *g_stackp; // = &stackbase;
1602 char *g_stacknxt; // = stackbase.space; 1827 char *g_stacknxt; // = stackbase.space;
@@ -1619,7 +1844,6 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
1619 sstrend = stackbase.space + MINSIZE; \ 1844 sstrend = stackbase.space + MINSIZE; \
1620} while (0) 1845} while (0)
1621 1846
1622
1623#define stackblock() ((void *)g_stacknxt) 1847#define stackblock() ((void *)g_stacknxt)
1624#define stackblocksize() g_stacknleft 1848#define stackblocksize() g_stacknleft
1625 1849
@@ -1998,6 +2222,18 @@ maybe_single_quote(const char *s)
1998 return single_quote(s); 2222 return single_quote(s);
1999} 2223}
2000 2224
2225#if ENABLE_PLATFORM_MINGW32
2226/* Copy path to a string on the stack long enough to allow a file extension
2227 * to be added. */
2228static char *
2229stack_add_ext_space(const char *path)
2230{
2231 char *p = growstackto(strlen(path) + 5);
2232 strcpy(p, path);
2233 return p;
2234}
2235#endif
2236
2001 2237
2002/* ============ nextopt */ 2238/* ============ nextopt */
2003 2239
@@ -2120,7 +2356,9 @@ struct localvar {
2120#else 2356#else
2121# define VDYNAMIC 0 2357# define VDYNAMIC 0
2122#endif 2358#endif
2123 2359#if ENABLE_PLATFORM_MINGW32
2360# define VIMPORT 0x400 /* variable was imported from environment */
2361#endif
2124 2362
2125/* Need to be before varinit_data[] */ 2363/* Need to be before varinit_data[] */
2126#if ENABLE_LOCALE_SUPPORT 2364#if ENABLE_LOCALE_SUPPORT
@@ -2149,6 +2387,24 @@ static void change_seconds(const char *) FAST_FUNC;
2149static void change_realtime(const char *) FAST_FUNC; 2387static void change_realtime(const char *) FAST_FUNC;
2150#endif 2388#endif
2151 2389
2390#if ENABLE_PLATFORM_MINGW32
2391static void FAST_FUNC
2392change_terminal_mode(const char *newval UNUSED_PARAM)
2393{
2394 terminal_mode(TRUE);
2395}
2396
2397static void clearcmdentry(void);
2398static void FAST_FUNC
2399change_override_applets(const char *newval UNUSED_PARAM)
2400{
2401 clearcmdentry();
2402}
2403
2404# define LINENO_INDEX (5 + 2 * ENABLE_ASH_MAIL + ENABLE_ASH_GETOPTS)
2405# define FUNCNAME_INDEX (LINENO_INDEX + 1)
2406#endif
2407
2152static const struct { 2408static const struct {
2153 int flags; 2409 int flags;
2154 const char *var_text; 2410 const char *var_text;
@@ -2186,6 +2442,12 @@ static const struct {
2186#if ENABLE_FEATURE_EDITING_SAVEHISTORY 2442#if ENABLE_FEATURE_EDITING_SAVEHISTORY
2187 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL }, 2443 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
2188#endif 2444#endif
2445#if ENABLE_PLATFORM_MINGW32
2446 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_SKIP_ANSI_EMULATION, change_terminal_mode },
2447 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_TERMINAL_MODE, change_terminal_mode },
2448 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_OVERRIDE_APPLETS, change_override_applets },
2449 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_CRITICAL_ERROR_DIALOGS, change_critical_error_dialogs },
2450#endif
2189}; 2451};
2190 2452
2191struct redirtab; 2453struct redirtab;
@@ -2409,6 +2671,65 @@ bltinlookup(const char *name)
2409 return lookupvar(name); 2671 return lookupvar(name);
2410} 2672}
2411 2673
2674#if ENABLE_PLATFORM_MINGW32
2675static char *
2676fix_pathvar(const char *path, int len)
2677{
2678 char *newpath = xstrdup(path);
2679 char *p;
2680 int modified = FALSE;
2681
2682 p = newpath + len;
2683 while (*p) {
2684 if (*p != ':' && *p != ';') {
2685 /* skip drive */
2686 if (isalpha(*p) && p[1] == ':')
2687 p += 2;
2688 /* skip through path component */
2689 for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2690 continue;
2691 }
2692 /* *p is ':', ';' or '\0' here */
2693 if (*p == ':') {
2694 *p++ = ';';
2695 modified = TRUE;
2696 }
2697 else if (*p == ';') {
2698 ++p;
2699 }
2700 }
2701
2702 if (!modified) {
2703 free(newpath);
2704 newpath = NULL;
2705 }
2706 return newpath;
2707}
2708
2709#define BB_VAR_EXACT 1 /* exact match for name */
2710#define BB_VAR_ASSIGN -1 /* matches name followed by '=' */
2711
2712/* Match variables that should be placed in the environment immediately
2713 * they're exported and removed immediately they're no longer exported */
2714static int
2715is_bb_var(const char *s)
2716{
2717 const char *p;
2718 int len;
2719
2720 for (p = bbvar; *p; p += len + 1) {
2721 len = strlen(p);
2722 if (strncmp(s, p, len) == 0) {
2723 if (s[len] == '\0')
2724 return BB_VAR_EXACT;
2725 else if (s[len] == '=')
2726 return BB_VAR_ASSIGN;
2727 }
2728 }
2729 return FALSE;
2730}
2731#endif
2732
2412/* 2733/*
2413 * Same as setvar except that the variable and value are passed in 2734 * Same as setvar except that the variable and value are passed in
2414 * the first argument as name=value. Since the first argument will 2735 * the first argument as name=value. Since the first argument will
@@ -2420,6 +2741,26 @@ static struct var *
2420setvareq(char *s, int flags) 2741setvareq(char *s, int flags)
2421{ 2742{
2422 struct var *vp, **vpp; 2743 struct var *vp, **vpp;
2744#if ENABLE_PLATFORM_MINGW32
2745 const char *paths = "PATH=\0""CDPATH=\0""MANPATH=\0";
2746 const char *p;
2747 int len;
2748
2749 for (p = paths; *p; p += len + 1) {
2750 len = strlen(p);
2751 if (strncmp(s, p, len) == 0) {
2752 char *newpath = fix_pathvar(s, len);
2753 if (newpath) {
2754 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2755 free(s);
2756 flags |= VNOSAVE;
2757 flags &= ~(VTEXTFIXED|VSTACK);
2758 s = newpath;
2759 }
2760 break;
2761 }
2762 }
2763#endif
2423 2764
2424 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2765 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2425 vpp = findvar(s); 2766 vpp = findvar(s);
@@ -2444,6 +2785,11 @@ setvareq(char *s, int flags)
2444 if (!(vp->flags & (VTEXTFIXED|VSTACK))) 2785 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2445 free((char*)vp->var_text); 2786 free((char*)vp->var_text);
2446 2787
2788#if ENABLE_PLATFORM_MINGW32
2789 if ((flags & VUNSET) && (vp->flags & VEXPORT) &&
2790 is_bb_var(s) == BB_VAR_EXACT)
2791 unsetenv(s);
2792#endif
2447 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) { 2793 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2448 *vpp = vp->next; 2794 *vpp = vp->next;
2449 free(vp); 2795 free(vp);
@@ -2473,6 +2819,10 @@ setvareq(char *s, int flags)
2473 s = ckstrdup(s); 2819 s = ckstrdup(s);
2474 vp->var_text = s; 2820 vp->var_text = s;
2475 vp->flags = flags; 2821 vp->flags = flags;
2822#if ENABLE_PLATFORM_MINGW32
2823 if ((flags & VEXPORT) && is_bb_var(s) == BB_VAR_ASSIGN)
2824 putenv(s);
2825#endif
2476 2826
2477 out: 2827 out:
2478 return vp; 2828 return vp;
@@ -2594,6 +2944,65 @@ listvars(int on, int off, struct strlist *lp, char ***end)
2594 return grabstackstr(ep); 2944 return grabstackstr(ep);
2595} 2945}
2596 2946
2947#if ENABLE_PLATFORM_MINGW32
2948/* Adjust directory separator in variables imported from the environment */
2949static void
2950setwinxp(int on)
2951{
2952 static smallint is_winxp = 1;
2953 struct var **vpp;
2954 struct var *vp;
2955
2956 if (on == is_winxp)
2957 return;
2958 is_winxp = on;
2959
2960 for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
2961 for (vp = *vpp; vp; vp = vp->next) {
2962 if ((vp->flags & VIMPORT)) {
2963 char *end = strchr(vp->var_text, '=');
2964 if (!end || is_prefixed_with(vp->var_text, "COMSPEC=") ||
2965 is_prefixed_with(vp->var_text, "SYSTEMROOT="))
2966 continue;
2967 if (!on)
2968 bs_to_slash(end + 1);
2969 else
2970 slash_to_bs(end + 1);
2971 }
2972 }
2973 }
2974}
2975
2976# if ENABLE_ASH_NOCONSOLE
2977/*
2978 * Console state is either:
2979 * 0 normal
2980 * 1 iconified/hidden
2981 * 2 unknown
2982 */
2983static int console_state(void)
2984{
2985 DECLARE_PROC_ADDR(BOOL, ShowWindow, HWND, int);
2986
2987 if (INIT_PROC_ADDR(user32.dll, ShowWindow)) {
2988 BOOL visible = IsWindowVisible(GetConsoleWindow());
2989 BOOL iconified = IsIconic(GetConsoleWindow());
2990
2991 return !visible || iconified;
2992 }
2993 return 2;
2994}
2995
2996static void hide_console(int hide)
2997{
2998 // Switch console state if it's known and isn't the required state
2999 if (console_state() == !hide)
3000 ShowWindow(GetConsoleWindow(), hide ?
3001 (noiconify ? SW_HIDE : SW_MINIMIZE) : SW_NORMAL);
3002}
3003# endif
3004#endif
3005
2597 3006
2598/* ============ Path search helper */ 3007/* ============ Path search helper */
2599static const char * 3008static const char *
@@ -2637,7 +3046,7 @@ static const char *pathopt; /* set by padvance */
2637static int 3046static int
2638padvance_magic(const char **path, const char *name, int magic) 3047padvance_magic(const char **path, const char *name, int magic)
2639{ 3048{
2640 const char *term = "%:"; 3049 const char *term = "%"PATH_SEP_STR;
2641 const char *lpathopt; 3050 const char *lpathopt;
2642 const char *p; 3051 const char *p;
2643 char *q; 3052 char *q;
@@ -2654,14 +3063,14 @@ padvance_magic(const char **path, const char *name, int magic)
2654 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) { 3063 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2655 lpathopt = start + 1; 3064 lpathopt = start + 1;
2656 start = p; 3065 start = p;
2657 term = ":"; 3066 term = PATH_SEP_STR;
2658 } 3067 }
2659 3068
2660 len = strcspn(start, term); 3069 len = strcspn(start, term);
2661 p = start + len; 3070 p = start + len;
2662 3071
2663 if (*p == '%') { 3072 if (*p == '%') {
2664 size_t extra = strchrnul(p, ':') - p; 3073 size_t extra = strchrnul(p, PATH_SEP) - p;
2665 3074
2666 if (legal_pathopt(p + 1, term, magic)) 3075 if (legal_pathopt(p + 1, term, magic))
2667 lpathopt = p + 1; 3076 lpathopt = p + 1;
@@ -2672,14 +3081,18 @@ padvance_magic(const char **path, const char *name, int magic)
2672 } 3081 }
2673 3082
2674 pathopt = lpathopt; 3083 pathopt = lpathopt;
2675 *path = *p == ':' ? p + 1 : NULL; 3084 *path = *p == PATH_SEP ? p + 1 : NULL;
2676 3085
2677 /* "2" is for '/' and '\0' */ 3086 /* "2" is for '/' and '\0' */
2678 qlen = len + strlen(name) + 2; 3087 /* reserve space for suffix on WIN32 */
3088 qlen = len + strlen(name) + 2 IF_PLATFORM_MINGW32(+ 4);
2679 q = growstackto(qlen); 3089 q = growstackto(qlen);
2680 3090
2681 if (len) { 3091 if (len) {
2682 q = mempcpy(q, start, len); 3092 q = mempcpy(q, start, len);
3093#if ENABLE_PLATFORM_MINGW32
3094 if (q[-1] != '/' && q[-1] != '\\')
3095#endif
2683 *q++ = '/'; 3096 *q++ = '/';
2684 } 3097 }
2685 strcpy(q, name); 3098 strcpy(q, name);
@@ -2770,6 +3183,7 @@ setprompt_if(smallint do_set, int whichprompt)
2770 3183
2771#define CD_PHYSICAL 1 3184#define CD_PHYSICAL 1
2772#define CD_PRINT 2 3185#define CD_PRINT 2
3186#define CD_PRINT_ALL 4
2773 3187
2774static int 3188static int
2775cdopt(void) 3189cdopt(void)
@@ -2778,7 +3192,14 @@ cdopt(void)
2778 int i, j; 3192 int i, j;
2779 3193
2780 j = 'L'; 3194 j = 'L';
3195#if ENABLE_PLATFORM_MINGW32
3196 while ((i = nextopt("LPa")) != '\0') {
3197 if (i == 'a')
3198 flags |= CD_PRINT_ALL;
3199 else
3200#else
2781 while ((i = nextopt("LP")) != '\0') { 3201 while ((i = nextopt("LP")) != '\0') {
3202#endif
2782 if (i != j) { 3203 if (i != j) {
2783 flags ^= CD_PHYSICAL; 3204 flags ^= CD_PHYSICAL;
2784 j = i; 3205 j = i;
@@ -2795,6 +3216,130 @@ cdopt(void)
2795static const char * 3216static const char *
2796updatepwd(const char *dir) 3217updatepwd(const char *dir)
2797{ 3218{
3219#if ENABLE_PLATFORM_MINGW32
3220 /*
3221 * Due to Windows drive notion, getting pwd is a completely
3222 * different thing. Handle it in a separate routine
3223 */
3224
3225 char *new;
3226 char *p;
3227 char *cdcomppath;
3228 const char *lim;
3229 int len;
3230 char buffer[PATH_MAX];
3231 /*
3232 * There are five cases that make some kind of sense
3233 *
3234 * Absolute paths:
3235 * c:/path
3236 * //host/share
3237 *
3238 * Relative to current working directory of other drive:
3239 * c:path
3240 *
3241 * Relative to current root (drive/share):
3242 * /path
3243 *
3244 * Relative to current working directory of current root (drive/share):
3245 * path
3246 */
3247 enum {ABS_DRIVE, ABS_SHARE, REL_OTHER, REL_ROOT, REL_CWD} target;
3248
3249 /* skip multiple leading separators unless dir is a UNC path */
3250 if (is_dir_sep(*dir) && unc_root_len(dir) == 0) {
3251 while (is_dir_sep(dir[1]))
3252 ++dir;
3253 }
3254
3255 len = strlen(dir);
3256 if (len >= 2 && has_dos_drive_prefix(dir))
3257 target = len >= 3 && is_dir_sep(dir[2]) ? ABS_DRIVE : REL_OTHER;
3258 else if (unc_root_len(dir) != 0)
3259 target = ABS_SHARE;
3260 else if (is_dir_sep(*dir))
3261 target = REL_ROOT;
3262 else
3263 target = REL_CWD;
3264
3265 cdcomppath = sstrdup(dir);
3266 STARTSTACKSTR(new);
3267
3268 switch (target) {
3269 case REL_OTHER:
3270 /* c:path */
3271 if (get_drive_cwd(dir, buffer, PATH_MAX) == NULL)
3272 return 0;
3273 new = stack_putstr(buffer, new);
3274 len = 2;
3275 cdcomppath += len;
3276 dir += len;
3277 break;
3278 case REL_CWD:
3279 case REL_ROOT:
3280 /* path or /path */
3281 len = root_len(curdir);
3282 if (len == 0)
3283 return 0;
3284 new = target == REL_CWD ? stack_putstr(curdir, new) :
3285 stnputs(curdir, len, new);
3286 break;
3287 default:
3288 /* //host/share or c:/path */
3289 len = root_len(dir);
3290 if (len == 0)
3291 return 0;
3292 new = stnputs(dir, len, new);
3293 cdcomppath += len;
3294 dir += len;
3295 break;
3296 }
3297
3298 new = makestrspace(strlen(dir) + 2, new);
3299 lim = (char *)stackblock() + len + 1;
3300
3301 if (!is_dir_sep(*dir)) {
3302 if (!is_dir_sep(new[-1]))
3303 USTPUTC('/', new);
3304 if (new > lim && is_dir_sep(*lim))
3305 lim++;
3306 } else {
3307 USTPUTC('/', new);
3308 cdcomppath++;
3309 if (is_dir_sep(dir[1]) && !is_dir_sep(dir[2])) {
3310 USTPUTC('/', new);
3311 cdcomppath++;
3312 lim++;
3313 }
3314 }
3315 p = strtok(cdcomppath, "/\\");
3316 while (p) {
3317 switch (*p) {
3318 case '.':
3319 if (p[1] == '.' && p[2] == '\0') {
3320 while (new > lim) {
3321 STUNPUTC(new);
3322 if (is_dir_sep(new[-1]))
3323 break;
3324 }
3325 break;
3326 }
3327 if (p[1] == '\0')
3328 break;
3329 /* fall through */
3330 default:
3331 new = stack_putstr(p, new);
3332 USTPUTC('/', new);
3333 }
3334 p = strtok(NULL, "/\\");
3335 }
3336 if (new > lim)
3337 STUNPUTC(new);
3338 *new = 0;
3339 strip_dot_space((char *)stackblock());
3340 fix_path_case((char *)stackblock());
3341 return bs_to_slash((char *)stackblock());
3342#else
2798 char *new; 3343 char *new;
2799 char *p; 3344 char *p;
2800 char *cdcomppath; 3345 char *cdcomppath;
@@ -2848,6 +3393,7 @@ updatepwd(const char *dir)
2848 STUNPUTC(new); 3393 STUNPUTC(new);
2849 *new = 0; 3394 *new = 0;
2850 return stackblock(); 3395 return stackblock();
3396#endif
2851} 3397}
2852 3398
2853/* 3399/*
@@ -2943,7 +3489,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2943 } 3489 }
2944 if (!dest) 3490 if (!dest)
2945 dest = nullstr; 3491 dest = nullstr;
2946 if (*dest == '/') 3492 if (!is_relative_path(dest))
2947 goto step6; 3493 goto step6;
2948 if (*dest == '.') { 3494 if (*dest == '.') {
2949 c = dest[1]; 3495 c = dest[1];
@@ -2966,7 +3512,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2966 p = stalloc(len); 3512 p = stalloc(len);
2967 3513
2968 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 3514 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2969 if (c && c != ':') 3515 if (c && c != PATH_SEP)
2970 flags |= CD_PRINT; 3516 flags |= CD_PRINT;
2971 docd: 3517 docd:
2972 if (!docd(p, flags)) 3518 if (!docd(p, flags))
@@ -2988,6 +3534,26 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2988 return 0; 3534 return 0;
2989} 3535}
2990 3536
3537#if ENABLE_PLATFORM_MINGW32
3538static void
3539print_all_cwd(void)
3540{
3541 FILE *mnt;
3542 struct mntent *entry;
3543 char buffer[PATH_MAX];
3544
3545 mnt = setmntent(bb_path_mtab_file, "r");
3546 if (mnt) {
3547 while ((entry=getmntent(mnt)) != NULL) {
3548 entry->mnt_dir[2] = '\0';
3549 if (get_drive_cwd(entry->mnt_dir, buffer, PATH_MAX) != NULL)
3550 out1fmt("%s\n", buffer);
3551 }
3552 endmntent(mnt);
3553 }
3554}
3555#endif
3556
2991static int FAST_FUNC 3557static int FAST_FUNC
2992pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 3558pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2993{ 3559{
@@ -2995,6 +3561,12 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2995 const char *dir = curdir; 3561 const char *dir = curdir;
2996 3562
2997 flags = cdopt(); 3563 flags = cdopt();
3564#if ENABLE_PLATFORM_MINGW32
3565 if (flags & CD_PRINT_ALL) {
3566 print_all_cwd();
3567 return 0;
3568 }
3569#endif
2998 if (flags) { 3570 if (flags) {
2999 if (physdir == nullstr) 3571 if (physdir == nullstr)
3000 setpwd(dir, 0); 3572 setpwd(dir, 0);
@@ -3007,7 +3579,6 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3007 3579
3008/* ============ ... */ 3580/* ============ ... */
3009 3581
3010
3011#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) 3582#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
3012 3583
3013/* Syntax classes */ 3584/* Syntax classes */
@@ -3414,13 +3985,11 @@ struct alias {
3414 int flag; 3985 int flag;
3415}; 3986};
3416 3987
3417
3418static struct alias **atab; // [ATABSIZE]; 3988static struct alias **atab; // [ATABSIZE];
3419#define INIT_G_alias() do { \ 3989#define INIT_G_alias() do { \
3420 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ 3990 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3421} while (0) 3991} while (0)
3422 3992
3423
3424static struct alias ** 3993static struct alias **
3425__lookupalias(const char *name) 3994__lookupalias(const char *name)
3426{ 3995{
@@ -3602,7 +4171,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3602 4171
3603#endif /* ASH_ALIAS */ 4172#endif /* ASH_ALIAS */
3604 4173
3605
3606/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ 4174/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3607#define FORK_FG 0 4175#define FORK_FG 0
3608#define FORK_BG 1 4176#define FORK_BG 1
@@ -3623,7 +4191,12 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3623struct procstat { 4191struct procstat {
3624 pid_t ps_pid; /* process id */ 4192 pid_t ps_pid; /* process id */
3625 int ps_status; /* last process status from wait() */ 4193 int ps_status; /* last process status from wait() */
4194#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3626 char *ps_cmd; /* text of command being run */ 4195 char *ps_cmd; /* text of command being run */
4196#endif
4197#if ENABLE_PLATFORM_MINGW32
4198 HANDLE ps_proc;
4199#endif
3627}; 4200};
3628 4201
3629struct job { 4202struct job {
@@ -3639,8 +4212,10 @@ struct job {
3639#define JOBDONE 2 /* all procs are completed */ 4212#define JOBDONE 2 /* all procs are completed */
3640 unsigned 4213 unsigned
3641 state: 8, 4214 state: 8,
3642#if JOBS 4215#if JOBS || ENABLE_PLATFORM_MINGW32
3643 sigint: 1, /* job was killed by SIGINT */ 4216 sigint: 1, /* job was killed by SIGINT */
4217#endif
4218#if JOBS
3644 jobctl: 1, /* job running under job control */ 4219 jobctl: 1, /* job running under job control */
3645#endif 4220#endif
3646 waited: 1, /* true if this entry has been waited for */ 4221 waited: 1, /* true if this entry has been waited for */
@@ -3650,17 +4225,23 @@ struct job {
3650}; 4225};
3651 4226
3652static struct job *makejob(/*union node *,*/ int); 4227static struct job *makejob(/*union node *,*/ int);
4228#if !ENABLE_PLATFORM_MINGW32
3653static int forkshell(struct job *, union node *, int); 4229static int forkshell(struct job *, union node *, int);
4230#endif
3654static int waitforjob(struct job *); 4231static int waitforjob(struct job *);
3655 4232
3656#if !JOBS 4233#if !JOBS && !JOBS_WIN32
3657enum { doing_jobctl = 0 }; 4234enum { doing_jobctl = 0 };
3658#define setjobctl(on) do {} while (0) 4235#define setjobctl(on) do {} while (0)
3659#else 4236#elif JOBS_WIN32
4237static smallint doing_jobctl; //references:8
4238#define setjobctl(on) do { if (rootshell) doing_jobctl = on; } while (0)
4239#else /* JOBS */
3660static smallint doing_jobctl; //references:8 4240static smallint doing_jobctl; //references:8
3661static void setjobctl(int); 4241static void setjobctl(int);
3662#endif 4242#endif
3663 4243
4244#if !ENABLE_PLATFORM_MINGW32
3664/* 4245/*
3665 * Ignore a signal. 4246 * Ignore a signal.
3666 */ 4247 */
@@ -3809,6 +4390,10 @@ setsignal(int signo)
3809 4390
3810 sigaction_set(signo, &act); 4391 sigaction_set(signo, &act);
3811} 4392}
4393#else
4394#define setsignal(s)
4395#define ignoresig(s)
4396#endif
3812 4397
3813/* mode flags for set_curjob */ 4398/* mode flags for set_curjob */
3814#define CUR_DELETE 2 4399#define CUR_DELETE 2
@@ -3942,7 +4527,7 @@ set_curjob(struct job *jp, unsigned mode)
3942 } 4527 }
3943} 4528}
3944 4529
3945#if JOBS || DEBUG 4530#if JOBS || ENABLE_PLATFORM_MINGW32 || DEBUG
3946static int 4531static int
3947jobno(const struct job *jp) 4532jobno(const struct job *jp)
3948{ 4533{
@@ -3960,7 +4545,9 @@ static struct job *
3960getjob(const char *name, int getctl) 4545getjob(const char *name, int getctl)
3961{ 4546{
3962 struct job *jp; 4547 struct job *jp;
4548#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3963 struct job *found; 4549 struct job *found;
4550#endif
3964 const char *err_msg = "%s: no such job"; 4551 const char *err_msg = "%s: no such job";
3965 unsigned num; 4552 unsigned num;
3966 int c; 4553 int c;
@@ -4005,6 +4592,7 @@ getjob(const char *name, int getctl)
4005 } 4592 }
4006 } 4593 }
4007 4594
4595#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4008 found = NULL; 4596 found = NULL;
4009 while (jp) { 4597 while (jp) {
4010 if (*p == '?' 4598 if (*p == '?'
@@ -4021,6 +4609,9 @@ getjob(const char *name, int getctl)
4021 if (!found) 4609 if (!found)
4022 goto err; 4610 goto err;
4023 jp = found; 4611 jp = found;
4612#else
4613 goto err;
4614#endif
4024 4615
4025 gotit: 4616 gotit:
4026#if JOBS 4617#if JOBS
@@ -4039,14 +4630,18 @@ getjob(const char *name, int getctl)
4039static void 4630static void
4040freejob(struct job *jp) 4631freejob(struct job *jp)
4041{ 4632{
4633#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4042 struct procstat *ps; 4634 struct procstat *ps;
4043 int i; 4635 int i;
4636#endif
4044 4637
4045 INT_OFF; 4638 INT_OFF;
4639#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4046 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { 4640 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
4047 if (ps->ps_cmd != nullstr) 4641 if (ps->ps_cmd != nullstr)
4048 free(ps->ps_cmd); 4642 free(ps->ps_cmd);
4049 } 4643 }
4644#endif
4050 if (jp->ps != &jp->ps0) 4645 if (jp->ps != &jp->ps0)
4051 free(jp->ps); 4646 free(jp->ps);
4052 jp->used = 0; 4647 jp->used = 0;
@@ -4139,7 +4734,9 @@ setjobctl(int on)
4139 ttyfd = fd; 4734 ttyfd = fd;
4140 doing_jobctl = on; 4735 doing_jobctl = on;
4141} 4736}
4737#endif
4142 4738
4739#if JOBS || JOBS_WIN32
4143static int FAST_FUNC 4740static int FAST_FUNC
4144killcmd(int argc, char **argv) 4741killcmd(int argc, char **argv)
4145{ 4742{
@@ -4169,8 +4766,10 @@ killcmd(int argc, char **argv)
4169 * sh -c 'true|sleep 1 & sleep 2; kill %1' 4766 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4170 */ 4767 */
4171 n = jp->nprocs; /* can't be 0 (I hope) */ 4768 n = jp->nprocs; /* can't be 0 (I hope) */
4769#if !ENABLE_PLATFORM_MINGW32
4172 if (jp->jobctl) 4770 if (jp->jobctl)
4173 n = 1; 4771 n = 1;
4772#endif
4174 dst = alloca(n * sizeof(int)*4); 4773 dst = alloca(n * sizeof(int)*4);
4175 argv[i] = dst; 4774 argv[i] = dst;
4176 for (j = 0; j < n; j++) { 4775 for (j = 0; j < n; j++) {
@@ -4185,7 +4784,11 @@ killcmd(int argc, char **argv)
4185 * leading space. Needed to not confuse 4784 * leading space. Needed to not confuse
4186 * negative pids with "kill -SIGNAL_NO" syntax 4785 * negative pids with "kill -SIGNAL_NO" syntax
4187 */ 4786 */
4787#if !ENABLE_PLATFORM_MINGW32
4188 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); 4788 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4789#else
4790 dst += sprintf(dst, " -%u", (int)ps->ps_pid);
4791#endif
4189 } 4792 }
4190 *dst = '\0'; 4793 *dst = '\0';
4191 } 4794 }
@@ -4193,7 +4796,9 @@ killcmd(int argc, char **argv)
4193 } 4796 }
4194 return kill_main(argc, argv); 4797 return kill_main(argc, argv);
4195} 4798}
4799#endif
4196 4800
4801#if JOBS
4197static void 4802static void
4198showpipe(struct job *jp /*, FILE *out*/) 4803showpipe(struct job *jp /*, FILE *out*/)
4199{ 4804{
@@ -4298,6 +4903,65 @@ sprint_status48(char *os, int status, int sigonly)
4298 return s - os; 4903 return s - os;
4299} 4904}
4300 4905
4906#if ENABLE_PLATFORM_MINGW32
4907static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
4908{
4909 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
4910# if ENABLE_FEATURE_EDITING
4911 bb_got_signal = SIGINT; /* for read_line_input: "we got a signal" */
4912# endif
4913 if (!suppress_int && !(rootshell && iflag))
4914 raise_interrupt();
4915 pending_int = 1;
4916 return TRUE;
4917 }
4918 return FALSE;
4919}
4920
4921/*
4922 * Windows does not know about parent-child relationship
4923 * They don't support waitpid(-1)
4924 */
4925static pid_t
4926waitpid_child(int *status, int wait_flags)
4927{
4928 struct job *jb;
4929 int pid_nr = 0;
4930 static HANDLE *proclist = NULL;
4931 static int pid_max = 0;
4932 pid_t pid = -1;
4933 DWORD win_status, idx;
4934 int i;
4935
4936 for (jb = curjob; jb; jb = jb->prev_job) {
4937 if (jb->state != JOBDONE) {
4938 if (pid_nr + jb->nprocs > pid_max) {
4939 pid_max = pid_nr + jb->nprocs;
4940 proclist = ckrealloc(proclist, sizeof(*proclist) * pid_max);
4941 }
4942
4943 for (i = 0; i < jb->nprocs; ++i) {
4944 if (jb->ps[i].ps_proc) {
4945 proclist[pid_nr++] = jb->ps[i].ps_proc;
4946 }
4947 }
4948 }
4949 }
4950
4951 if (pid_nr) {
4952 idx = WaitForMultipleObjects(pid_nr, proclist, FALSE,
4953 wait_flags & WNOHANG ? 0 : INFINITE);
4954 if (idx < pid_nr) {
4955 GetExitCodeProcess(proclist[idx], &win_status);
4956 *status = exit_code_to_wait_status(win_status);
4957 pid = GetProcessId(proclist[idx]);
4958 }
4959 }
4960 return pid;
4961}
4962#define waitpid(p, s, f) waitpid_child(s, f)
4963#endif
4964
4301#define DOWAIT_NONBLOCK 0 4965#define DOWAIT_NONBLOCK 0
4302#define DOWAIT_BLOCK 1 4966#define DOWAIT_BLOCK 1
4303#define DOWAIT_BLOCK_OR_SIG 2 4967#define DOWAIT_BLOCK_OR_SIG 2
@@ -4308,6 +4972,7 @@ sprint_status48(char *os, int status, int sigonly)
4308static int 4972static int
4309waitproc(int block, int *status) 4973waitproc(int block, int *status)
4310{ 4974{
4975#if !ENABLE_PLATFORM_MINGW32
4311 sigset_t oldmask; 4976 sigset_t oldmask;
4312 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; 4977 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4313 int err; 4978 int err;
@@ -4338,6 +5003,11 @@ waitproc(int block, int *status)
4338 } while (got_sigchld); 5003 } while (got_sigchld);
4339 5004
4340 return err; 5005 return err;
5006#else
5007 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
5008 *status = 0;
5009 return waitpid(-1, status, flags);
5010#endif
4341} 5011}
4342 5012
4343static int 5013static int
@@ -4394,6 +5064,10 @@ waitone(int block, struct job *job)
4394 jobno(jp), pid, ps->ps_status, status)); 5064 jobno(jp), pid, ps->ps_status, status));
4395 ps->ps_status = status; 5065 ps->ps_status = status;
4396 thisjob = jp; 5066 thisjob = jp;
5067#if ENABLE_PLATFORM_MINGW32
5068 CloseHandle(ps->ps_proc);
5069 ps->ps_proc = NULL;
5070#endif
4397 } 5071 }
4398 if (ps->ps_status == -1) 5072 if (ps->ps_status == -1)
4399 jobstate = JOBRUNNING; 5073 jobstate = JOBRUNNING;
@@ -4456,6 +5130,7 @@ waitone(int block, struct job *job)
4456static int 5130static int
4457dowait(int block, struct job *jp) 5131dowait(int block, struct job *jp)
4458{ 5132{
5133#if !ENABLE_PLATFORM_MINGW32
4459 smallint gotchld = *(volatile smallint *)&got_sigchld; 5134 smallint gotchld = *(volatile smallint *)&got_sigchld;
4460 int rpid; 5135 int rpid;
4461 int pid; 5136 int pid;
@@ -4477,9 +5152,17 @@ dowait(int block, struct job *jp)
4477 } while (pid >= 0); 5152 } while (pid >= 0);
4478 5153
4479 return rpid; 5154 return rpid;
5155#else
5156 int pid = 1;
5157
5158 while (jp ? jp->state == JOBRUNNING : pid > 0)
5159 pid = waitone(block, jp);
5160
5161 return pid;
5162#endif
4480} 5163}
4481 5164
4482#if JOBS 5165#if JOBS || JOBS_WIN32
4483static void 5166static void
4484showjob(struct job *jp, int mode) 5167showjob(struct job *jp, int mode)
4485{ 5168{
@@ -4494,7 +5177,7 @@ showjob(struct job *jp, int mode)
4494 5177
4495 if (mode & SHOW_ONLY_PGID) { /* jobs -p */ 5178 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
4496 /* just output process (group) id of pipeline */ 5179 /* just output process (group) id of pipeline */
4497 fprintf(out, "%d\n", ps->ps_pid); 5180 fprintf(out, "%"PID_FMT"d\n", ps->ps_pid);
4498 return; 5181 return;
4499 } 5182 }
4500 5183
@@ -4507,7 +5190,7 @@ showjob(struct job *jp, int mode)
4507 s[col - 3] = '-'; 5190 s[col - 3] = '-';
4508 5191
4509 if (mode & SHOW_PIDS) 5192 if (mode & SHOW_PIDS)
4510 col += fmtstr(s + col, 16, "%d ", ps->ps_pid); 5193 col += fmtstr(s + col, 16, "%"PID_FMT"d ", ps->ps_pid);
4511 5194
4512 psend = ps + jp->nprocs; 5195 psend = ps + jp->nprocs;
4513 5196
@@ -4516,8 +5199,10 @@ showjob(struct job *jp, int mode)
4516 col += sizeof("Running") - 1; 5199 col += sizeof("Running") - 1;
4517 } else { 5200 } else {
4518 int status = psend[-1].ps_status; 5201 int status = psend[-1].ps_status;
5202#if !ENABLE_PLATFORM_MINGW32
4519 if (jp->state == JOBSTOPPED) 5203 if (jp->state == JOBSTOPPED)
4520 status = jp->stopstatus; 5204 status = jp->stopstatus;
5205#endif
4521 col += sprint_status48(s + col, status, 0); 5206 col += sprint_status48(s + col, status, 0);
4522 } 5207 }
4523 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ 5208 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
@@ -4536,14 +5221,18 @@ showjob(struct job *jp, int mode)
4536 s[0] = '\0'; 5221 s[0] = '\0';
4537 col = 33; 5222 col = 33;
4538 if (mode & SHOW_PIDS) 5223 if (mode & SHOW_PIDS)
4539 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; 5224 col = fmtstr(s, 48, "\n%*c%"PID_FMT"d ", indent_col, ' ', ps->ps_pid) - 1;
4540 start: 5225 start:
5226#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4541 fprintf(out, "%s%*c%s%s", 5227 fprintf(out, "%s%*c%s%s",
4542 s, 5228 s,
4543 33 - col >= 0 ? 33 - col : 0, ' ', 5229 33 - col >= 0 ? 33 - col : 0, ' ',
4544 ps == jp->ps ? "" : "| ", 5230 ps == jp->ps ? "" : "| ",
4545 ps->ps_cmd 5231 ps->ps_cmd
4546 ); 5232 );
5233#else
5234 fprintf(out, "%s", s);
5235#endif
4547 } while (++ps != psend); 5236 } while (++ps != psend);
4548 newline_and_flush(out); 5237 newline_and_flush(out);
4549 5238
@@ -4628,7 +5317,7 @@ getstatus(struct job *job)
4628 { 5317 {
4629 /* XXX: limits number of signals */ 5318 /* XXX: limits number of signals */
4630 retval = WTERMSIG(status); 5319 retval = WTERMSIG(status);
4631#if JOBS 5320#if JOBS || ENABLE_PLATFORM_MINGW32
4632 if (retval == SIGINT) 5321 if (retval == SIGINT)
4633 job->sigint = 1; 5322 job->sigint = 1;
4634#endif 5323#endif
@@ -4800,7 +5489,7 @@ makejob(/*union node *node,*/ int nprocs)
4800 break; 5489 break;
4801 if (jp->state != JOBDONE || !jp->waited) 5490 if (jp->state != JOBDONE || !jp->waited)
4802 continue; 5491 continue;
4803#if JOBS 5492#if JOBS || JOBS_WIN32
4804 if (doing_jobctl) 5493 if (doing_jobctl)
4805 continue; 5494 continue;
4806#endif 5495#endif
@@ -4826,7 +5515,7 @@ makejob(/*union node *node,*/ int nprocs)
4826 return jp; 5515 return jp;
4827} 5516}
4828 5517
4829#if JOBS 5518#if JOBS || JOBS_WIN32
4830/* 5519/*
4831 * Return a string identifying a command (to be printed by the 5520 * Return a string identifying a command (to be printed by the
4832 * jobs command). 5521 * jobs command).
@@ -5168,6 +5857,7 @@ clear_traps(void)
5168 INT_ON; 5857 INT_ON;
5169} 5858}
5170 5859
5860#if !ENABLE_PLATFORM_MINGW32
5171/* Lives far away from here, needed for forkchild */ 5861/* Lives far away from here, needed for forkchild */
5172static void closescript(void); 5862static void closescript(void);
5173 5863
@@ -5294,14 +5984,22 @@ forkchild(struct job *jp, union node *n, int mode)
5294 for (jp = curjob; jp; jp = jp->prev_job) 5984 for (jp = curjob; jp; jp = jp->prev_job)
5295 freejob(jp); 5985 freejob(jp);
5296} 5986}
5987#endif
5297 5988
5298/* Called after fork(), in parent */ 5989/* Called after fork(), in parent */
5299#if !JOBS 5990#if !JOBS && !JOBS_WIN32
5300#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) 5991#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5301#endif 5992#endif
5302static void 5993static void
5994#if !ENABLE_PLATFORM_MINGW32
5303forkparent(struct job *jp, union node *n, int mode, pid_t pid) 5995forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5996#else
5997forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
5998#endif
5304{ 5999{
6000#if ENABLE_PLATFORM_MINGW32
6001 pid_t pid = GetProcessId(proc);
6002#endif
5305 TRACE(("In parent shell: child = %d\n", pid)); 6003 TRACE(("In parent shell: child = %d\n", pid));
5306 if (!jp) /* jp is NULL when called by openhere() for heredoc support */ 6004 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
5307 return; 6005 return;
@@ -5320,19 +6018,29 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5320 if (mode == FORK_BG) { 6018 if (mode == FORK_BG) {
5321 backgndpid = pid; /* set $! */ 6019 backgndpid = pid; /* set $! */
5322 set_curjob(jp, CUR_RUNNING); 6020 set_curjob(jp, CUR_RUNNING);
6021#if ENABLE_PLATFORM_MINGW32
6022 if (iflag && jp && jp->nprocs == 0)
6023 fprintf(stderr, "[%d] %"PID_FMT"d\n", jobno(jp), pid);
6024#endif
5323 } 6025 }
5324 if (jp) { 6026 if (jp) {
5325 struct procstat *ps = &jp->ps[jp->nprocs++]; 6027 struct procstat *ps = &jp->ps[jp->nprocs++];
5326 ps->ps_pid = pid; 6028 ps->ps_pid = pid;
5327 ps->ps_status = -1; 6029 ps->ps_status = -1;
6030#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
5328 ps->ps_cmd = nullstr; 6031 ps->ps_cmd = nullstr;
5329#if JOBS 6032#endif
6033#if ENABLE_PLATFORM_MINGW32
6034 ps->ps_proc = proc;
6035#endif
6036#if JOBS || JOBS_WIN32
5330 if (doing_jobctl && n) 6037 if (doing_jobctl && n)
5331 ps->ps_cmd = commandtext(n); 6038 ps->ps_cmd = commandtext(n);
5332#endif 6039#endif
5333 } 6040 }
5334} 6041}
5335 6042
6043#if !ENABLE_PLATFORM_MINGW32
5336/* jp and n are NULL when called by openhere() for heredoc support */ 6044/* jp and n are NULL when called by openhere() for heredoc support */
5337static int 6045static int
5338forkshell(struct job *jp, union node *n, int mode) 6046forkshell(struct job *jp, union node *n, int mode)
@@ -5355,6 +6063,7 @@ forkshell(struct job *jp, union node *n, int mode)
5355 } 6063 }
5356 return pid; 6064 return pid;
5357} 6065}
6066#endif
5358 6067
5359/* 6068/*
5360 * Wait for job to finish. 6069 * Wait for job to finish.
@@ -5418,6 +6127,10 @@ waitforjob(struct job *jp)
5418 return exitstatus; 6127 return exitstatus;
5419 6128
5420 st = getstatus(jp); 6129 st = getstatus(jp);
6130#if ENABLE_PLATFORM_MINGW32
6131 if (!jp->sigint && iflag && rootshell)
6132 pending_int = 0;
6133#endif
5421#if JOBS 6134#if JOBS
5422 if (jp->jobctl) { 6135 if (jp->jobctl) {
5423 xtcsetpgrp(ttyfd, rootpid); 6136 xtcsetpgrp(ttyfd, rootpid);
@@ -5443,6 +6156,7 @@ waitforjob(struct job *jp)
5443/* 6156/*
5444 * return 1 if there are stopped jobs, otherwise 0 6157 * return 1 if there are stopped jobs, otherwise 0
5445 */ 6158 */
6159#if !ENABLE_PLATFORM_MINGW32
5446static int 6160static int
5447stoppedjobs(void) 6161stoppedjobs(void)
5448{ 6162{
@@ -5461,7 +6175,17 @@ stoppedjobs(void)
5461 out: 6175 out:
5462 return retval; 6176 return retval;
5463} 6177}
5464 6178#else
6179static int
6180stoppedjobs(void)
6181{
6182 if (iflag && curjob) {
6183 out2str("You have background jobs.\n");
6184 return 1;
6185 }
6186 return 0;
6187}
6188#endif
5465 6189
5466/* 6190/*
5467 * Code for dealing with input/output redirection. 6191 * Code for dealing with input/output redirection.
@@ -5485,6 +6209,7 @@ openhere(union node *redir)
5485 char *p; 6209 char *p;
5486 int pip[2]; 6210 int pip[2];
5487 size_t len = 0; 6211 size_t len = 0;
6212 IF_PLATFORM_MINGW32(struct forkshell fs);
5488 6213
5489 if (pipe(pip) < 0) 6214 if (pipe(pip) < 0)
5490 ash_msg_and_raise_perror("can't create pipe"); 6215 ash_msg_and_raise_perror("can't create pipe");
@@ -5501,6 +6226,14 @@ openhere(union node *redir)
5501 goto out; 6226 goto out;
5502 } 6227 }
5503 6228
6229#if ENABLE_PLATFORM_MINGW32
6230 memset(&fs, 0, sizeof(fs));
6231 fs.fpid = FS_OPENHERE;
6232 fs.fd[0] = pip[0];
6233 fs.fd[1] = pip[1];
6234 fs.path = p;
6235 spawn_forkshell(&fs, NULL, NULL, FORK_NOJOB);
6236#else
5504 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { 6237 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
5505 /* child */ 6238 /* child */
5506 close(pip[0]); 6239 close(pip[0]);
@@ -5512,6 +6245,7 @@ openhere(union node *redir)
5512 xwrite(pip[1], p, len); 6245 xwrite(pip[1], p, len);
5513 _exit_SUCCESS(); 6246 _exit_SUCCESS();
5514 } 6247 }
6248#endif
5515 out: 6249 out:
5516 close(pip[1]); 6250 close(pip[1]);
5517 return pip[0]; 6251 return pip[0];
@@ -5589,6 +6323,9 @@ openredirect(union node *redir)
5589 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); 6323 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5590 if (f < 0) 6324 if (f < 0)
5591 goto ecreate; 6325 goto ecreate;
6326#if ENABLE_PLATFORM_MINGW32
6327 lseek(f, 0, SEEK_END);
6328#endif
5592 break; 6329 break;
5593 } 6330 }
5594 6331
@@ -5799,6 +6536,12 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
5799 if (fd == preverrout_fd) 6536 if (fd == preverrout_fd)
5800 preverrout_fd = new_fd; 6537 preverrout_fd = new_fd;
5801 6538
6539#if ENABLE_PLATFORM_MINGW32 && !defined(_UCRT)
6540 // Workaround for problems with stderr in MSVCRT
6541 if (fd == fileno(stderr))
6542 setvbuf(stderr, NULL, _IONBF, 0);
6543#endif
6544
5802 return 0; /* "we did not close fd" */ 6545 return 0; /* "we did not close fd" */
5803} 6546}
5804 6547
@@ -6152,6 +6895,9 @@ ifsbreakup(char *string, struct arglist *arglist)
6152 const char *ifs, *realifs; 6895 const char *ifs, *realifs;
6153 int ifsspc; 6896 int ifsspc;
6154 int nulonly; 6897 int nulonly;
6898#if ENABLE_PLATFORM_MINGW32
6899 int lshift = 0;
6900#endif
6155 6901
6156 start = string; 6902 start = string;
6157 if (ifslastp != NULL) { 6903 if (ifslastp != NULL) {
@@ -6162,7 +6908,33 @@ ifsbreakup(char *string, struct arglist *arglist)
6162 do { 6908 do {
6163 int afternul; 6909 int afternul;
6164 6910
6911#if ENABLE_PLATFORM_MINGW32
6912 /* Adjust region offsets for left-shifted string. */
6913 ifsp->begoff -= lshift;
6914 ifsp->endoff -= lshift;
6915#endif
6165 p = string + ifsp->begoff; 6916 p = string + ifsp->begoff;
6917#if ENABLE_PLATFORM_MINGW32
6918 if (ifsp->endoff > ifsp->begoff + 1) {
6919 /* Transform CRLF to LF. Skip regions having zero or
6920 * one characters: they can't contain CRLF. If the
6921 * region shrinks shift the rest of the string left. */
6922 int oldlen = ifsp->endoff - ifsp->begoff;
6923 int newlen = remove_cr(p, oldlen);
6924 int delta = oldlen - newlen;
6925
6926 if (delta > 0) {
6927 char *t = string + ifsp->endoff;
6928 char *s = string + ifsp->endoff - delta;
6929
6930 while (*t)
6931 *s++ = *t++;
6932 *s = '\0';
6933 lshift += delta;
6934 ifsp->endoff -= delta;
6935 }
6936 }
6937#endif
6166 afternul = nulonly; 6938 afternul = nulonly;
6167 nulonly = ifsp->nulonly; 6939 nulonly = ifsp->nulonly;
6168 ifs = nulonly ? nullstr : realifs; 6940 ifs = nulonly ? nullstr : realifs;
@@ -6607,6 +7379,7 @@ evalbackcmd(union node *n, struct backcmd *result
6607 const int ip = 0; 7379 const int ip = 0;
6608 const int ic = 1; 7380 const int ic = 1;
6609#endif 7381#endif
7382 IF_PLATFORM_MINGW32(struct forkshell fs);
6610 7383
6611 result->fd = -1; 7384 result->fd = -1;
6612 result->buf = NULL; 7385 result->buf = NULL;
@@ -6620,6 +7393,15 @@ evalbackcmd(union node *n, struct backcmd *result
6620 ash_msg_and_raise_perror("can't create pipe"); 7393 ash_msg_and_raise_perror("can't create pipe");
6621 /* process substitution uses NULL job, like openhere() */ 7394 /* process substitution uses NULL job, like openhere() */
6622 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; 7395 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;
7396#if ENABLE_PLATFORM_MINGW32
7397 memset(&fs, 0, sizeof(fs));
7398 fs.fpid = FS_EVALBACKCMD;
7399 fs.n = n;
7400 fs.fd[0] = pip[0];
7401 fs.fd[1] = pip[1];
7402 fs.fd[2] = ctl;
7403 spawn_forkshell(&fs, jp, n, FORK_NOJOB);
7404#else
6623 if (forkshell(jp, n, FORK_NOJOB) == 0) { 7405 if (forkshell(jp, n, FORK_NOJOB) == 0) {
6624 /* child */ 7406 /* child */
6625 FORCE_INT_ON; 7407 FORCE_INT_ON;
@@ -6643,6 +7425,7 @@ evalbackcmd(union node *n, struct backcmd *result
6643 evaltreenr(n, EV_EXIT); 7425 evaltreenr(n, EV_EXIT);
6644 /* NOTREACHED */ 7426 /* NOTREACHED */
6645 } 7427 }
7428#endif
6646 /* parent */ 7429 /* parent */
6647#if BASH_PROCESS_SUBST 7430#if BASH_PROCESS_SUBST
6648 if (ctl != CTLBACKQ) { 7431 if (ctl != CTLBACKQ) {
@@ -6721,8 +7504,14 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
6721 7504
6722 /* Eat all trailing newlines */ 7505 /* Eat all trailing newlines */
6723 dest = expdest; 7506 dest = expdest;
6724 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) 7507 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) {
6725 STUNPUTC(dest); 7508 STUNPUTC(dest);
7509#if ENABLE_PLATFORM_MINGW32
7510 if (dest > ((char *)stackblock() + startloc) && dest[-1] == '\r') {
7511 STUNPUTC(dest);
7512 }
7513#endif
7514 }
6726 expdest = dest; 7515 expdest = dest;
6727 7516
6728 if (!(flag & EXP_QUOTED)) 7517 if (!(flag & EXP_QUOTED))
@@ -7869,6 +8658,26 @@ expandmeta(struct strlist *str /*, int flag*/)
7869#else 8658#else
7870/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */ 8659/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
7871 8660
8661#if ENABLE_ASH_GLOB_OPTIONS
8662static int FAST_FUNC
8663ash_accept_glob(const char *name)
8664{
8665 struct stat st;
8666
8667 if (nohiddenglob || nohidsysglob) {
8668 if (!lstat(name, &st)) {
8669 if ((st.st_attr & FILE_ATTRIBUTE_HIDDEN)) {
8670 if (nohiddenglob ||
8671 (st.st_attr & FILE_ATTRIBUTE_SYSTEM)) {
8672 return FALSE;
8673 }
8674 }
8675 }
8676 }
8677 return TRUE;
8678}
8679#endif
8680
7872/* 8681/*
7873 * Do metacharacter (i.e. *, ?, [...]) expansion. 8682 * Do metacharacter (i.e. *, ?, [...]) expansion.
7874 */ 8683 */
@@ -7896,6 +8705,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7896 8705
7897 metaflag = 0; 8706 metaflag = 0;
7898 start = name; 8707 start = name;
8708#if ENABLE_PLATFORM_MINGW32
8709 if (expdir_len == 0 && has_dos_drive_prefix(start) && start[2] != '/')
8710 start += 2;
8711#endif
7899 for (p = name; esc = 0, *p; p += esc + 1) { 8712 for (p = name; esc = 0, *p; p += esc + 1) {
7900 if (*p == '*' || *p == '?') 8713 if (*p == '*' || *p == '?')
7901 metaflag = 1; 8714 metaflag = 1;
@@ -7970,8 +8783,16 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7970 while (!pending_int && (dp = readdir(dirp)) != NULL) { 8783 while (!pending_int && (dp = readdir(dirp)) != NULL) {
7971 if (dp->d_name[0] == '.' && !matchdot) 8784 if (dp->d_name[0] == '.' && !matchdot)
7972 continue; 8785 continue;
8786#if ENABLE_ASH_GLOB_OPTIONS
8787# undef pmatch
8788# define pmatch(a, b) !fnmatch((a), (b), nocaseglob ? FNM_CASEFOLD : 0)
8789#endif
7973 if (pmatch(start, dp->d_name)) { 8790 if (pmatch(start, dp->d_name)) {
7974 if (atend) { 8791 if (atend) {
8792#if ENABLE_ASH_GLOB_OPTIONS
8793 if (!ash_accept_glob(dp->d_name))
8794 continue;
8795#endif
7975 strcpy(enddir, dp->d_name); 8796 strcpy(enddir, dp->d_name);
7976 addfname(expdir); 8797 addfname(expdir);
7977 } else { 8798 } else {
@@ -7999,6 +8820,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7999 endname[-esc - 1] = esc ? '\\' : '/'; 8820 endname[-esc - 1] = esc ? '\\' : '/';
8000#undef expdir 8821#undef expdir
8001#undef expdir_max 8822#undef expdir_max
8823#if ENABLE_ASH_GLOB_OPTIONS
8824# undef pmatch
8825# define pmatch(a, b) !fnmatch((a), (b), 0)
8826#endif
8002} 8827}
8003 8828
8004static struct strlist * 8829static struct strlist *
@@ -8271,14 +9096,40 @@ static int builtinloc = -1; /* index in path of %builtin, or -1 */
8271 9096
8272 9097
8273static void 9098static void
9099#if ENABLE_PLATFORM_MINGW32
9100tryexec(IF_FEATURE_SH_STANDALONE(int applet_no, const char *path, int noexec,)
9101 const char *cmd, char **argv, char **envp)
9102#else
8274tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp) 9103tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp)
9104#endif
8275{ 9105{
9106#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
9107 interp_t interp;
9108#endif
8276#if ENABLE_FEATURE_SH_STANDALONE 9109#if ENABLE_FEATURE_SH_STANDALONE
8277 if (applet_no >= 0) { 9110 if (applet_no >= 0) {
9111# if ENABLE_PLATFORM_MINGW32
9112 /* Treat all applets as NOEXEC, including the shell itself
9113 * if we were called from forkshell_shellexec(). */
9114 run_noexec:
9115 if (applet_main[applet_no] != ash_main || noexec) {
9116 /* mingw-w64's getopt() uses __argv[0] as the program name */
9117 __argv[0] = (char *)cmd;
9118 /* 'which' wants to know if it was invoked from a standalone
9119 * shell. 'Which' in argv[0] indicates this. */
9120 if (strcmp(argv[0], "which") == 0) {
9121 argv[0] = (char *)"Which";
9122 }
9123# else
8278 if (APPLET_IS_NOEXEC(applet_no)) { 9124 if (APPLET_IS_NOEXEC(applet_no)) {
9125# endif
9126#if !ENABLE_PLATFORM_MINGW32 || !defined(_UCRT)
9127 /* If building for UCRT move this up into shellexec() to
9128 * work around a bug. */
8279 clearenv(); 9129 clearenv();
8280 while (*envp) 9130 while (*envp)
8281 putenv(*envp++); 9131 putenv(*envp++);
9132#endif
8282 popredir(/*drop:*/ 1); 9133 popredir(/*drop:*/ 1);
8283 run_noexec_applet_and_exit(applet_no, cmd, argv); 9134 run_noexec_applet_and_exit(applet_no, cmd, argv);
8284 } 9135 }
@@ -8289,6 +9140,44 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8289 } 9140 }
8290#endif 9141#endif
8291 9142
9143#if ENABLE_PLATFORM_MINGW32
9144 /* Workaround for libtool, which assumes the host is an MSYS2
9145 * environment and requires special-case escaping for cmd.exe.
9146 * https://github.com/skeeto/w64devkit/issues/50 */
9147 if (string_array_len(argv) >= 3 &&
9148 strcmp(argv[0], "cmd") == 0 &&
9149 strcmp(argv[1], "//c") == 0 &&
9150 strcmp(argv[2], "echo") == 0) {
9151 argv[1]++; /* drop extra slash */
9152 }
9153
9154 /* cmd was allocated on the stack with room for an extension */
9155 add_win32_extension((char *)cmd);
9156
9157# if ENABLE_FEATURE_SH_STANDALONE
9158 /* If the command is a script with an interpreter which is an
9159 * applet, we can run it as if it were a noexec applet. */
9160 if (parse_interpreter(cmd, &interp)) {
9161 applet_no = find_applet_by_name_for_sh(interp.name, path);
9162 if (applet_no >= 0) {
9163 argv[0] = (char *)cmd;
9164 /* evalcommand()/spawn_forkshell() add two elements before argv */
9165 if (interp.opts) {
9166 argv--;
9167 argv[0] = (char *)interp.opts;
9168 }
9169 argv--;
9170 cmd = argv[0] = (char *)interp.name;
9171 /* Identify the index of the script file in argv */
9172 set_interp(1 + (interp.opts != NULL));
9173 goto run_noexec;
9174 }
9175 }
9176# endif
9177
9178 execve(cmd, argv, envp);
9179 /* skip POSIX-mandated retry on ENOEXEC */
9180#else /* !ENABLE_PLATFORM_MINGW32 */
8292 repeat: 9181 repeat:
8293#ifdef SYSV 9182#ifdef SYSV
8294 do { 9183 do {
@@ -8324,15 +9213,23 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8324 argv[0] = (char*) "ash"; 9213 argv[0] = (char*) "ash";
8325 goto repeat; 9214 goto repeat;
8326 } 9215 }
9216#endif /* !ENABLE_PLATFORM_MINGW32 */
8327} 9217}
8328 9218
9219#if !ENABLE_PLATFORM_MINGW32 || !ENABLE_FEATURE_SH_STANDALONE
9220# define shellexec(prg, a, pth, i, n) shellexec(prg, a, pth, i)
9221#endif
9222
8329/* 9223/*
8330 * Exec a program. Never returns. If you change this routine, you may 9224 * Exec a program. Never returns. If you change this routine, you may
8331 * have to change the find_command routine as well. 9225 * have to change the find_command routine as well.
8332 * argv[-1] must exist and be writable! See tryexec() for why. 9226 * argv[-1] must exist and be writable! See tryexec() for why.
8333 */ 9227 */
8334static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; 9228static struct builtincmd *find_builtin(const char *name);
8335static void shellexec(char *prog, char **argv, const char *path, int idx) 9229static void shellexec(char *prog, char **argv, const char *path, int idx,
9230 int noexec) NORETURN;
9231static void shellexec(char *prog, char **argv, const char *path, int idx,
9232 int noexec)
8336{ 9233{
8337 char *cmdname; 9234 char *cmdname;
8338 int e; 9235 int e;
@@ -8341,12 +9238,30 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8341 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ 9238 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
8342 9239
8343 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 9240 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
9241#if ENABLE_FEATURE_SH_STANDALONE && ENABLE_PLATFORM_MINGW32 && defined(_UCRT)
9242 /* Avoid UCRT bug by updating parent's environment and passing a
9243 * NULL environment pointer to execve(). */
9244 clearenv();
9245 while (*envp)
9246 putenv(*envp++);
9247 envp = NULL;
9248#endif
9249#if !ENABLE_PLATFORM_MINGW32
8344 if (strchr(prog, '/') != NULL 9250 if (strchr(prog, '/') != NULL
9251#else
9252 if (has_path(prog)
9253#endif
8345#if ENABLE_FEATURE_SH_STANDALONE 9254#if ENABLE_FEATURE_SH_STANDALONE
8346 || (applet_no = find_applet_by_name(prog)) >= 0 9255 || (applet_no = find_applet_by_name_for_sh(prog, path)) >= 0
8347#endif 9256#endif
8348 ) { 9257 ) {
9258#if ENABLE_PLATFORM_MINGW32
9259 char *progext = stack_add_ext_space(prog);
9260 tryexec(IF_FEATURE_SH_STANDALONE(applet_no, path, noexec,)
9261 progext, argv, envp);
9262#else
8349 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp); 9263 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
9264#endif
8350 if (applet_no >= 0) { 9265 if (applet_no >= 0) {
8351 /* We tried execing ourself, but it didn't work. 9266 /* We tried execing ourself, but it didn't work.
8352 * Maybe /proc/self/exe doesn't exist? 9267 * Maybe /proc/self/exe doesn't exist?
@@ -8355,13 +9270,33 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8355 goto try_PATH; 9270 goto try_PATH;
8356 } 9271 }
8357 e = errno; 9272 e = errno;
9273#if ENABLE_PLATFORM_MINGW32
9274 if (unix_path(prog)) {
9275 const char *name = bb_basename(prog);
9276# if ENABLE_FEATURE_SH_STANDALONE
9277 if ((applet_no = find_applet_by_name_for_sh(name, path)) >= 0) {
9278 tryexec(applet_no, path, noexec, name, argv, envp);
9279 e = errno;
9280 }
9281# endif
9282 if (!find_builtin(name)) {
9283 argv[0] = (char *)name;
9284 goto try_PATH;
9285 }
9286 }
9287#endif
8358 } else { 9288 } else {
8359 try_PATH: 9289 try_PATH:
8360 e = ENOENT; 9290 e = ENOENT;
8361 while (padvance(&path, argv[0]) >= 0) { 9291 while (padvance(&path, argv[0]) >= 0) {
8362 cmdname = stackblock(); 9292 cmdname = stackblock();
8363 if (--idx < 0 && pathopt == NULL) { 9293 if (--idx < 0 && pathopt == NULL) {
9294#if ENABLE_PLATFORM_MINGW32
9295 tryexec(IF_FEATURE_SH_STANDALONE(-1, path, noexec,)
9296 cmdname, argv, envp);
9297#else
8364 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); 9298 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
9299#endif
8365 if (errno != ENOENT && errno != ENOTDIR) 9300 if (errno != ENOENT && errno != ENOTDIR)
8366 e = errno; 9301 e = errno;
8367 } 9302 }
@@ -8400,6 +9335,9 @@ printentry(struct tblentry *cmdp)
8400 padvance(&path, cmdp->cmdname); 9335 padvance(&path, cmdp->cmdname);
8401 } while (--idx >= 0); 9336 } while (--idx >= 0);
8402 name = stackblock(); 9337 name = stackblock();
9338#if ENABLE_PLATFORM_MINGW32
9339 add_win32_extension(name);
9340#endif
8403 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); 9341 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8404} 9342}
8405 9343
@@ -8600,7 +9538,7 @@ changepath(const char *newval)
8600 bltin = idx; 9538 bltin = idx;
8601 break; 9539 break;
8602 } 9540 }
8603 new = strchr(new, ':'); 9541 new = strchr(new, PATH_SEP);
8604 if (!new) 9542 if (!new)
8605 break; 9543 break;
8606 idx++; 9544 idx++;
@@ -8777,14 +9715,37 @@ describe_command(char *command, const char *path, int describe_command_verbose)
8777 case CMDNORMAL: { 9715 case CMDNORMAL: {
8778 int j = entry.u.index; 9716 int j = entry.u.index;
8779 char *p; 9717 char *p;
9718#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
9719 if (j < -1) {
9720 p = (char *)bb_basename(command);
9721 if (describe_command_verbose) {
9722 out1fmt(" is a builtin applet");
9723 } else {
9724 out1str(applet_to_exe(p));
9725 }
9726 break;
9727 }
9728#endif
8780 if (j < 0) { 9729 if (j < 0) {
9730#if ENABLE_PLATFORM_MINGW32
9731 p = stack_add_ext_space(command);
9732#else
8781 p = command; 9733 p = command;
9734#endif
8782 } else { 9735 } else {
9736#if ENABLE_PLATFORM_MINGW32
9737 if (unix_path(command))
9738 command = (char *)bb_basename(command);
9739#endif
8783 do { 9740 do {
8784 padvance(&path, command); 9741 padvance(&path, command);
8785 } while (--j >= 0); 9742 } while (--j >= 0);
8786 p = stackblock(); 9743 p = stackblock();
8787 } 9744 }
9745#if ENABLE_PLATFORM_MINGW32
9746 add_win32_extension(p);
9747 bs_to_slash(p);
9748#endif
8788 if (describe_command_verbose) { 9749 if (describe_command_verbose) {
8789 out1fmt(" is %s", p); 9750 out1fmt(" is %s", p);
8790 } else { 9751 } else {
@@ -8935,11 +9896,19 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8935} 9896}
8936#endif 9897#endif
8937 9898
8938
8939/*static int funcblocksize; // size of structures in function */ 9899/*static int funcblocksize; // size of structures in function */
8940/*static int funcstringsize; // size of strings in node */ 9900/*static int funcstringsize; // size of strings in node */
8941static void *funcblock; /* block to allocate function from */ 9901static void *funcblock; /* block to allocate function from */
8942static char *funcstring_end; /* end of block to allocate strings from */ 9902static char *funcstring_end; /* end of block to allocate strings from */
9903#if ENABLE_PLATFORM_MINGW32
9904static int fs_size;
9905static void *fs_start;
9906# if FORKSHELL_DEBUG
9907static void *fs_funcstring;
9908static const char **annot;
9909static char *annot_free;
9910# endif
9911#endif
8943 9912
8944static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 9913static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8945 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 9914 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -9072,14 +10041,79 @@ calcsize(int funcblocksize, union node *n)
9072} 10041}
9073 10042
9074static char * 10043static char *
9075nodeckstrdup(char *s) 10044nodeckstrdup(const char *s)
9076{ 10045{
10046#if ENABLE_PLATFORM_MINGW32
10047 if(!s)
10048 return NULL;
10049#endif
9077 funcstring_end -= SHELL_ALIGN(strlen(s) + 1); 10050 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
9078 return strcpy(funcstring_end, s); 10051 return strcpy(funcstring_end, s);
9079} 10052}
9080 10053
9081static union node *copynode(union node *); 10054static union node *copynode(union node *);
9082 10055
10056#if ENABLE_PLATFORM_MINGW32
10057# if FORKSHELL_DEBUG
10058# define MARK_PTR(dst,note,flag) forkshell_mark_ptr((void *)&dst, note, flag)
10059# else
10060# define MARK_PTR(dst,note,flag) forkshell_mark_ptr((void *)&dst)
10061# endif
10062
10063#define NO_FREE 0
10064#define FREE 1
10065
10066#if FORKSHELL_DEBUG
10067static void forkshell_mark_ptr(void *dst, const char *note, int flag)
10068#else
10069static void forkshell_mark_ptr(void *dst)
10070#endif
10071{
10072 char *lrelocate = (char *)fs_start + fs_size;
10073 int index = ((char *)dst - (char *)fs_start)/sizeof(char *);
10074
10075 lrelocate[index/8] |= 1 << (index % 8);
10076
10077#if FORKSHELL_DEBUG
10078 if (dst < fs_start || dst >= fs_funcstring) {
10079 fprintf(stderr, "dst (%p) out of range (%p %p)\n",
10080 dst, fs_start, fs_funcstring);
10081 }
10082 if (annot) {
10083 if (annot[index]) {
10084 fprintf(stderr, "duplicate annotation: %s %s\n",
10085 annot[index], note);
10086 }
10087 annot[index] = note;
10088 annot_free[index] = flag;
10089 }
10090#endif
10091}
10092
10093# define SAVE_PTR(dst,note,flag) { \
10094 if (fs_size) { \
10095 MARK_PTR(dst,note,flag); \
10096 } \
10097}
10098# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2) { \
10099 if (fs_size) { \
10100 MARK_PTR(dst1,note1,flag1); \
10101 MARK_PTR(dst2,note2,flag2); \
10102 } \
10103}
10104# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3) { \
10105 if (fs_size) { \
10106 MARK_PTR(dst1,note1,flag1); \
10107 MARK_PTR(dst2,note2,flag2); \
10108 MARK_PTR(dst3,note3,flag3); \
10109 } \
10110}
10111#else
10112# define SAVE_PTR(dst,note,flag)
10113# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2)
10114# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3)
10115#endif
10116
9083static struct nodelist * 10117static struct nodelist *
9084copynodelist(struct nodelist *lp) 10118copynodelist(struct nodelist *lp)
9085{ 10119{
@@ -9091,6 +10125,8 @@ copynodelist(struct nodelist *lp)
9091 *lpp = funcblock; 10125 *lpp = funcblock;
9092 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); 10126 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
9093 (*lpp)->n = copynode(lp->n); 10127 (*lpp)->n = copynode(lp->n);
10128 SAVE_PTR2((*lpp)->n, "(*lpp)->next", NO_FREE,
10129 (*lpp)->next, "(*lpp)->next", NO_FREE);
9094 lp = lp->next; 10130 lp = lp->next;
9095 lpp = &(*lpp)->next; 10131 lpp = &(*lpp)->next;
9096 } 10132 }
@@ -9114,10 +10150,14 @@ copynode(union node *n)
9114 new->ncmd.args = copynode(n->ncmd.args); 10150 new->ncmd.args = copynode(n->ncmd.args);
9115 new->ncmd.assign = copynode(n->ncmd.assign); 10151 new->ncmd.assign = copynode(n->ncmd.assign);
9116 new->ncmd.linno = n->ncmd.linno; 10152 new->ncmd.linno = n->ncmd.linno;
10153 SAVE_PTR3(new->ncmd.redirect, "ncmd.redirect", NO_FREE,
10154 new->ncmd.args, "ncmd.args", NO_FREE,
10155 new->ncmd.assign, "ncmd.assign", NO_FREE);
9117 break; 10156 break;
9118 case NPIPE: 10157 case NPIPE:
9119 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); 10158 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
9120 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; 10159 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
10160 SAVE_PTR(new->npipe.cmdlist, "npipe.cmdlist", NO_FREE);
9121 break; 10161 break;
9122 case NREDIR: 10162 case NREDIR:
9123 case NBACKGND: 10163 case NBACKGND:
@@ -9125,6 +10165,8 @@ copynode(union node *n)
9125 new->nredir.redirect = copynode(n->nredir.redirect); 10165 new->nredir.redirect = copynode(n->nredir.redirect);
9126 new->nredir.n = copynode(n->nredir.n); 10166 new->nredir.n = copynode(n->nredir.n);
9127 new->nredir.linno = n->nredir.linno; 10167 new->nredir.linno = n->nredir.linno;
10168 SAVE_PTR2(new->nredir.redirect, "nredir.redirect", NO_FREE,
10169 new->nredir.n, "nredir.n", NO_FREE);
9128 break; 10170 break;
9129 case NAND: 10171 case NAND:
9130 case NOR: 10172 case NOR:
@@ -9133,37 +10175,58 @@ copynode(union node *n)
9133 case NUNTIL: 10175 case NUNTIL:
9134 new->nbinary.ch2 = copynode(n->nbinary.ch2); 10176 new->nbinary.ch2 = copynode(n->nbinary.ch2);
9135 new->nbinary.ch1 = copynode(n->nbinary.ch1); 10177 new->nbinary.ch1 = copynode(n->nbinary.ch1);
10178 SAVE_PTR2(new->nbinary.ch1, "nbinary.ch1", NO_FREE,
10179 new->nbinary.ch2, "nbinary.ch2", NO_FREE);
9136 break; 10180 break;
9137 case NIF: 10181 case NIF:
9138 new->nif.elsepart = copynode(n->nif.elsepart); 10182 new->nif.elsepart = copynode(n->nif.elsepart);
9139 new->nif.ifpart = copynode(n->nif.ifpart); 10183 new->nif.ifpart = copynode(n->nif.ifpart);
9140 new->nif.test = copynode(n->nif.test); 10184 new->nif.test = copynode(n->nif.test);
10185 SAVE_PTR3(new->nif.elsepart, "nif.elsepart", NO_FREE,
10186 new->nif.ifpart, "nif.ifpart", NO_FREE,
10187 new->nif.test, "nif.test", NO_FREE);
9141 break; 10188 break;
9142 case NFOR: 10189 case NFOR:
9143 new->nfor.var = nodeckstrdup(n->nfor.var); 10190 new->nfor.var = nodeckstrdup(n->nfor.var);
9144 new->nfor.body = copynode(n->nfor.body); 10191 new->nfor.body = copynode(n->nfor.body);
9145 new->nfor.args = copynode(n->nfor.args); 10192 new->nfor.args = copynode(n->nfor.args);
9146 new->nfor.linno = n->nfor.linno; 10193 new->nfor.linno = n->nfor.linno;
10194 SAVE_PTR3(new->nfor.var,
10195 xasprintf("nfor.var '%s'", n->nfor.var ?: "NULL"), FREE,
10196 new->nfor.body, "nfor.body", NO_FREE,
10197 new->nfor.args, "nfor.args", NO_FREE);
9147 break; 10198 break;
9148 case NCASE: 10199 case NCASE:
9149 new->ncase.cases = copynode(n->ncase.cases); 10200 new->ncase.cases = copynode(n->ncase.cases);
9150 new->ncase.expr = copynode(n->ncase.expr); 10201 new->ncase.expr = copynode(n->ncase.expr);
9151 new->ncase.linno = n->ncase.linno; 10202 new->ncase.linno = n->ncase.linno;
10203 SAVE_PTR2(new->ncase.cases, "ncase.cases", NO_FREE,
10204 new->ncase.expr, "ncase.expr", NO_FREE);
9152 break; 10205 break;
9153 case NCLIST: 10206 case NCLIST:
9154 new->nclist.body = copynode(n->nclist.body); 10207 new->nclist.body = copynode(n->nclist.body);
9155 new->nclist.pattern = copynode(n->nclist.pattern); 10208 new->nclist.pattern = copynode(n->nclist.pattern);
9156 new->nclist.next = copynode(n->nclist.next); 10209 new->nclist.next = copynode(n->nclist.next);
10210 SAVE_PTR3(new->nclist.body, "nclist.body", NO_FREE,
10211 new->nclist.pattern, "nclist.pattern", NO_FREE,
10212 new->nclist.next, "nclist.next", NO_FREE);
9157 break; 10213 break;
9158 case NDEFUN: 10214 case NDEFUN:
9159 new->ndefun.body = copynode(n->ndefun.body); 10215 new->ndefun.body = copynode(n->ndefun.body);
9160 new->ndefun.text = nodeckstrdup(n->ndefun.text); 10216 new->ndefun.text = nodeckstrdup(n->ndefun.text);
9161 new->ndefun.linno = n->ndefun.linno; 10217 new->ndefun.linno = n->ndefun.linno;
10218 SAVE_PTR2(new->ndefun.body, "ndefun.body", NO_FREE,
10219 new->ndefun.text,
10220 xasprintf("ndefun.text '%s'", n->ndefun.text ?: "NULL"), FREE);
9162 break; 10221 break;
9163 case NARG: 10222 case NARG:
9164 new->narg.backquote = copynodelist(n->narg.backquote); 10223 new->narg.backquote = copynodelist(n->narg.backquote);
9165 new->narg.text = nodeckstrdup(n->narg.text); 10224 new->narg.text = nodeckstrdup(n->narg.text);
9166 new->narg.next = copynode(n->narg.next); 10225 new->narg.next = copynode(n->narg.next);
10226 SAVE_PTR3(new->narg.backquote, "narg.backquote", NO_FREE,
10227 new->narg.text,
10228 xasprintf("narg.text '%s'", n->narg.text ?: "NULL"), FREE,
10229 new->narg.next, "narg.next", NO_FREE);
9167 break; 10230 break;
9168 case NTO: 10231 case NTO:
9169#if BASH_REDIR_OUTPUT 10232#if BASH_REDIR_OUTPUT
@@ -9176,6 +10239,8 @@ copynode(union node *n)
9176 new->nfile.fname = copynode(n->nfile.fname); 10239 new->nfile.fname = copynode(n->nfile.fname);
9177 new->nfile.fd = n->nfile.fd; 10240 new->nfile.fd = n->nfile.fd;
9178 new->nfile.next = copynode(n->nfile.next); 10241 new->nfile.next = copynode(n->nfile.next);
10242 SAVE_PTR2(new->nfile.fname, "nfile.fname", NO_FREE,
10243 new->nfile.next, "nfile.next", NO_FREE);
9179 break; 10244 break;
9180 case NTOFD: 10245 case NTOFD:
9181 case NFROMFD: 10246 case NFROMFD:
@@ -9183,15 +10248,20 @@ copynode(union node *n)
9183 new->ndup.dupfd = n->ndup.dupfd; 10248 new->ndup.dupfd = n->ndup.dupfd;
9184 new->ndup.fd = n->ndup.fd; 10249 new->ndup.fd = n->ndup.fd;
9185 new->ndup.next = copynode(n->ndup.next); 10250 new->ndup.next = copynode(n->ndup.next);
10251 SAVE_PTR2(new->ndup.vname, "ndup.vname", NO_FREE,
10252 new->ndup.next, "ndup.next", NO_FREE);
9186 break; 10253 break;
9187 case NHERE: 10254 case NHERE:
9188 case NXHERE: 10255 case NXHERE:
9189 new->nhere.doc = copynode(n->nhere.doc); 10256 new->nhere.doc = copynode(n->nhere.doc);
9190 new->nhere.fd = n->nhere.fd; 10257 new->nhere.fd = n->nhere.fd;
9191 new->nhere.next = copynode(n->nhere.next); 10258 new->nhere.next = copynode(n->nhere.next);
10259 SAVE_PTR2(new->nhere.doc, "nhere.doc", NO_FREE,
10260 new->nhere.next, "nhere.next", NO_FREE);
9192 break; 10261 break;
9193 case NNOT: 10262 case NNOT:
9194 new->nnot.com = copynode(n->nnot.com); 10263 new->nnot.com = copynode(n->nnot.com);
10264 SAVE_PTR(new->nnot.com, "nnot.com", NO_FREE);
9195 break; 10265 break;
9196 }; 10266 };
9197 new->type = n->type; 10267 new->type = n->type;
@@ -9212,6 +10282,7 @@ copyfunc(union node *n)
9212 f = ckzalloc(blocksize /* + funcstringsize */); 10282 f = ckzalloc(blocksize /* + funcstringsize */);
9213 funcblock = (char *) f + offsetof(struct funcnode, n); 10283 funcblock = (char *) f + offsetof(struct funcnode, n);
9214 funcstring_end = (char *) f + blocksize; 10284 funcstring_end = (char *) f + blocksize;
10285 IF_PLATFORM_MINGW32(fs_size = 0);
9215 copynode(n); 10286 copynode(n);
9216 /* f->count = 0; - ckzalloc did it */ 10287 /* f->count = 0; - ckzalloc did it */
9217 return f; 10288 return f;
@@ -9239,12 +10310,15 @@ defun(union node *func)
9239#define SKIPFUNCDEF (1 << 3) 10310#define SKIPFUNCDEF (1 << 3)
9240static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 10311static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
9241static int skipcount; /* number of levels to skip */ 10312static int skipcount; /* number of levels to skip */
10313#if ENABLE_PLATFORM_POSIX
9242static int loopnest; /* current loop nesting level */ 10314static int loopnest; /* current loop nesting level */
10315#endif
9243static int funcline; /* starting line number of current function, or 0 if not in a function */ 10316static int funcline; /* starting line number of current function, or 0 if not in a function */
9244 10317
9245/* Forward decl way out to parsing code - dotrap needs it */ 10318/* Forward decl way out to parsing code - dotrap needs it */
9246static int evalstring(char *s, int flags); 10319static int evalstring(char *s, int flags);
9247 10320
10321#if !ENABLE_PLATFORM_MINGW32
9248/* Called to execute a trap. 10322/* Called to execute a trap.
9249 * Single callsite - at the end of evaltree(). 10323 * Single callsite - at the end of evaltree().
9250 * If we return non-zero, evaltree raises EXEXIT exception. 10324 * If we return non-zero, evaltree raises EXEXIT exception.
@@ -9303,6 +10377,45 @@ dotrap(void)
9303 savestatus = last_status; 10377 savestatus = last_status;
9304 TRACE(("dotrap returns\n")); 10378 TRACE(("dotrap returns\n"));
9305} 10379}
10380#else
10381static void
10382dotrap(void)
10383{
10384 int status, last_status;
10385 char *p;
10386
10387 if (!pending_int)
10388 return;
10389
10390 status = savestatus;
10391 last_status = status;
10392 if (status < 0) {
10393 status = exitstatus;
10394 savestatus = status;
10395 }
10396 pending_int = 0;
10397 barrier();
10398
10399 TRACE(("dotrap entered\n"));
10400 if (evalskip) {
10401 pending_int = 1;
10402 return;
10403 }
10404
10405 p = trap[SIGINT];
10406 if (p) {
10407 TRACE(("sig %d is active, will run handler '%s'\n", SIGINT, p));
10408 trap_depth++;
10409 evalstring(p, 0);
10410 trap_depth--;
10411 if (evalskip != SKIPFUNC)
10412 exitstatus = status;
10413 }
10414
10415 savestatus = last_status;
10416 TRACE(("dotrap returns\n"));
10417}
10418#endif
9306 10419
9307/* forward declarations - evaluation is fairly recursive business... */ 10420/* forward declarations - evaluation is fairly recursive business... */
9308static int evalloop(union node *, int); 10421static int evalloop(union node *, int);
@@ -9588,19 +10701,36 @@ evalcase(union node *n, int flags)
9588static int 10701static int
9589evalsubshell(union node *n, int flags) 10702evalsubshell(union node *n, int flags)
9590{ 10703{
10704 IF_PLATFORM_MINGW32(struct forkshell fs;)
9591 struct job *jp; 10705 struct job *jp;
9592 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ 10706 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9593 int status; 10707 int status;
9594 10708
9595 errlinno = lineno = n->nredir.linno; 10709 errlinno = lineno = n->nredir.linno;
9596 10710
10711#if ENABLE_PLATFORM_MINGW32
10712 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) {
10713 expredir(n->nredir.redirect);
10714 redirect(n->nredir.redirect, 0);
10715 evaltreenr(n->nredir.n, flags);
10716 /* never returns */
10717 }
10718#else
9597 expredir(n->nredir.redirect); 10719 expredir(n->nredir.redirect);
9598 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) 10720 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
9599 goto nofork; 10721 goto nofork;
10722#endif
9600 INT_OFF; 10723 INT_OFF;
9601 if (backgnd == FORK_FG) 10724 if (backgnd == FORK_FG)
9602 get_tty_state(); 10725 get_tty_state();
9603 jp = makejob(/*n,*/ 1); 10726 jp = makejob(/*n,*/ 1);
10727#if ENABLE_PLATFORM_MINGW32
10728 memset(&fs, 0, sizeof(fs));
10729 fs.fpid = FS_EVALSUBSHELL;
10730 fs.n = n;
10731 fs.flags = flags;
10732 spawn_forkshell(&fs, jp, n, backgnd);
10733#else
9604 if (forkshell(jp, n, backgnd) == 0) { 10734 if (forkshell(jp, n, backgnd) == 0) {
9605 /* child */ 10735 /* child */
9606 INT_ON; 10736 INT_ON;
@@ -9612,6 +10742,7 @@ evalsubshell(union node *n, int flags)
9612 evaltreenr(n->nredir.n, flags); 10742 evaltreenr(n->nredir.n, flags);
9613 /* never returns */ 10743 /* never returns */
9614 } 10744 }
10745#endif
9615 /* parent */ 10746 /* parent */
9616 status = 0; 10747 status = 0;
9617 if (backgnd == FORK_FG) 10748 if (backgnd == FORK_FG)
@@ -9692,6 +10823,7 @@ expredir(union node *n)
9692static int 10823static int
9693evalpipe(union node *n, int flags) 10824evalpipe(union node *n, int flags)
9694{ 10825{
10826 IF_PLATFORM_MINGW32(struct forkshell fs;)
9695 struct job *jp; 10827 struct job *jp;
9696 struct nodelist *lp; 10828 struct nodelist *lp;
9697 int pipelen; 10829 int pipelen;
@@ -9718,6 +10850,16 @@ evalpipe(union node *n, int flags)
9718 ash_msg_and_raise_perror("can't create pipe"); 10850 ash_msg_and_raise_perror("can't create pipe");
9719 } 10851 }
9720 } 10852 }
10853#if ENABLE_PLATFORM_MINGW32
10854 memset(&fs, 0, sizeof(fs));
10855 fs.fpid = FS_EVALPIPE;
10856 fs.flags = flags;
10857 fs.n = lp->n;
10858 fs.fd[0] = pip[0];
10859 fs.fd[1] = pip[1];
10860 fs.fd[2] = prevfd;
10861 spawn_forkshell(&fs, jp, lp->n, n->npipe.pipe_backgnd);
10862#else
9721 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 10863 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
9722 /* child */ 10864 /* child */
9723 INT_ON; 10865 INT_ON;
@@ -9735,6 +10877,7 @@ evalpipe(union node *n, int flags)
9735 evaltreenr(lp->n, flags); 10877 evaltreenr(lp->n, flags);
9736 /* never returns */ 10878 /* never returns */
9737 } 10879 }
10880#endif
9738 /* parent */ 10881 /* parent */
9739 if (prevfd >= 0) 10882 if (prevfd >= 0)
9740 close(prevfd); 10883 close(prevfd);
@@ -9792,6 +10935,9 @@ setinteractive(int on)
9792 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP); 10935 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
9793# if ENABLE_FEATURE_TAB_COMPLETION 10936# if ENABLE_FEATURE_TAB_COMPLETION
9794 line_input_state->get_exe_name = ash_command_name; 10937 line_input_state->get_exe_name = ash_command_name;
10938# if ENABLE_ASH_GLOB_OPTIONS
10939 line_input_state->sh_accept_glob = ash_accept_glob;
10940# endif
9795# endif 10941# endif
9796# if EDITING_HAS_sh_get_var 10942# if EDITING_HAS_sh_get_var
9797 line_input_state->sh_get_var = lookupvar; 10943 line_input_state->sh_get_var = lookupvar;
@@ -9819,6 +10965,12 @@ optschanged(void)
9819#else 10965#else
9820 viflag = 0; /* forcibly keep the option off */ 10966 viflag = 0; /* forcibly keep the option off */
9821#endif 10967#endif
10968#if ENABLE_ASH_NOCONSOLE
10969 hide_console(noconsole);
10970#endif
10971#if ENABLE_PLATFORM_MINGW32
10972 setwinxp(winxp);
10973#endif
9822} 10974}
9823 10975
9824struct localvar_list { 10976struct localvar_list {
@@ -9838,6 +10990,9 @@ poplocalvars(int keep)
9838 struct localvar_list *ll; 10990 struct localvar_list *ll;
9839 struct localvar *lvp, *next; 10991 struct localvar *lvp, *next;
9840 struct var *vp; 10992 struct var *vp;
10993#if ENABLE_PLATFORM_MINGW32
10994 int var_type;
10995#endif
9841 10996
9842 INT_OFF; 10997 INT_OFF;
9843 ll = localvar_stack; 10998 ll = localvar_stack;
@@ -9880,6 +11035,17 @@ poplocalvars(int keep)
9880 free((char*)vp->var_text); 11035 free((char*)vp->var_text);
9881 vp->flags = lvp->flags; 11036 vp->flags = lvp->flags;
9882 vp->var_text = lvp->text; 11037 vp->var_text = lvp->text;
11038#if ENABLE_PLATFORM_MINGW32
11039 var_type = is_bb_var(lvp->text);
11040 if (var_type == BB_VAR_ASSIGN && (lvp->flags & VEXPORT))
11041 putenv(lvp->text);
11042 else if (var_type) {
11043 char *var = xstrdup(lvp->text);
11044 *strchrnul(var, '=') = '\0';
11045 unsetenv(var);
11046 free(var);
11047 }
11048#endif
9883 } 11049 }
9884 free(lvp); 11050 free(lvp);
9885 } 11051 }
@@ -10102,7 +11268,7 @@ execcmd(int argc UNUSED_PARAM, char **argv)
10102 prog = argv[0]; 11268 prog = argv[0];
10103 if (optionarg) 11269 if (optionarg)
10104 argv[0] = optionarg; 11270 argv[0] = optionarg;
10105 shellexec(prog, argv, pathval(), 0); 11271 shellexec(prog, argv, pathval(), 0, FALSE);
10106 /* NOTREACHED */ 11272 /* NOTREACHED */
10107 } 11273 }
10108 return 0; 11274 return 0;
@@ -10155,6 +11321,9 @@ static int readcmd(int, char **) FAST_FUNC;
10155static int setcmd(int, char **) FAST_FUNC; 11321static int setcmd(int, char **) FAST_FUNC;
10156static int shiftcmd(int, char **) FAST_FUNC; 11322static int shiftcmd(int, char **) FAST_FUNC;
10157static int timescmd(int, char **) FAST_FUNC; 11323static int timescmd(int, char **) FAST_FUNC;
11324#if ENABLE_PLATFORM_MINGW32
11325static int titlecmd(int, char **) FAST_FUNC;
11326#endif
10158static int trapcmd(int, char **) FAST_FUNC; 11327static int trapcmd(int, char **) FAST_FUNC;
10159static int umaskcmd(int, char **) FAST_FUNC; 11328static int umaskcmd(int, char **) FAST_FUNC;
10160static int unsetcmd(int, char **) FAST_FUNC; 11329static int unsetcmd(int, char **) FAST_FUNC;
@@ -10227,7 +11396,7 @@ static const struct builtincmd builtintab[] = {
10227#if MAX_HISTORY 11396#if MAX_HISTORY
10228 { BUILTIN_NOSPEC "history" , historycmd }, 11397 { BUILTIN_NOSPEC "history" , historycmd },
10229#endif 11398#endif
10230#if JOBS 11399#if JOBS || JOBS_WIN32
10231 { BUILTIN_REGULAR "jobs" , jobscmd }, 11400 { BUILTIN_REGULAR "jobs" , jobscmd },
10232 { BUILTIN_REGULAR "kill" , killcmd }, 11401 { BUILTIN_REGULAR "kill" , killcmd },
10233#endif 11402#endif
@@ -10254,6 +11423,9 @@ static const struct builtincmd builtintab[] = {
10254 { BUILTIN_REGULAR "test" , testcmd }, 11423 { BUILTIN_REGULAR "test" , testcmd },
10255#endif 11424#endif
10256 { BUILTIN_SPEC_REG "times" , timescmd }, 11425 { BUILTIN_SPEC_REG "times" , timescmd },
11426#if ENABLE_PLATFORM_MINGW32
11427 { BUILTIN_REGULAR "title" , titlecmd },
11428#endif
10257 { BUILTIN_SPEC_REG "trap" , trapcmd }, 11429 { BUILTIN_SPEC_REG "trap" , trapcmd },
10258 { BUILTIN_REGULAR "true" , truecmd }, 11430 { BUILTIN_REGULAR "true" , truecmd },
10259 { BUILTIN_REGULAR "type" , typecmd }, 11431 { BUILTIN_REGULAR "type" , typecmd },
@@ -10272,7 +11444,7 @@ static const struct builtincmd builtintab[] = {
10272 /* [ */ 1 * ENABLE_ASH_TEST + \ 11444 /* [ */ 1 * ENABLE_ASH_TEST + \
10273 /* [[ */ 1 * BASH_TEST2 + \ 11445 /* [[ */ 1 * BASH_TEST2 + \
10274 /* alias */ 1 * ENABLE_ASH_ALIAS + \ 11446 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10275 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ 11447 /* bg */ 1 * JOBS + \
10276 /* break cd cddir */ 3) 11448 /* break cd cddir */ 3)
10277#define EVALCMD (COMMANDCMD + \ 11449#define EVALCMD (COMMANDCMD + \
10278 /* command */ 1 * ENABLE_ASH_CMDCMD + \ 11450 /* command */ 1 * ENABLE_ASH_CMDCMD + \
@@ -10353,6 +11525,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10353 * as POSIX mandates */ 11525 * as POSIX mandates */
10354 return back_exitstatus; 11526 return back_exitstatus;
10355} 11527}
11528
10356static int 11529static int
10357evalcommand(union node *cmd, int flags) 11530evalcommand(union node *cmd, int flags)
10358{ 11531{
@@ -10452,9 +11625,19 @@ evalcommand(union node *cmd, int flags)
10452 11625
10453 localvar_stop = pushlocalvars(vlocal); 11626 localvar_stop = pushlocalvars(vlocal);
10454 11627
11628#if ENABLE_PLATFORM_MINGW32
11629# if ENABLE_FEATURE_SH_STANDALONE
11630 /* Reserve two extra spots at the front for shellexec. */
11631 nargv = stalloc(sizeof(char *) * (argc + 3));
11632 argv = nargv = nargv + 2;
11633# else
11634 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
11635# endif
11636#else
10455 /* Reserve one extra spot at the front for shellexec. */ 11637 /* Reserve one extra spot at the front for shellexec. */
10456 nargv = stalloc(sizeof(char *) * (argc + 2)); 11638 nargv = stalloc(sizeof(char *) * (argc + 2));
10457 argv = ++nargv; 11639 argv = ++nargv;
11640#endif
10458 for (sp = arglist.list; sp; sp = sp->next) { 11641 for (sp = arglist.list; sp; sp = sp->next) {
10459 TRACE(("evalcommand arg: %s\n", sp->text)); 11642 TRACE(("evalcommand arg: %s\n", sp->text));
10460 *nargv++ = sp->text; 11643 *nargv++ = sp->text;
@@ -10563,9 +11746,11 @@ evalcommand(union node *cmd, int flags)
10563 11746
10564 default: { 11747 default: {
10565 11748
11749//TODO: find a better solution for Windows on ARM than ignoring NOFORK
10566#if ENABLE_FEATURE_SH_STANDALONE \ 11750#if ENABLE_FEATURE_SH_STANDALONE \
10567 && ENABLE_FEATURE_SH_NOFORK \ 11751 && ENABLE_FEATURE_SH_NOFORK \
10568 && NUM_APPLETS > 1 11752 && NUM_APPLETS > 1 \
11753 && !(defined(_ARM64_) && !defined(_UCRT) && ENABLE_PLATFORM_MINGW32)
10569/* (1) BUG: if variables are set, we need to fork, or save/restore them 11754/* (1) BUG: if variables are set, we need to fork, or save/restore them
10570 * around run_nofork_applet() call. 11755 * around run_nofork_applet() call.
10571 * (2) Should this check also be done in forkshell()? 11756 * (2) Should this check also be done in forkshell()?
@@ -10575,6 +11760,9 @@ evalcommand(union node *cmd, int flags)
10575 int applet_no = (- cmdentry.u.index - 2); 11760 int applet_no = (- cmdentry.u.index - 2);
10576 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { 11761 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
10577 char **sv_environ; 11762 char **sv_environ;
11763#if ENABLE_PLATFORM_MINGW32
11764 char *sv_argv0;
11765#endif
10578 11766
10579 INT_OFF; 11767 INT_OFF;
10580 sv_environ = environ; 11768 sv_environ = environ;
@@ -10587,8 +11775,16 @@ evalcommand(union node *cmd, int flags)
10587 * and/or wait for user input ineligible for NOFORK: 11775 * and/or wait for user input ineligible for NOFORK:
10588 * for example, "yes" or "rm" (rm -i waits for input). 11776 * for example, "yes" or "rm" (rm -i waits for input).
10589 */ 11777 */
11778#if ENABLE_PLATFORM_MINGW32
11779 sv_argv0 = __argv[0];
11780 argv[0] = (char *)bb_basename(argv[0]);
11781 __argv[0] = argv[0];
11782#endif
10590 exitstatus = run_nofork_applet(applet_no, argv); 11783 exitstatus = run_nofork_applet(applet_no, argv);
10591 environ = sv_environ; 11784 environ = sv_environ;
11785#if ENABLE_PLATFORM_MINGW32
11786 __argv[0] = sv_argv0;
11787#endif
10592 /* 11788 /*
10593 * Try enabling NOFORK for "yes" applet. 11789 * Try enabling NOFORK for "yes" applet.
10594 * ^C _will_ stop it (write returns EINTR), 11790 * ^C _will_ stop it (write returns EINTR),
@@ -10606,6 +11802,22 @@ evalcommand(union node *cmd, int flags)
10606 * in a script or a subshell does not need forking, 11802 * in a script or a subshell does not need forking,
10607 * we can just exec it. 11803 * we can just exec it.
10608 */ 11804 */
11805#if ENABLE_PLATFORM_MINGW32
11806 if (!(flags & EV_EXIT) || may_have_traps IF_SUW32(|| delayexit)) {
11807 /* No, forking off a child is necessary */
11808 struct forkshell fs;
11809
11810 INT_OFF;
11811 memset(&fs, 0, sizeof(fs));
11812 fs.fpid = FS_SHELLEXEC;
11813 fs.argv = argv;
11814 fs.path = (char*)path;
11815 fs.fd[0] = cmdentry.u.index;
11816 jp = makejob(/*cmd,*/ 1);
11817 spawn_forkshell(&fs, jp, cmd, FORK_FG);
11818 break;
11819 }
11820#else
10609 if (!(flags & EV_EXIT) || may_have_traps) { 11821 if (!(flags & EV_EXIT) || may_have_traps) {
10610 /* No, forking off a child is necessary */ 11822 /* No, forking off a child is necessary */
10611 INT_OFF; 11823 INT_OFF;
@@ -10619,7 +11831,8 @@ evalcommand(union node *cmd, int flags)
10619 FORCE_INT_ON; 11831 FORCE_INT_ON;
10620 /* fall through to exec'ing external program */ 11832 /* fall through to exec'ing external program */
10621 } 11833 }
10622 shellexec(argv[0], argv, path, cmdentry.u.index); 11834#endif
11835 shellexec(argv[0], argv, path, cmdentry.u.index, FALSE);
10623 /* NOTREACHED */ 11836 /* NOTREACHED */
10624 } /* default */ 11837 } /* default */
10625 case CMDBUILTIN: 11838 case CMDBUILTIN:
@@ -10698,7 +11911,6 @@ goodname(const char *p)
10698 return endofname(p)[0] == '\0'; 11911 return endofname(p)[0] == '\0';
10699} 11912}
10700 11913
10701
10702/* 11914/*
10703 * Search for a command. This is called before we fork so that the 11915 * Search for a command. This is called before we fork so that the
10704 * location of the command will be available in the parent as well as 11916 * location of the command will be available in the parent as well as
@@ -10831,6 +12043,54 @@ static void popstring(void)
10831 INT_ON; 12043 INT_ON;
10832} 12044}
10833 12045
12046#if ENABLE_PLATFORM_MINGW32
12047/*
12048 * Wrapper around nonblock_immune_read() to remove CRs, but only from
12049 * CRLF pairs. The tricky part is handling a CR at the end of the buffer.
12050 */
12051static inline ssize_t
12052nonblock_immune_wrapper(struct parsefile *pf, char *buffer, size_t count)
12053{
12054 int nr, injected_cr;
12055
12056 // Inject unprocessed CR from previous read into the buffer.
12057 if (pf->cr)
12058 *buffer = '\r';
12059 retry:
12060 nr = nonblock_immune_read(pf->pf_fd, buffer + pf->cr, count - pf->cr);
12061 if (nr < 0)
12062 return nr;
12063
12064 injected_cr = pf->cr;
12065 nr += pf->cr;
12066 pf->cr = 0;
12067
12068 if (nr > 0) {
12069 nr = remove_cr(buffer, nr);
12070 // remove_cr() won't reduce size to zero, so [nr - 1] is OK.
12071 if (buffer[nr - 1] == '\r') {
12072 if (nr > 1) {
12073 // Ignore trailing CR for now: we'll deal with it later.
12074 pf->cr = 1;
12075 --nr;
12076 } else if (injected_cr) { // nr == 1
12077 // Buffer only contains an injected CR. This means the
12078 // read returned EOF. Return the buffer as-is. The
12079 // next call will detect EOF.
12080 } else {
12081 // Buffer only contains a CR from the most recent read.
12082 // Try another read, treating the CR as injected. We'll
12083 // either get more characters or EOF. Either way we
12084 // won't end up here again.
12085 pf->cr = 1;
12086 goto retry;
12087 }
12088 }
12089 }
12090 return nr;
12091}
12092#endif
12093
10834static int 12094static int
10835preadfd(void) 12095preadfd(void)
10836{ 12096{
@@ -10841,7 +12101,11 @@ preadfd(void)
10841#if ENABLE_FEATURE_EDITING 12101#if ENABLE_FEATURE_EDITING
10842 /* retry: */ 12102 /* retry: */
10843 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 12103 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
12104#if ENABLE_PLATFORM_MINGW32
12105 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
12106#else
10844 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 12107 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
12108#endif
10845 else { 12109 else {
10846# if ENABLE_ASH_IDLE_TIMEOUT 12110# if ENABLE_ASH_IDLE_TIMEOUT
10847 int timeout = -1; 12111 int timeout = -1;
@@ -10880,12 +12144,21 @@ preadfd(void)
10880 INT_ON; /* here non-blocked SIGINT will longjmp */ 12144 INT_ON; /* here non-blocked SIGINT will longjmp */
10881 if (nr == 0) { 12145 if (nr == 0) {
10882 /* ^C pressed, "convert" to SIGINT */ 12146 /* ^C pressed, "convert" to SIGINT */
12147# if !ENABLE_PLATFORM_MINGW32
10883 write(STDOUT_FILENO, "^C\n", 3); 12148 write(STDOUT_FILENO, "^C\n", 3);
10884 raise(SIGINT); /* here non-blocked SIGINT will longjmp */ 12149 raise(SIGINT); /* here non-blocked SIGINT will longjmp */
10885 /* raise(SIGINT) did not work! (e.g. if SIGINT 12150 /* raise(SIGINT) did not work! (e.g. if SIGINT
10886 * is SIG_IGNed on startup, it stays SIG_IGNed) 12151 * is SIG_IGNed on startup, it stays SIG_IGNed)
10887 */ 12152 */
12153# else
12154 raise_interrupt();
12155 write(STDOUT_FILENO, "^C\n", 3);
12156# endif
10888 if (trap[SIGINT]) { 12157 if (trap[SIGINT]) {
12158# if ENABLE_PLATFORM_MINGW32
12159 pending_int = 1;
12160 dotrap();
12161# endif
10889 empty_line_input: 12162 empty_line_input:
10890 buf[0] = '\n'; 12163 buf[0] = '\n';
10891 buf[1] = '\0'; 12164 buf[1] = '\0';
@@ -10914,7 +12187,11 @@ preadfd(void)
10914 } 12187 }
10915 } 12188 }
10916#else 12189#else
12190# if ENABLE_PLATFORM_MINGW32
12191 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
12192# else
10917 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 12193 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
12194# endif
10918#endif 12195#endif
10919 12196
10920#if 0 /* disabled: nonblock_immune_read() handles this problem */ 12197#if 0 /* disabled: nonblock_immune_read() handles this problem */
@@ -11213,6 +12490,7 @@ popallfiles(void)
11213 unwindfiles(&basepf); 12490 unwindfiles(&basepf);
11214} 12491}
11215 12492
12493#if !ENABLE_PLATFORM_MINGW32
11216/* 12494/*
11217 * Close the file(s) that the shell is reading commands from. Called 12495 * Close the file(s) that the shell is reading commands from. Called
11218 * after a fork is done. 12496 * after a fork is done.
@@ -11226,6 +12504,7 @@ closescript(void)
11226 g_parsefile->pf_fd = 0; 12504 g_parsefile->pf_fd = 0;
11227 } 12505 }
11228} 12506}
12507#endif
11229 12508
11230/* 12509/*
11231 * Like setinputfile, but takes an open file descriptor. Call this with 12510 * Like setinputfile, but takes an open file descriptor. Call this with
@@ -11462,8 +12741,19 @@ options(int *login_sh)
11462 int val; 12741 int val;
11463 int c; 12742 int c;
11464 12743
11465 if (login_sh != NULL) /* if we came from startup code */ 12744#if ENABLE_ASH_NOCONSOLE
12745 noconsole = console_state();
12746#endif
12747 if (login_sh != NULL) { /* if we came from startup code */
11466 minusc = NULL; 12748 minusc = NULL;
12749#if ENABLE_PLATFORM_MINGW32
12750 dirarg = NULL;
12751 title = NULL;
12752# if ENABLE_SUW32
12753 delayexit = 0;
12754# endif
12755#endif
12756 }
11467 while ((p = *argptr) != NULL) { 12757 while ((p = *argptr) != NULL) {
11468 c = *p++; 12758 c = *p++;
11469 if (c != '-' && c != '+') 12759 if (c != '-' && c != '+')
@@ -11493,6 +12783,31 @@ options(int *login_sh)
11493 cflag = 1; 12783 cflag = 1;
11494 continue; 12784 continue;
11495 } 12785 }
12786#if ENABLE_PLATFORM_MINGW32
12787 /* Undocumented flags;
12788 * -d force current directory
12789 * -t title to display in console window
12790 * -N prompt user before exit
12791 * Must appear before -s or -c. */
12792 if (c == 'd' && val == 1) {
12793 if (*argptr == NULL)
12794 ash_msg_and_raise_error(bb_msg_requires_arg, "-d");
12795 dirarg = *argptr++;
12796 continue;
12797 }
12798 if (c == 't' && val == 1) {
12799 if (*argptr == NULL)
12800 ash_msg_and_raise_error(bb_msg_requires_arg, "-t");
12801 title = *argptr++;
12802 continue;
12803 }
12804# if ENABLE_SUW32
12805 if (c == 'N' && val == 1) {
12806 delayexit = 1;
12807 continue;
12808 }
12809# endif
12810#endif
11496 if (c == 's') { /* -s, +s */ 12811 if (c == 's') { /* -s, +s */
11497 sflag = 1; 12812 sflag = 1;
11498 continue; 12813 continue;
@@ -13448,7 +14763,6 @@ parseheredoc(void)
13448 } 14763 }
13449} 14764}
13450 14765
13451
13452static const char * 14766static const char *
13453expandstr(const char *ps, int syntax_type) 14767expandstr(const char *ps, int syntax_type)
13454{ 14768{
@@ -13528,6 +14842,9 @@ evalstring(char *s, int flags)
13528 int status; 14842 int status;
13529 14843
13530 s = sstrdup(s); 14844 s = sstrdup(s);
14845#if ENABLE_PLATFORM_MINGW32
14846 remove_cr(s, strlen(s)+1);
14847#endif
13531 setinputstring(s); 14848 setinputstring(s);
13532 setstackmark(&smark); 14849 setstackmark(&smark);
13533 14850
@@ -13615,7 +14932,7 @@ cmdloop(int top)
13615 int skip; 14932 int skip;
13616 14933
13617 setstackmark(&smark); 14934 setstackmark(&smark);
13618#if JOBS 14935#if JOBS || JOBS_WIN32
13619 if (doing_jobctl) 14936 if (doing_jobctl)
13620 showjobs(SHOW_CHANGED|SHOW_STDERR); 14937 showjobs(SHOW_CHANGED|SHOW_STDERR);
13621#endif 14938#endif
@@ -13623,6 +14940,9 @@ cmdloop(int top)
13623 if (iflag && top) { 14940 if (iflag && top) {
13624 inter++; 14941 inter++;
13625 chkmail(); 14942 chkmail();
14943#if ENABLE_PLATFORM_MINGW32
14944 terminal_mode(TRUE);
14945#endif
13626 } 14946 }
13627 n = parsecmd(inter); 14947 n = parsecmd(inter);
13628#if DEBUG 14948#if DEBUG
@@ -13646,8 +14966,10 @@ cmdloop(int top)
13646 } else { 14966 } else {
13647 int i; 14967 int i;
13648 14968
14969#if !ENABLE_PLATFORM_MINGW32
13649 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ 14970 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13650 job_warning >>= 1; 14971 job_warning >>= 1;
14972#endif
13651 numeof = 0; 14973 numeof = 0;
13652 i = evaltree(n, 0); 14974 i = evaltree(n, 0);
13653 if (n) 14975 if (n)
@@ -13677,7 +14999,7 @@ find_dot_file(char *basename)
13677 int len; 14999 int len;
13678 15000
13679 /* don't try this for absolute or relative paths */ 15001 /* don't try this for absolute or relative paths */
13680 if (strchr(basename, '/')) 15002 if (strchr(basename, '/') IF_PLATFORM_MINGW32(|| strchr(basename, '\\')))
13681 return basename; 15003 return basename;
13682 15004
13683 path = pathval(); 15005 path = pathval();
@@ -13842,6 +15164,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13842 struct builtincmd *bcmd; 15164 struct builtincmd *bcmd;
13843 int len; 15165 int len;
13844 15166
15167#if !ENABLE_PLATFORM_MINGW32
13845 /* If name contains a slash, don't use PATH or hash table */ 15168 /* If name contains a slash, don't use PATH or hash table */
13846 if (strchr(name, '/') != NULL) { 15169 if (strchr(name, '/') != NULL) {
13847 entry->u.index = -1; 15170 entry->u.index = -1;
@@ -13861,6 +15184,35 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13861 entry->cmdtype = CMDNORMAL; 15184 entry->cmdtype = CMDNORMAL;
13862 return; 15185 return;
13863 } 15186 }
15187#else /* ENABLE_PLATFORM_MINGW32 */
15188 /* If name contains a slash or drive prefix, don't use PATH or hash table */
15189 if (has_path(name)) {
15190 entry->u.index = -1;
15191 entry->cmdtype = CMDNORMAL;
15192 fullname = stack_add_ext_space(name);
15193 if (add_win32_extension(fullname)) {
15194 return;
15195 } else if (unix_path(name)) {
15196 name = (char *)bb_basename(name);
15197 if (
15198# if ENABLE_FEATURE_SH_STANDALONE
15199 find_applet_by_name_for_sh(name, path) >= 0 ||
15200# endif
15201 !find_builtin(bb_basename(name))
15202 ) {
15203 act |= DO_NOFUNC;
15204 } else if (act & DO_ABS) {
15205 entry->cmdtype = CMDUNKNOWN;
15206 return;
15207 }
15208 } else if (act & DO_ABS) {
15209 entry->cmdtype = CMDUNKNOWN;
15210 return;
15211 } else {
15212 return;
15213 }
15214 }
15215#endif /* ENABLE_PLATFORM_MINGW32 */
13864 15216
13865/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ 15217/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
13866 15218
@@ -13915,7 +15267,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13915 15267
13916#if ENABLE_FEATURE_SH_STANDALONE 15268#if ENABLE_FEATURE_SH_STANDALONE
13917 { 15269 {
13918 int applet_no = find_applet_by_name(name); 15270 int applet_no = find_applet_by_name_for_sh(name, path);
13919 if (applet_no >= 0) { 15271 if (applet_no >= 0) {
13920 entry->cmdtype = CMDNORMAL; 15272 entry->cmdtype = CMDNORMAL;
13921 entry->u.index = -2 - applet_no; 15273 entry->u.index = -2 - applet_no;
@@ -13954,12 +15306,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13954 } 15306 }
13955 } 15307 }
13956 /* if rehash, don't redo absolute path names */ 15308 /* if rehash, don't redo absolute path names */
13957 if (fullname[0] == '/' && idx <= prev) { 15309 if (!is_relative_path(fullname) && idx <= prev) {
13958 if (idx < prev) 15310 if (idx < prev)
13959 continue; 15311 continue;
13960 TRACE(("searchexec \"%s\": no change\n", name)); 15312 TRACE(("searchexec \"%s\": no change\n", name));
13961 goto success; 15313 goto success;
13962 } 15314 }
15315#if ENABLE_PLATFORM_MINGW32
15316 add_win32_extension(fullname);
15317#endif
13963 while (stat(fullname, &statb) < 0) { 15318 while (stat(fullname, &statb) < 0) {
13964#ifdef SYSV 15319#ifdef SYSV
13965 if (errno == EINTR) 15320 if (errno == EINTR)
@@ -14037,7 +15392,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
14037 entry->u = cmdp->param; 15392 entry->u = cmdp->param;
14038} 15393}
14039 15394
14040
14041/* 15395/*
14042 * The trap builtin. 15396 * The trap builtin.
14043 */ 15397 */
@@ -14098,19 +15452,45 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14098 if (LONE_DASH(action)) 15452 if (LONE_DASH(action))
14099 action = NULL; 15453 action = NULL;
14100 else { 15454 else {
15455#if !ENABLE_PLATFORM_MINGW32
14101 if (action[0]) /* not NULL and not "" and not "-" */ 15456 if (action[0]) /* not NULL and not "" and not "-" */
14102 may_have_traps = 1; 15457 may_have_traps = 1;
15458#endif
14103 action = ckstrdup(action); 15459 action = ckstrdup(action);
14104 } 15460 }
14105 } 15461 }
14106 free(trap[signo]); 15462 free(trap[signo]);
14107 trap[signo] = action; 15463 trap[signo] = action;
15464#if ENABLE_PLATFORM_MINGW32
15465 if (signo == SIGINT) {
15466 // trap '' INT disables Ctrl-C, anything else enables it
15467 if (action && action[0] == '\0') {
15468 SetConsoleCtrlHandler(NULL, TRUE);
15469# if ENABLE_FEATURE_EDITING
15470 if (line_input_state) {
15471 line_input_state->flags |= IGNORE_CTRL_C;
15472 }
15473# endif
15474 } else {
15475 SetConsoleCtrlHandler(NULL, FALSE);
15476# if ENABLE_FEATURE_EDITING
15477 if (line_input_state) {
15478 line_input_state->flags &= ~IGNORE_CTRL_C;
15479 }
15480# endif
15481 }
15482 }
15483#else
14108 if (signo != 0 && signo < NSIG) 15484 if (signo != 0 && signo < NSIG)
14109 setsignal(signo); 15485 setsignal(signo);
15486#endif
14110 INT_ON; 15487 INT_ON;
14111 next: 15488 next:
14112 ap++; 15489 ap++;
14113 } 15490 }
15491#if ENABLE_PLATFORM_MINGW32
15492 may_have_traps = trap[SIGINT] && trap[SIGINT][0] != '\0';
15493#endif
14114 return exitcode; 15494 return exitcode;
14115} 15495}
14116 15496
@@ -14139,10 +15519,12 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14139 { 15519 {
14140 const char *a = applet_names; 15520 const char *a = applet_names;
14141 while (*a) { 15521 while (*a) {
14142 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a); 15522 if (prefer_applet(a, pathval())) {
14143 if (col > 60) { 15523 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
14144 out1fmt("\n"); 15524 if (col > 60) {
14145 col = 0; 15525 out1fmt("\n");
15526 col = 0;
15527 }
14146 } 15528 }
14147 while (*a++ != '\0') 15529 while (*a++ != '\0')
14148 continue; 15530 continue;
@@ -14204,7 +15586,24 @@ exportcmd(int argc UNUSED_PARAM, char **argv)
14204 } else { 15586 } else {
14205 vp = *findvar(name); 15587 vp = *findvar(name);
14206 if (vp) { 15588 if (vp) {
15589#if ENABLE_PLATFORM_MINGW32
15590 if (is_bb_var(name) == BB_VAR_EXACT) {
15591 if (flag_off == ~VEXPORT)
15592 unsetenv(name);
15593 else if (flag == VEXPORT && !(vp->flags & VUNSET))
15594 putenv(vp->var_text);
15595 }
15596#endif
14207 vp->flags = ((vp->flags | flag) & flag_off); 15597 vp->flags = ((vp->flags | flag) & flag_off);
15598#if ENABLE_PLATFORM_MINGW32
15599 /* Unexporting a variable imported from the
15600 * environment restores its original value and
15601 * removes the VIMPORT flag. */
15602 if ((vp->flags & VIMPORT) && (flag_off == ~VEXPORT)) {
15603 vp->flags &= ~VIMPORT;
15604 p = getenv(name);
15605 } else
15606#endif
14208 continue; 15607 continue;
14209 } 15608 }
14210 } 15609 }
@@ -14295,6 +15694,21 @@ timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14295 return 0; 15694 return 0;
14296} 15695}
14297 15696
15697#if ENABLE_PLATFORM_MINGW32
15698static int FAST_FUNC
15699titlecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15700{
15701 if (*argptr == NULL) {
15702 char buffer[256];
15703 if (get_title(buffer, sizeof(buffer)))
15704 puts(buffer);
15705 } else {
15706 set_title(*argptr);
15707 }
15708 return 0;
15709}
15710#endif
15711
14298#if ENABLE_FEATURE_SH_MATH 15712#if ENABLE_FEATURE_SH_MATH
14299/* 15713/*
14300 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell. 15714 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
@@ -14387,6 +15801,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14387 r = shell_builtin_read(&params); 15801 r = shell_builtin_read(&params);
14388 INT_ON; 15802 INT_ON;
14389 15803
15804#if !ENABLE_PLATFORM_MINGW32
14390 if ((uintptr_t)r == 1 && errno == EINTR) { 15805 if ((uintptr_t)r == 1 && errno == EINTR) {
14391 /* To get SIGCHLD: sleep 1 & read x; echo $x 15806 /* To get SIGCHLD: sleep 1 & read x; echo $x
14392 * Correct behavior is to not exit "read" 15807 * Correct behavior is to not exit "read"
@@ -14399,6 +15814,29 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14399 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */ 15814 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
14400 /* The actual value observed with bash 5.2.15: */ 15815 /* The actual value observed with bash 5.2.15: */
14401 return 128 + SIGALRM; 15816 return 128 + SIGALRM;
15817#else /* ENABLE_PLATFORM_MINGW32 */
15818 if ((uintptr_t)r == 2) {
15819 /* Timeout, return 128 + SIGALRM */
15820 return 142;
15821 } else if ((uintptr_t)r == 3) {
15822 /* ^C pressed, propagate event */
15823 if (trap[SIGINT]) {
15824 write(STDOUT_FILENO, "^C", 2);
15825 pending_int = 1;
15826 dotrap();
15827 if (!(rootshell && iflag))
15828 return (uintptr_t)0;
15829 else
15830 goto again;
15831 } else if (iflag) {
15832 raise_interrupt();
15833 } else {
15834 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
15835 exitshell();
15836 }
15837 return (uintptr_t)r;
15838 }
15839#endif
14402 15840
14403 if ((uintptr_t)r > 1) 15841 if ((uintptr_t)r > 1)
14404 ash_msg_and_raise_error(r); 15842 ash_msg_and_raise_error(r);
@@ -14460,6 +15898,9 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14460 if (!isdigit(modestr[0])) 15898 if (!isdigit(modestr[0]))
14461 mask ^= 0777; 15899 mask ^= 0777;
14462 umask(mask); 15900 umask(mask);
15901#if ENABLE_PLATFORM_MINGW32
15902 setvareq(xasprintf("BB_UMASK=0%o", mask), VEXPORT|VNOSAVE);
15903#endif
14463 } 15904 }
14464 return 0; 15905 return 0;
14465} 15906}
@@ -14560,6 +16001,13 @@ exitshell(void)
14560 /*free(p); - we'll exit soon */ 16001 /*free(p); - we'll exit soon */
14561 } 16002 }
14562 out: 16003 out:
16004#if ENABLE_SUW32
16005 if (delayexit) {
16006#define EXIT_MSG "Press any key to exit..."
16007 console_write(EXIT_MSG, sizeof(EXIT_MSG) - 1);
16008 _getch();
16009 }
16010#endif
14563 exitreset(); 16011 exitreset();
14564 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}". 16012 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14565 * our setjobctl(0) does not panic if tcsetpgrp fails inside it. 16013 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
@@ -14570,22 +16018,97 @@ exitshell(void)
14570 /* NOTREACHED */ 16018 /* NOTREACHED */
14571} 16019}
14572 16020
16021#if ENABLE_PLATFORM_MINGW32
16022/* We need to see if HOME is *really* unset */
16023# undef getenv
16024static void setvar_if_unset(const char *key, const char *value)
16025{
16026 if (!getenv(key) || getuid() == 0)
16027 setvar(key, value, VEXPORT);
16028}
16029#endif
16030
14573/* Don't inline: conserve stack of caller from having our locals too */ 16031/* Don't inline: conserve stack of caller from having our locals too */
14574static NOINLINE void 16032static NOINLINE void
14575init(void) 16033init(void)
14576{ 16034{
16035#if ENABLE_PLATFORM_MINGW32
16036 int import = 0;
16037#else
14577 /* we will never free this */ 16038 /* we will never free this */
14578 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ); 16039 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ);
14579 basepf.linno = 1; 16040 basepf.linno = 1;
14580 16041
14581 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 16042 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
14582 setsignal(SIGCHLD); 16043 setsignal(SIGCHLD);
16044#endif
14583 16045
14584 { 16046 {
14585 char **envp; 16047 char **envp;
14586 const char *p; 16048 const char *p;
14587 16049
14588 initvar(); 16050 initvar();
16051
16052#if ENABLE_PLATFORM_MINGW32
16053 /*
16054 * case insensitive env names from Windows world
16055 *
16056 * Some standard env names such as PATH is named Path and so on
16057 * ash itself is case sensitive, so "Path" will confuse it, as
16058 * MSVC getenv() is case insensitive.
16059 *
16060 * We may end up having both Path and PATH. Then Path will be chosen
16061 * because it appears first.
16062 */
16063 if (windows_env()) {
16064 /*
16065 * If we get here it's because the environment suggests we
16066 * haven't been invoked from an earlier instance of BusyBox.
16067 */
16068 char *start, *end;
16069 struct passwd *pw;
16070
16071 /* mintty sets HOME: unset it */
16072 const char *tty = getenv("TERM_PROGRAM");
16073 if (tty && strcmp(tty, "mintty") == 0) {
16074 unsetenv("HOME");
16075 }
16076
16077 import = VIMPORT;
16078 for (envp = environ; envp && *envp; envp++) {
16079 if (!(end=strchr(*envp, '=')))
16080 continue;
16081
16082 /* check for invalid characters in name */
16083 start = (char *)endofname(*envp);
16084 if (*start != '=') {
16085 /* Make a copy of the original variable */
16086 setvareq(xstrdup(*envp), VEXPORT|VNOSAVE);
16087
16088 /* Replace invalid characters with underscores */
16089 for (; start < end; start++) {
16090 if (!isalnum(*start)) {
16091 *start = '_';
16092 }
16093 }
16094 }
16095
16096 /* make all variable names uppercase */
16097 for (start = *envp;start < end;start++)
16098 *start = toupper(*start);
16099 }
16100
16101 /* Initialise some variables normally set at login, but
16102 * only if someone hasn't already set them or we're root. */
16103 pw = getpwuid(getuid());
16104 if (pw) {
16105 setvar_if_unset("USER", pw->pw_name);
16106 setvar_if_unset("LOGNAME", pw->pw_name);
16107 setvar_if_unset("HOME", pw->pw_dir);
16108 }
16109 setvar_if_unset("SHELL", DEFAULT_SHELL);
16110 }
16111#endif
14589 for (envp = environ; envp && *envp; envp++) { 16112 for (envp = environ; envp && *envp; envp++) {
14590/* Used to have 16113/* Used to have
14591 * p = endofname(*envp); 16114 * p = endofname(*envp);
@@ -14599,7 +16122,11 @@ init(void)
14599 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this 16122 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14600 */ 16123 */
14601 if (strchr(*envp, '=')) { 16124 if (strchr(*envp, '=')) {
16125#if !ENABLE_PLATFORM_MINGW32
14602 setvareq(*envp, VEXPORT|VTEXTFIXED); 16126 setvareq(*envp, VEXPORT|VTEXTFIXED);
16127#else
16128 setvareq(*envp, VEXPORT|import);
16129#endif
14603 } 16130 }
14604 } 16131 }
14605 16132
@@ -14631,7 +16158,6 @@ init(void)
14631 } 16158 }
14632} 16159}
14633 16160
14634
14635//usage:#define ash_trivial_usage 16161//usage:#define ash_trivial_usage
14636//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" 16162//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
14637//////// comes from ^^^^^^^^^^optletters 16163//////// comes from ^^^^^^^^^^optletters
@@ -14650,7 +16176,11 @@ procargs(char **argv)
14650 int login_sh; 16176 int login_sh;
14651 16177
14652 xargv = argv; 16178 xargv = argv;
16179#if ENABLE_PLATFORM_MINGW32
16180 login_sh = applet_name[0] == 'l';
16181#else
14653 login_sh = xargv[0] && xargv[0][0] == '-'; 16182 login_sh = xargv[0] && xargv[0][0] == '-';
16183#endif
14654#if NUM_SCRIPTS > 0 16184#if NUM_SCRIPTS > 0
14655 if (minusc) 16185 if (minusc)
14656 goto setarg0; 16186 goto setarg0;
@@ -14674,7 +16204,9 @@ procargs(char **argv)
14674 } 16204 }
14675 if (iflag == 2 /* no explicit -i given */ 16205 if (iflag == 2 /* no explicit -i given */
14676 && sflag == 1 /* -s given (or implied) */ 16206 && sflag == 1 /* -s given (or implied) */
16207#if !ENABLE_PLATFORM_MINGW32
14677 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */ 16208 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
16209#endif
14678 && isatty(0) && isatty(1) /* we are on tty */ 16210 && isatty(0) && isatty(1) /* we are on tty */
14679 ) { 16211 ) {
14680 iflag = 1; 16212 iflag = 1;
@@ -14694,6 +16226,9 @@ procargs(char **argv)
14694 goto setarg0; 16226 goto setarg0;
14695 } else if (!sflag) { 16227 } else if (!sflag) {
14696 setinputfile(*xargv, 0); 16228 setinputfile(*xargv, 0);
16229#if ENABLE_PLATFORM_MINGW32
16230 bs_to_slash(*xargv);
16231#endif
14697 setarg0: 16232 setarg0:
14698 arg0 = *xargv++; 16233 arg0 = *xargv++;
14699 commandname = arg0; 16234 commandname = arg0;
@@ -14717,8 +16252,10 @@ procargs(char **argv)
14717 * NB: must do it before setting up signals (in optschanged()) 16252 * NB: must do it before setting up signals (in optschanged())
14718 * and reading .profile etc (after we return from here): 16253 * and reading .profile etc (after we return from here):
14719 */ 16254 */
16255#if !ENABLE_PLATFORM_MINGW32
14720 if (iflag) 16256 if (iflag)
14721 signal(SIGHUP, SIG_DFL); 16257 signal(SIGHUP, SIG_DFL);
16258#endif
14722 16259
14723 optschanged(); 16260 optschanged();
14724 16261
@@ -14763,9 +16300,25 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14763 struct stackmark smark; 16300 struct stackmark smark;
14764 int login_sh; 16301 int login_sh;
14765 16302
16303#if ENABLE_PLATFORM_MINGW32
16304 INIT_G_memstack();
16305
16306 /* from init() */
16307 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ);
16308 basepf.linno = 1;
16309
16310 if (argc == 3 && !strcmp(argv[1], "--fs")) {
16311 forkshell_init(argv[2]);
16312 /* only reached in case of error */
16313 bb_error_msg_and_die("forkshell failed");
16314 }
16315#endif
16316
14766 /* Initialize global data */ 16317 /* Initialize global data */
14767 INIT_G_misc(); 16318 INIT_G_misc();
16319#if !ENABLE_PLATFORM_MINGW32
14768 INIT_G_memstack(); 16320 INIT_G_memstack();
16321#endif
14769 INIT_G_var(); 16322 INIT_G_var();
14770#if ENABLE_ASH_ALIAS 16323#if ENABLE_ASH_ALIAS
14771 INIT_G_alias(); 16324 INIT_G_alias();
@@ -14811,6 +16364,10 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14811 init(); 16364 init();
14812 setstackmark(&smark); 16365 setstackmark(&smark);
14813 16366
16367#if ENABLE_PLATFORM_MINGW32
16368 SetConsoleCtrlHandler(ctrl_handler, TRUE);
16369#endif
16370
14814#if NUM_SCRIPTS > 0 16371#if NUM_SCRIPTS > 0
14815 if (argc < 0) 16372 if (argc < 0)
14816 /* Non-NULL minusc tells procargs that an embedded script is being run */ 16373 /* Non-NULL minusc tells procargs that an embedded script is being run */
@@ -14822,11 +16379,45 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14822 trace_puts_args(argv); 16379 trace_puts_args(argv);
14823#endif 16380#endif
14824 16381
16382#if ENABLE_PLATFORM_MINGW32
16383 if (!dirarg && !login_sh && iflag) {
16384 char *cwd = getcwd(NULL, 0);
16385 if (cwd) {
16386 chdir(cwd);
16387 setpwd(NULL, 0);
16388 free(cwd);
16389 }
16390 }
16391
16392 if (title)
16393 set_title(title);
16394#endif
16395
14825 if (login_sh) { 16396 if (login_sh) {
14826 const char *hp; 16397 const char *hp;
14827 16398
16399#if ENABLE_PLATFORM_MINGW32
16400 if (!dirarg) {
16401 hp = lookupvar("HOME");
16402 if (hp == NULL || *hp == '\0')
16403 hp = xgetpwuid(getuid())->pw_dir;
16404 chdir(hp);
16405 setpwd(NULL, 0);
16406 }
16407#endif
16408
14828 state = 1; 16409 state = 1;
16410#if ENABLE_PLATFORM_MINGW32
16411 hp = concat_path_file(get_system_drive(), "/etc/profile");
16412 read_profile(hp);
16413 free((void *)hp);
16414
16415 hp = exe_relative_path("/etc/profile");
16416 read_profile(hp);
16417 free((void *)hp);
16418#else
14829 read_profile("/etc/profile"); 16419 read_profile("/etc/profile");
16420#endif
14830 state1: 16421 state1:
14831 state = 2; 16422 state = 2;
14832 hp = lookupvar("HOME"); 16423 hp = lookupvar("HOME");
@@ -14835,10 +16426,18 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14835 } 16426 }
14836 state2: 16427 state2:
14837 state = 3; 16428 state = 3;
16429#if ENABLE_PLATFORM_MINGW32
16430 if (dirarg) {
16431 chdir(dirarg);
16432 setpwd(NULL, 0);
16433 }
16434#endif
14838 if (iflag 16435 if (iflag
16436#if ENABLE_PLATFORM_POSIX
14839#ifndef linux 16437#ifndef linux
14840 && getuid() == geteuid() && getgid() == getegid() 16438 && getuid() == geteuid() && getgid() == getegid()
14841#endif 16439#endif
16440#endif
14842 ) { 16441 ) {
14843 const char *shinit = lookupvar("ENV"); 16442 const char *shinit = lookupvar("ENV");
14844 if (shinit != NULL && *shinit != '\0') 16443 if (shinit != NULL && *shinit != '\0')
@@ -14863,7 +16462,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14863 // ash -sc 'echo $-' 16462 // ash -sc 'echo $-'
14864 // continue reading input from stdin after running 'echo'. 16463 // continue reading input from stdin after running 'echo'.
14865 // bash does not do this: it prints "hBcs" and exits. 16464 // bash does not do this: it prints "hBcs" and exits.
16465#if !ENABLE_PLATFORM_MINGW32
14866 evalstring(minusc, EV_EXIT); 16466 evalstring(minusc, EV_EXIT);
16467#else
16468 evalstring(minusc, sflag ? 0 : EV_EXIT);
16469#endif
14867 } 16470 }
14868 16471
14869 if (sflag || minusc == NULL) { 16472 if (sflag || minusc == NULL) {
@@ -14911,6 +16514,1101 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14911 /* NOTREACHED */ 16514 /* NOTREACHED */
14912} 16515}
14913 16516
16517#if ENABLE_PLATFORM_MINGW32
16518static void
16519forkshell_openhere(struct forkshell *fs)
16520{
16521 const char *p = fs->path;
16522 size_t len = strlen(p);
16523 int pip[2];
16524
16525 pip[0] = fs->fd[0];
16526 pip[1] = fs->fd[1];
16527
16528 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16529
16530 close(pip[0]);
16531 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
16532 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
16533 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
16534 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
16535 signal(SIGPIPE, SIG_DFL);
16536 xwrite(pip[1], p, len);
16537 _exit_SUCCESS();
16538}
16539
16540static void
16541forkshell_evalbackcmd(struct forkshell *fs)
16542{
16543#if BASH_PROCESS_SUBST
16544 /* determine end of pipe used by parent (ip) and child (ic) */
16545 const int ctl = fs->fd[2];
16546 const int ip = (ctl == CTLTOPROC);
16547 const int ic = !(ctl == CTLTOPROC);
16548#else
16549 const int ip = 0;
16550 const int ic = 1;
16551#endif
16552 union node *n = fs->n;
16553 int pip[2];
16554
16555 pip[ip] = fs->fd[ip];
16556 pip[ic] = fs->fd[ic];
16557
16558 FORCE_INT_ON;
16559 close(pip[ip]);
16560 if (pip[ic] != ic) {
16561 /*close(ic);*/
16562 dup2_or_raise(pip[ic], ic);
16563 close(pip[ic]);
16564 }
16565 eflag = 0;
16566 ifsfree();
16567 evaltreenr(n, EV_EXIT);
16568 /* NOTREACHED */
16569}
16570
16571static void
16572forkshell_evalsubshell(struct forkshell *fs)
16573{
16574 union node *n = fs->n;
16575 int flags = fs->flags;
16576
16577 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16578 INT_ON;
16579 flags |= EV_EXIT;
16580 if (fs->mode)
16581 flags &= ~EV_TESTED;
16582 expredir(n->nredir.redirect);
16583 redirect(n->nredir.redirect, 0);
16584 evaltreenr(n->nredir.n, flags);
16585 /* never returns */
16586}
16587
16588static void
16589forkshell_evalpipe(struct forkshell *fs)
16590{
16591 union node *n = fs->n;
16592 int flags = fs->flags;
16593 int prevfd = fs->fd[2];
16594 int pip[2] = {fs->fd[0], fs->fd[1]};
16595
16596 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16597 INT_ON;
16598 if (pip[1] >= 0) {
16599 close(pip[0]);
16600 }
16601 if (prevfd > 0) {
16602 dup2(prevfd, 0);
16603 close(prevfd);
16604 }
16605 if (pip[1] > 1) {
16606 dup2(pip[1], 1);
16607 close(pip[1]);
16608 }
16609 evaltreenr(n, flags);
16610}
16611
16612static void
16613forkshell_shellexec(struct forkshell *fs)
16614{
16615 int idx = fs->fd[0];
16616 char **argv = fs->argv;
16617 char *path = fs->path;
16618
16619 FORCE_INT_ON;
16620 shellexec(argv[0], argv, path, idx, TRUE);
16621}
16622
16623static void
16624forkshell_child(struct forkshell *fs)
16625{
16626 switch ( fs->fpid ) {
16627 case FS_OPENHERE:
16628 forkshell_openhere(fs);
16629 break;
16630 case FS_EVALBACKCMD:
16631 forkshell_evalbackcmd(fs);
16632 break;
16633 case FS_EVALSUBSHELL:
16634 forkshell_evalsubshell(fs);
16635 break;
16636 case FS_EVALPIPE:
16637 forkshell_evalpipe(fs);
16638 break;
16639 case FS_SHELLEXEC:
16640 forkshell_shellexec(fs);
16641 break;
16642 }
16643}
16644
16645/*
16646 * Reinitialise the builtin environment variables in varinit. Their
16647 * current settings have been copied from the parent in vartab. Look
16648 * these up using the names from varinit_data, copy the details from
16649 * vartab to varinit and replace the old copy in vartab with the new
16650 * one in varinit.
16651 *
16652 * Also reinitialise the function pointers and line number variable.
16653 */
16654static void
16655reinitvar(void)
16656{
16657 int i;
16658 const char *name;
16659 struct var **old;
16660
16661 for (i=0; i<ARRAY_SIZE(varinit); ++i) {
16662 if (i == LINENO_INDEX)
16663 name = "LINENO=";
16664 else if (i == FUNCNAME_INDEX)
16665 name = "FUNCNAME=";
16666 else
16667 name = varinit_data[i].var_text;
16668 if ((old = findvar(name)) != NULL) {
16669 varinit[i] = **old;
16670 *old = varinit+i;
16671 }
16672 varinit[i].var_func = varinit_data[i].var_func;
16673 }
16674 vlineno.var_text = linenovar;
16675 vfuncname.var_text = funcnamevar;
16676}
16677
16678static void
16679spawn_forkshell(struct forkshell *fs, struct job *jp, union node *n, int mode)
16680{
16681 struct forkshell *new;
16682 char buf[32];
16683 const char *argv[] = { "sh", "--fs", NULL, NULL };
16684 intptr_t ret;
16685
16686 new = forkshell_prepare(fs);
16687 if (new == NULL)
16688 goto fail;
16689
16690 new->mode = mode;
16691 new->nprocs = jp == NULL ? 0 : jp->nprocs;
16692#if JOBS_WIN32
16693 new->jpnull = jp == NULL;
16694#endif
16695 sprintf(buf, "%p", new->hMapFile);
16696 argv[2] = buf;
16697 ret = spawnve(P_NOWAIT, bb_busybox_exec_path, (char *const *)argv, NULL);
16698 CloseHandle(new->hMapFile);
16699 UnmapViewOfFile(new);
16700 if (ret == -1) {
16701 fail:
16702 if (jp)
16703 freejob(jp);
16704 ash_msg_and_raise_error("unable to spawn shell");
16705 }
16706 forkparent(jp, n, mode, (HANDLE)ret);
16707}
16708
16709/*
16710 * forkshell_prepare() and friends
16711 *
16712 * The sequence is as follows:
16713 * - funcblocksize is initialized
16714 * - forkshell_size(fs) is called to calculate the exact memory needed
16715 * - a new struct is allocated
16716 * - funcblock, funcstring, relocate are initialized from the new block
16717 * - forkshell_copy(fs) is called to copy recursively everything over
16718 * it will record all relocations along the way
16719 *
16720 * When this memory is mapped elsewhere, pointer fixup will be needed
16721 */
16722
16723/* redefine without test that fs_size is nonzero */
16724#undef SAVE_PTR
16725#undef SAVE_PTR2
16726#undef SAVE_PTR3
16727#define SAVE_PTR(dst,note,flag) {MARK_PTR(dst,note,flag);}
16728
16729static int align_len(const char *s)
16730{
16731 return s ? SHELL_ALIGN(strlen(s)+1) : 0;
16732}
16733
16734struct datasize {
16735 int funcblocksize;
16736 int funcstringsize;
16737};
16738
16739#define SLIST_SIZE_BEGIN(name,type) \
16740static struct datasize \
16741name(struct datasize ds, type *p) \
16742{ \
16743 while (p) { \
16744 ds.funcblocksize += sizeof(type);
16745 /* do something here with p */
16746#define SLIST_SIZE_END() \
16747 p = p->next; \
16748 } \
16749 return ds; \
16750}
16751
16752#define SLIST_COPY_BEGIN(name,type) \
16753static type * \
16754name(type *vp) \
16755{ \
16756 type *start; \
16757 type **vpp; \
16758 vpp = &start; \
16759 while (vp) { \
16760 *vpp = funcblock; \
16761 funcblock = (char *) funcblock + sizeof(type);
16762 /* do something here with vpp and vp */
16763#define SLIST_COPY_END() \
16764 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE); \
16765 vp = vp->next; \
16766 vpp = &(*vpp)->next; \
16767 } \
16768 *vpp = NULL; \
16769 return start; \
16770}
16771
16772/*
16773 * struct var
16774 */
16775SLIST_SIZE_BEGIN(var_size,struct var)
16776ds.funcstringsize += align_len(p->var_text);
16777SLIST_SIZE_END()
16778
16779SLIST_COPY_BEGIN(var_copy,struct var)
16780(*vpp)->var_text = nodeckstrdup(vp->var_text);
16781(*vpp)->flags = vp->flags;
16782(*vpp)->var_func = NULL;
16783SAVE_PTR((*vpp)->var_text, xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"), FREE);
16784SLIST_COPY_END()
16785
16786/*
16787 * struct tblentry
16788 */
16789static struct datasize
16790tblentry_size(struct datasize ds, struct tblentry *tep)
16791{
16792 while (tep) {
16793 ds.funcblocksize += sizeof(struct tblentry) + align_len(tep->cmdname);
16794 /* CMDBUILTIN, e->param.cmd needs no pointer relocation */
16795 if (tep->cmdtype == CMDFUNCTION) {
16796 ds.funcblocksize += offsetof(struct funcnode, n);
16797 ds.funcblocksize = calcsize(ds.funcblocksize, &tep->param.func->n);
16798 }
16799 tep = tep->next;
16800 }
16801 return ds;
16802}
16803
16804static struct tblentry *
16805tblentry_copy(struct tblentry *tep)
16806{
16807 struct tblentry *start;
16808 struct tblentry **newp;
16809 int size;
16810
16811 newp = &start;
16812 while (tep) {
16813 *newp = funcblock;
16814 size = sizeof(struct tblentry) + align_len(tep->cmdname);
16815
16816 funcblock = (char *) funcblock + size;
16817 memcpy(*newp, tep, sizeof(struct tblentry)+strlen(tep->cmdname));
16818 switch (tep->cmdtype) {
16819 case CMDBUILTIN:
16820 /* Save index of builtin, not pointer; fixed by forkshell_init() */
16821 (*newp)->param.index = tep->param.cmd - builtintab;
16822 break;
16823 case CMDFUNCTION:
16824 (*newp)->param.func = funcblock;
16825 funcblock = (char *) funcblock + offsetof(struct funcnode, n);
16826 copynode(&tep->param.func->n);
16827 SAVE_PTR((*newp)->param.func, "param.func", NO_FREE);
16828 break;
16829 default:
16830 break;
16831 }
16832 SAVE_PTR((*newp)->next, xasprintf("cmdname '%s'", tep->cmdname), FREE);
16833 tep = tep->next;
16834 newp = &(*newp)->next;
16835 }
16836 *newp = NULL;
16837 return start;
16838}
16839
16840static struct datasize
16841cmdtable_size(struct datasize ds)
16842{
16843 int i;
16844 ds.funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE;
16845 for (i = 0; i < CMDTABLESIZE; i++)
16846 ds = tblentry_size(ds, cmdtable[i]);
16847 return ds;
16848}
16849
16850static struct tblentry **
16851cmdtable_copy(void)
16852{
16853 struct tblentry **new = funcblock;
16854 int i;
16855
16856 funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE;
16857 for (i = 0; i < CMDTABLESIZE; i++) {
16858 new[i] = tblentry_copy(cmdtable[i]);
16859 SAVE_PTR(new[i], xasprintf("cmdtable[%d]", i), FREE);
16860 }
16861 return new;
16862}
16863
16864#if ENABLE_ASH_ALIAS
16865/*
16866 * struct alias
16867 */
16868SLIST_SIZE_BEGIN(alias_size,struct alias)
16869ds.funcstringsize += align_len(p->name);
16870ds.funcstringsize += align_len(p->val);
16871SLIST_SIZE_END()
16872
16873SLIST_COPY_BEGIN(alias_copy,struct alias)
16874(*vpp)->name = nodeckstrdup(vp->name);
16875(*vpp)->val = nodeckstrdup(vp->val);
16876(*vpp)->flag = vp->flag;
16877SAVE_PTR((*vpp)->name, xasprintf("(*vpp)->name '%s'", vp->name ?: "NULL"), FREE);
16878SAVE_PTR((*vpp)->val, xasprintf("(*vpp)->val '%s'", vp->val ?: "NULL"), FREE);
16879SLIST_COPY_END()
16880
16881static struct datasize
16882atab_size(struct datasize ds)
16883{
16884 int i;
16885 ds.funcblocksize += sizeof(struct alias *)*ATABSIZE;
16886 for (i = 0; i < ATABSIZE; i++)
16887 ds = alias_size(ds, atab[i]);
16888 return ds;
16889}
16890
16891static struct alias **
16892atab_copy(void)
16893{
16894 struct alias **new = funcblock;
16895 int i;
16896
16897 funcblock = (char *) funcblock + sizeof(struct alias *)*ATABSIZE;
16898 for (i = 0; i < ATABSIZE; i++) {
16899 new[i] = alias_copy(atab[i]);
16900 SAVE_PTR(new[i], xasprintf("atab[%d]", i), FREE);
16901 }
16902 return new;
16903}
16904#endif
16905
16906/*
16907 * char **
16908 */
16909static struct datasize
16910argv_size(struct datasize ds, char **p)
16911{
16912 if (p) {
16913 while (*p) {
16914 ds.funcblocksize += sizeof(char *);
16915 ds.funcstringsize += align_len(*p);
16916 p++;
16917 }
16918 // Allow two extra elements for tryexec().
16919 ds.funcblocksize += 3 * sizeof(char *);
16920 }
16921 return ds;
16922}
16923
16924static char **
16925argv_copy(char **p)
16926{
16927 char **new, **start = funcblock;
16928#if FORKSHELL_DEBUG
16929 int i = 0;
16930#endif
16931
16932 if (p) {
16933 // Allow two extra elements for tryexec().
16934 funcblock = (char *) funcblock + 2 * sizeof(char *);
16935 while (*p) {
16936 new = funcblock;
16937 funcblock = (char *) funcblock + sizeof(char *);
16938 *new = nodeckstrdup(*p);
16939 SAVE_PTR(*new, xasprintf("argv[%d] '%s'", i++, *p), FREE);
16940 p++;
16941 }
16942 new = funcblock;
16943 funcblock = (char *) funcblock + sizeof(char *);
16944 *new = NULL;
16945 return start + 2;
16946 }
16947 return NULL;
16948}
16949
16950#if MAX_HISTORY
16951static struct datasize
16952history_size(struct datasize ds)
16953{
16954 int i;
16955 line_input_t *st = line_input_state;
16956
16957 ds.funcblocksize += sizeof(char *) * st->cnt_history;
16958 for (i = 0; i < st->cnt_history; i++) {
16959 ds.funcstringsize += align_len(st->history[i]);
16960 }
16961 return ds;
16962}
16963
16964static char **
16965history_copy(void)
16966{
16967 line_input_t *st = line_input_state;
16968 char **new = funcblock;
16969 int i;
16970
16971 funcblock = (char *)funcblock + sizeof(char *) * st->cnt_history;
16972 for (i = 0; i < st->cnt_history; i++) {
16973 new[i] = nodeckstrdup(st->history[i]);
16974 SAVE_PTR(new[i],
16975 xasprintf("history[%d] '%s'", i, st->history[i]), FREE);
16976 }
16977 return new;
16978}
16979#endif
16980
16981#if JOBS_WIN32
16982/*
16983 * struct procstat
16984 */
16985static struct datasize
16986procstat_size(struct datasize ds, int nj)
16987{
16988 struct job *jp = jobtab + nj;
16989
16990 if (jp->ps != &jp->ps0)
16991 ds.funcblocksize += sizeof(struct procstat) * jp->nprocs;
16992
16993 for (int i = 0; i < jp->nprocs; i++)
16994 ds.funcstringsize += align_len(jp->ps[i].ps_cmd);
16995
16996 return ds;
16997}
16998
16999static struct procstat *
17000procstat_copy(int nj)
17001{
17002 struct job *jp = jobtab + nj;
17003 struct procstat *new = funcblock;
17004
17005 funcblock = (char *)funcblock + sizeof(struct procstat) * jp->nprocs;
17006 memcpy(new, jp->ps, sizeof(struct procstat) * jp->nprocs);
17007
17008 for (int i = 0; i < jp->nprocs; i++) {
17009 new[i].ps_cmd = nodeckstrdup(jp->ps[i].ps_cmd);
17010 SAVE_PTR(new[i].ps_cmd,
17011 xasprintf("jobtab[%d].ps[%d].ps_cmd '%s'",
17012 nj, i, jp->ps[i].ps_cmd), FREE);
17013 }
17014 return new;
17015}
17016
17017/*
17018 * struct jobs
17019 */
17020static struct datasize
17021jobtab_size(struct datasize ds)
17022{
17023 ds.funcblocksize += sizeof(struct job) * njobs;
17024 for (int i = 0; i < njobs; i++) {
17025 if (jobtab[i].used)
17026 ds = procstat_size(ds, i);
17027 }
17028 return ds;
17029}
17030
17031static struct job *
17032jobtab_copy(void)
17033{
17034 struct job *new = funcblock;
17035 int i;
17036
17037 funcblock = (char *)funcblock + sizeof(struct job) * njobs;
17038 memcpy(new, jobtab, sizeof(struct job) * njobs);
17039
17040 for (i = 0; i < njobs; i++) {
17041 if (!jobtab[i].used)
17042 continue;
17043
17044 if (jobtab[i].ps == &jobtab[i].ps0) {
17045 new[i].ps0.ps_cmd = nodeckstrdup(jobtab[i].ps0.ps_cmd);
17046 SAVE_PTR(new[i].ps0.ps_cmd,
17047 xasprintf("jobtab[%d].ps0.ps_cmd '%s'",
17048 i, jobtab[i].ps0.ps_cmd), FREE);
17049 new[i].ps = &new[i].ps0;
17050 } else if (jobtab[i].nprocs) {
17051 new[i].ps = procstat_copy(i);
17052 } else {
17053 new[i].ps = NULL;
17054 }
17055 SAVE_PTR(new[i].ps, xasprintf("jobtab[%d].ps", i), FREE);
17056
17057 if (jobtab[i].prev_job) {
17058 new[i].prev_job = new + (jobtab[i].prev_job - jobtab);
17059 SAVE_PTR(new[i].prev_job,
17060 xasprintf("jobtab[%d].prev_job", i), FREE);
17061 }
17062 }
17063 return new;
17064}
17065#endif
17066
17067/*
17068 * struct redirtab
17069 */
17070static int
17071redirtab_size(int funcblocksize, struct redirtab *rdtp)
17072{
17073 while (rdtp) {
17074 funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
17075 rdtp = rdtp->next;
17076 }
17077 return funcblocksize;
17078}
17079
17080static struct redirtab *
17081redirtab_copy(struct redirtab *rdtp)
17082{
17083 struct redirtab *start;
17084 struct redirtab **vpp;
17085
17086 vpp = &start;
17087 while (rdtp) {
17088 int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
17089 *vpp = funcblock;
17090 funcblock = (char *) funcblock + size;
17091 memcpy(*vpp, rdtp, size);
17092 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE);
17093 rdtp = rdtp->next;
17094 vpp = &(*vpp)->next;
17095 }
17096 *vpp = NULL;
17097 return start;
17098}
17099
17100static struct datasize
17101globals_var_size(struct datasize ds)
17102{
17103 int i;
17104
17105 ds.funcblocksize += sizeof(struct globals_var);
17106 ds.funcstringsize += align_len(funcname);
17107 ds = argv_size(ds, shellparam.p);
17108 ds.funcblocksize = redirtab_size(ds.funcblocksize, redirlist);
17109 for (i = 0; i < VTABSIZE; i++)
17110 ds = var_size(ds, vartab[i]);
17111 return ds;
17112}
17113
17114#undef funcname
17115#undef shellparam
17116#undef redirlist
17117#undef vartab
17118static struct globals_var *
17119globals_var_copy(void)
17120{
17121 int i;
17122 struct globals_var *gvp, *new;
17123
17124 gvp = ash_ptr_to_globals_var;
17125 new = funcblock;
17126 funcblock = (char *) funcblock + sizeof(struct globals_var);
17127 memcpy(new, gvp, sizeof(struct globals_var));
17128
17129 new->funcname = nodeckstrdup(gvp->funcname);
17130 SAVE_PTR(new->funcname, xasprintf("funcname '%s'", gvp->funcname ?: "NULL"), FREE);
17131
17132 /* shparam */
17133 new->shellparam.malloced = 0;
17134 new->shellparam.p = argv_copy(gvp->shellparam.p);
17135 SAVE_PTR(new->shellparam.p, "shellparam.p", NO_FREE);
17136
17137 new->redirlist = redirtab_copy(gvp->redirlist);
17138 SAVE_PTR(new->redirlist, "redirlist", NO_FREE);
17139
17140 for (i = 0; i < VTABSIZE; i++) {
17141 new->vartab[i] = var_copy(gvp->vartab[i]);
17142 SAVE_PTR(new->vartab[i], xasprintf("vartab[%d]", i), FREE);
17143 }
17144
17145 return new;
17146}
17147
17148static struct datasize
17149globals_misc_size(struct datasize ds)
17150{
17151 ds.funcblocksize += sizeof(struct globals_misc);
17152 ds.funcstringsize += align_len(minusc);
17153 if (curdir != nullstr)
17154 ds.funcstringsize += align_len(curdir);
17155 if (physdir != nullstr)
17156 ds.funcstringsize += align_len(physdir);
17157 ds.funcstringsize += align_len(arg0);
17158 ds.funcstringsize += align_len(commandname);
17159 for (int i = 0; i < ARRAY_SIZE(trap); i++)
17160 ds.funcstringsize += align_len(trap[i]);
17161 return ds;
17162}
17163
17164#undef minusc
17165#undef curdir
17166#undef physdir
17167#undef arg0
17168#undef commandname
17169#undef nullstr
17170#undef trap
17171static struct globals_misc *
17172globals_misc_copy(void)
17173{
17174 struct globals_misc *p = ash_ptr_to_globals_misc;
17175 struct globals_misc *new = funcblock;
17176
17177 funcblock = (char *) funcblock + sizeof(struct globals_misc);
17178 memcpy(new, p, sizeof(struct globals_misc));
17179
17180 new->minusc = nodeckstrdup(p->minusc);
17181 new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr;
17182 new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr;
17183 new->arg0 = nodeckstrdup(p->arg0);
17184 new->commandname = nodeckstrdup(p->commandname);
17185 SAVE_PTR(new->minusc, xasprintf("minusc '%s'", p->minusc ?: "NULL"), FREE);
17186 SAVE_PTR(new->curdir,
17187 xasprintf("curdir '%s'", new->curdir ?: "NULL"), FREE);
17188 SAVE_PTR(new->physdir,
17189 xasprintf("physdir '%s'", new->physdir ?: "NULL"), FREE);
17190 SAVE_PTR(new->arg0, xasprintf("arg0 '%s'", p->arg0 ?: "NULL"), FREE);
17191 SAVE_PTR(new->commandname,
17192 xasprintf("commandname '%s'", p->commandname ?: "NULL"), FREE);
17193 for (int i = 0; i < ARRAY_SIZE(p->trap); i++) {
17194 new->trap[i] = nodeckstrdup(p->trap[i]);
17195 SAVE_PTR(new->trap[i], xasprintf("trap[%d]", i), FREE);
17196 }
17197 return new;
17198}
17199
17200static struct datasize
17201forkshell_size(struct forkshell *fs)
17202{
17203 struct datasize ds = {0, 0};
17204
17205 ds.funcstringsize += align_len(fs->path);
17206 if (fs->fpid == FS_OPENHERE)
17207 return ds;
17208
17209 ds = globals_var_size(ds);
17210 ds = globals_misc_size(ds);
17211 ds = cmdtable_size(ds);
17212
17213 ds.funcblocksize = calcsize(ds.funcblocksize, fs->n);
17214 ds = argv_size(ds, fs->argv);
17215
17216 if ((ENABLE_ASH_ALIAS || MAX_HISTORY || JOBS_WIN32) &&
17217 fs->fpid != FS_SHELLEXEC) {
17218#if ENABLE_ASH_ALIAS
17219 ds = atab_size(ds);
17220#endif
17221#if MAX_HISTORY
17222 if (line_input_state)
17223 ds = history_size(ds);
17224#endif
17225#if JOBS_WIN32
17226 ds = jobtab_size(ds);
17227#endif
17228 }
17229 return ds;
17230}
17231
17232static void
17233forkshell_copy(struct forkshell *fs, struct forkshell *new)
17234{
17235 memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */
17236
17237 new->path = nodeckstrdup(fs->path);
17238 SAVE_PTR(new->path, xasprintf("path '%s'", fs->path ?: "NULL"), FREE);
17239 if (fs->fpid == FS_OPENHERE)
17240 return;
17241
17242 new->gvp = globals_var_copy();
17243 new->gmp = globals_misc_copy();
17244 new->cmdtable = cmdtable_copy();
17245 SAVE_PTR(new->gvp, "gvp", NO_FREE);
17246 SAVE_PTR(new->gmp, "gmp", NO_FREE);
17247 SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE);
17248
17249 new->n = copynode(fs->n);
17250 new->argv = argv_copy(fs->argv);
17251 SAVE_PTR(new->n, "n", NO_FREE);
17252 SAVE_PTR(new->argv, "argv", NO_FREE);
17253
17254 if ((ENABLE_ASH_ALIAS || MAX_HISTORY || JOBS_WIN32) &&
17255 fs->fpid != FS_SHELLEXEC) {
17256#if ENABLE_ASH_ALIAS
17257 new->atab = atab_copy();
17258 SAVE_PTR(new->atab, "atab", NO_FREE);
17259#endif
17260#if MAX_HISTORY
17261 if (line_input_state) {
17262 new->history = history_copy();
17263 SAVE_PTR(new->history, "history", NO_FREE);
17264 new->cnt_history = line_input_state->cnt_history;
17265 }
17266#endif
17267#if JOBS_WIN32
17268 if (njobs) {
17269 new->jobtab = jobtab_copy();
17270 SAVE_PTR(new->jobtab, "jobtab", NO_FREE);
17271 new->njobs = njobs;
17272 if (curjob) {
17273 new->curjob = new->jobtab + (curjob - jobtab);
17274 SAVE_PTR(new->curjob, "curjob", NO_FREE);
17275 }
17276 }
17277#endif
17278 }
17279}
17280
17281#if FORKSHELL_DEBUG
17282#define NUM_BLOCKS FUNCSTRING
17283enum {GVP, GMP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, JOBTAB, FUNCSTRING};
17284
17285/* fp0 and notes can each be NULL */
17286static void
17287forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
17288{
17289 FILE *fp;
17290 void *lfuncblock;
17291 char *lfuncstring;
17292 char *lrelocate;
17293 char *s;
17294 int count, i, total, bitmapsize;
17295 int size[NUM_BLOCKS];
17296 char *lptr[NUM_BLOCKS+1];
17297 const char *fsname[] = {
17298 "FS_OPENHERE",
17299 "FS_EVALBACKCMD",
17300 "FS_EVALSUBSHELL",
17301 "FS_EVALPIPE",
17302 "FS_SHELLEXEC"
17303 };
17304
17305 if (fp0 != NULL) {
17306 fp = fp0;
17307 }
17308 else {
17309 char name[64];
17310 static int num = 0;
17311
17312 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
17313 if ((fp=fopen(name, "w")) == NULL)
17314 return;
17315 }
17316
17317 bitmapsize = (fs->relocatesize + 7)/8;
17318 total = sizeof(struct forkshell) + fs->funcblocksize +
17319 fs->funcstringsize + bitmapsize;
17320 fprintf(fp, "total size %6d = %d + %d + %d + %d = %d\n",
17321 fs->size + bitmapsize,
17322 (int)sizeof(struct forkshell), fs->funcblocksize,
17323 fs->funcstringsize, bitmapsize, total);
17324
17325 lfuncblock = (char *)(fs + 1);
17326 lfuncstring = (char *)lfuncblock + fs->funcblocksize;
17327 lrelocate = (char *)lfuncstring + fs->funcstringsize;
17328
17329 /* funcblocksize is zero for FS_OPENHERE */
17330 if (fs->funcblocksize != 0) {
17331 /* Depending on the configuration and the type of forkshell
17332 * some items may not be present. */
17333 lptr[FUNCSTRING] = lfuncstring;
17334#if JOBS_WIN32
17335 lptr[JOBTAB] = fs->jobtab ? (char *)fs->jobtab : lptr[FUNCSTRING];
17336#else
17337 lptr[JOBTAB] = lptr[FUNCSTRING];
17338#endif
17339#if MAX_HISTORY
17340 lptr[HISTORY] = fs->history ? (char *)fs->history : lptr[JOBTAB];
17341#else
17342 lptr[HISTORY] = lptr[JOBTAB];
17343#endif
17344 lptr[ATAB] = IF_ASH_ALIAS(fs->atab ? (char *)fs->atab :) lptr[HISTORY];
17345 lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB];
17346 lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV];
17347 lptr[CMDTABLE] = (char *)fs->cmdtable;
17348 lptr[GMP] = (char *)fs->gmp;
17349 lptr[GVP] = (char *)fs->gvp;
17350
17351 fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize);
17352 total = 0;
17353 for (i=0; i<NUM_BLOCKS; ++i) {
17354 size[i] = (int)(lptr[i+1] - lptr[i]);
17355 total += size[i];
17356 fprintf(fp, "%d %c ", size[i], i == NUM_BLOCKS - 1 ? '=' : '+');
17357 }
17358 fprintf(fp, "%d\n\n", total);
17359 }
17360 else {
17361 fprintf(fp, "\n");
17362 }
17363
17364 fprintf(fp, "%s\n\n", fsname[fs->fpid]);
17365 fprintf(fp, "--- relocate ---\n");
17366 count = 0;
17367 for (i = 0; i < fs->relocatesize; ++i) {
17368 if (lrelocate[i/8] & (1 << i % 8)) {
17369 char **ptr = (char **)((char *)fs + i * sizeof(char *));
17370 fprintf(fp, "%p %p %s\n", ptr, *ptr,
17371 notes && notes[i] ? notes[i] : "");
17372 ++count;
17373 }
17374 }
17375 fprintf(fp, "--- %d relocations ---\n\n", count);
17376
17377 fprintf(fp, "--- funcstring ---\n");
17378 count = 0;
17379 s = lfuncstring;
17380 while (s-lfuncstring < fs->funcstringsize) {
17381 if (!*s) {
17382 ++s;
17383 continue;
17384 }
17385 fprintf(fp, "%p '%s'\n", s, s);
17386 s += strlen(s)+1;
17387 ++count;
17388 }
17389 fprintf(fp, "--- %d strings ---\n", count);
17390
17391 if (fp0 == NULL)
17392 fclose(fp);
17393}
17394#endif
17395
17396static struct forkshell *
17397forkshell_prepare(struct forkshell *fs)
17398{
17399 struct forkshell *new;
17400 struct datasize ds;
17401 int size, relocatesize, bitmapsize;
17402 HANDLE h;
17403 SECURITY_ATTRIBUTES sa;
17404#if FORKSHELL_DEBUG
17405 char *relocate;
17406 char name[64];
17407 FILE *fp;
17408 static int num = 0;
17409#endif
17410
17411 /* calculate size of structure, funcblock and funcstring */
17412 ds = forkshell_size(fs);
17413 size = sizeof(struct forkshell) + ds.funcblocksize + ds.funcstringsize;
17414 relocatesize = (sizeof(struct forkshell) + ds.funcblocksize)/sizeof(char *);
17415 bitmapsize = (relocatesize + 7)/8;
17416
17417 /* Allocate shared memory region */
17418 memset(&sa, 0, sizeof(sa));
17419 sa.nLength = sizeof(sa);
17420 sa.lpSecurityDescriptor = NULL;
17421 sa.bInheritHandle = TRUE;
17422 h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0,
17423 size+bitmapsize, NULL);
17424
17425 /* Initialise pointers */
17426 new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
17427 if (new == NULL)
17428 return NULL;
17429 fs_size = size;
17430 fs_start = new;
17431 funcblock = (char *)(new + 1);
17432 funcstring_end = (char *)new + size;
17433#if FORKSHELL_DEBUG
17434 fs_funcstring = (char *)new + sizeof(struct forkshell) + ds.funcblocksize;
17435 relocate = (char *)new + size;
17436 annot = (const char **)xzalloc(sizeof(char *)*relocatesize);
17437 annot_free = xzalloc(relocatesize);
17438#endif
17439
17440 /* Now pack them all */
17441 forkshell_copy(fs, new);
17442
17443 /* Finish it up */
17444 new->size = size;
17445 new->relocatesize = relocatesize;
17446 new->old_base = (char *)new;
17447 new->hMapFile = h;
17448#if FORKSHELL_DEBUG
17449 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
17450 if ((fp=fopen(name, "w")) != NULL) {
17451 int i;
17452
17453 new->funcblocksize = (char *)funcblock - (char *)(new + 1);
17454 new->funcstringsize = (char *)new + size - funcstring_end;
17455
17456 /* perform some sanity checks on pointers */
17457 fprintf(fp, "forkshell %p %6d\n", new, (int)sizeof(*new));
17458 fprintf(fp, "funcblock %p %6d\n", new+1, new->funcblocksize);
17459 fprintf(fp, "funcstring %p %6d\n", funcstring_end,
17460 new->funcstringsize);
17461 if ((char *)funcblock != funcstring_end)
17462 fprintf(fp, " funcstring != end funcblock + 1 %p\n", funcblock);
17463 fprintf(fp, "relocate %p %6d\n\n", relocate, bitmapsize);
17464
17465 forkshell_print(fp, new, annot);
17466
17467 for (i = 0; i < relocatesize; ++i) {
17468 if (annot_free[i]) {
17469 free((void *)annot[i]);
17470 }
17471 }
17472 free(annot);
17473 free(annot_free);
17474 annot = NULL;
17475 fclose(fp);
17476 }
17477#endif
17478 return new;
17479}
17480
17481#undef trap_ptr
17482static void
17483forkshell_init(const char *idstr)
17484{
17485 struct forkshell *fs;
17486 void *map_handle;
17487 HANDLE h;
17488 int i;
17489 char **ptr;
17490 char *lrelocate;
17491 struct jmploc jmploc;
17492
17493 if (sscanf(idstr, "%p", &map_handle) != 1)
17494 return;
17495
17496 h = (HANDLE)map_handle;
17497 fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
17498 if (!fs)
17499 return;
17500
17501 /* this memory can't be freed */
17502 sticky_mem_start = fs;
17503 sticky_mem_end = (char *) fs + fs->size;
17504
17505 /* pointer fixup */
17506 lrelocate = (char *)fs + fs->size;
17507 for (i = 0; i < fs->relocatesize; i++) {
17508 if (lrelocate[i/8] & (1 << i % 8)) {
17509 ptr = (char **)((char *)fs + i * sizeof(char *));
17510 if (*ptr)
17511 *ptr = (char *)fs + (*ptr - fs->old_base);
17512 }
17513 }
17514
17515 if (fs->fpid == FS_OPENHERE)
17516 goto end;
17517
17518 /* Now fix up stuff that can't be transferred */
17519 for (i = 0; i < CMDTABLESIZE; i++) {
17520 struct tblentry *e = fs->cmdtable[i];
17521 while (e) {
17522 if (e->cmdtype == CMDBUILTIN)
17523 e->param.cmd = builtintab + e->param.index;
17524 e = e->next;
17525 }
17526 }
17527 fs->gmp->trap_ptr = fs->gmp->trap;
17528
17529 /* Set global variables */
17530 ASSIGN_CONST_PTR(&ash_ptr_to_globals_var, fs->gvp);
17531 ASSIGN_CONST_PTR(&ash_ptr_to_globals_misc, fs->gmp);
17532 cmdtable = fs->cmdtable;
17533#if ENABLE_ASH_ALIAS
17534 atab = fs->atab; /* will be NULL for FS_SHELLEXEC */
17535#endif
17536#if MAX_HISTORY
17537 if (fs->cnt_history) {
17538 line_input_state = new_line_input_t(FOR_SHELL);
17539 line_input_state->cnt_history = fs->cnt_history;
17540 for (i = 0; i < line_input_state->cnt_history; i++)
17541 line_input_state->history[i] = fs->history[i];
17542 }
17543#endif
17544#if JOBS_WIN32
17545 jobtab = fs->jobtab;
17546 njobs = fs->njobs;
17547 curjob = fs->curjob;
17548#endif
17549
17550 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
17551
17552 reinitvar();
17553
17554 if (setjmp(jmploc.loc)) {
17555 exitreset();
17556 exitshell();
17557 }
17558 exception_handler = &jmploc;
17559
17560 shlvl++;
17561 if (fs->mode == FORK_BG) {
17562 SetConsoleCtrlHandler(NULL, TRUE);
17563 if (fs->nprocs == 0) {
17564 close(0);
17565 if (open(bb_dev_null, O_RDONLY) != 0)
17566 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
17567 }
17568 }
17569 else {
17570 SetConsoleCtrlHandler(ctrl_handler, TRUE);
17571 }
17572
17573 if (fs->n && fs->n->type == NCMD /* is it single cmd? */
17574 /* && n->ncmd.args->type == NARG - always true? */
17575 && fs->n->ncmd.args && strcmp(fs->n->ncmd.args->narg.text, "trap") == 0
17576 && fs->n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
17577 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
17578 ) {
17579 TRACE(("Trap hack\n"));
17580 /* Save trap handler strings for trap builtin to print */
17581 fs->gmp->trap_ptr = xmemdup(fs->gmp->trap, sizeof(fs->gmp->trap));
17582 /* Fall through into clearing traps */
17583 }
17584 clear_traps();
17585#if JOBS_WIN32
17586 /* do job control only in root shell */
17587 doing_jobctl = 0;
17588
17589 if (fs->n && fs->n->type == NCMD && fs->n->ncmd.args &&
17590 strcmp(fs->n->ncmd.args->narg.text, "jobs") == 0) {
17591 TRACE(("Job hack\n"));
17592 if (!fs->jpnull)
17593 freejob(curjob);
17594 goto end;
17595 }
17596 for (struct job *jp = curjob; jp; jp = jp->prev_job)
17597 freejob(jp);
17598#endif
17599 end:
17600 forkshell_child(fs);
17601}
17602
17603#undef free
17604static void
17605sticky_free(void *base)
17606{
17607 if (base >= sticky_mem_start && base < sticky_mem_end)
17608 return;
17609 free(base);
17610}
17611#endif
14914 17612
14915/*- 17613/*-
14916 * Copyright (c) 1989, 1991, 1993, 1994 17614 * Copyright (c) 1989, 1991, 1993, 1994