aboutsummaryrefslogtreecommitdiff
path: root/shell/ash.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/ash.c')
-rw-r--r--shell/ash.c2834
1 files changed, 2795 insertions, 39 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 841ffe880..12c95c338 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,94 @@ 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
387#if defined(_WIN64)
388# define ALIGN64 ALIGNED(16)
389#else
390# define ALIGN64
391#endif
392
393struct forkshell {
394 /* filled by forkshell_copy() */
395 struct globals_misc *gmp;
396 struct globals_var *gvp;
397 struct tblentry **cmdtable;
398#if ENABLE_ASH_ALIAS
399 struct alias **atab;
400#endif
401#if MAX_HISTORY
402 char **history;
403 int cnt_history;
404#endif
405#if JOBS_WIN32
406 struct job *jobtab;
407 unsigned njobs;
408 struct job *curjob;
409#endif
410 HANDLE hMapFile;
411 char *old_base;
412 int size;
413# if FORKSHELL_DEBUG
414 int funcblocksize;
415 int funcstringsize;
416# endif
417 int relocatesize;
418
419 /* type of forkshell */
420 int fpid;
421
422 /* generic data, used by forkshell_child */
423 int mode;
424 int nprocs;
425#if JOBS_WIN32
426 int jpnull;
427#endif
428
429 /* optional data, used by forkshell_child */
430 int flags;
431 int fd[3];
432 union node *n;
433 char **argv;
434 const char *path;
435} ALIGN64;
436
437enum {
438 FS_OPENHERE,
439 FS_EVALBACKCMD,
440 FS_EVALSUBSHELL,
441 FS_EVALPIPE,
442 FS_SHELLEXEC
443};
444
445static struct forkshell* forkshell_prepare(struct forkshell *fs);
446static void forkshell_init(const char *idstr);
447static void *sticky_mem_start, *sticky_mem_end;
448static void sticky_free(void *p);
449# define free(p) sticky_free(p)
450#if !JOBS && !JOBS_WIN32
451#define spawn_forkshell(fs, jp, n, mode) spawn_forkshell(fs, jp, mode)
452#endif
453static void spawn_forkshell(struct forkshell *fs, struct job *jp,
454 union node *n, int mode);
455# if FORKSHELL_DEBUG
456static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes);
457# endif
458#endif
459
323/* ============ Hash table sizes. Configurable. */ 460/* ============ Hash table sizes. Configurable. */
324 461
325#define VTABSIZE 39 462#define VTABSIZE 39
@@ -347,7 +484,11 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
347 "m" "monitor", 484 "m" "monitor",
348 "n" "noexec", 485 "n" "noexec",
349/* Ditto: bash has no "set -s", "set -c" */ 486/* Ditto: bash has no "set -s", "set -c" */
487#if !ENABLE_PLATFORM_MINGW32
350 "s" "", 488 "s" "",
489#else
490 "s" "stdin",
491#endif
351 "c" "", 492 "c" "",
352 "x" "xtrace", 493 "x" "xtrace",
353 "v" "verbose", 494 "v" "verbose",
@@ -364,6 +505,18 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
364 ,"\0" "nolog" 505 ,"\0" "nolog"
365 ,"\0" "debug" 506 ,"\0" "debug"
366#endif 507#endif
508#if ENABLE_PLATFORM_MINGW32
509 ,"X" "winxp"
510#endif
511#if ENABLE_ASH_NOCONSOLE
512 ,"\0" "noconsole"
513 ,"\0" "noiconify"
514#endif
515#if ENABLE_ASH_GLOB_OPTIONS
516 ,"\0" "nocaseglob"
517 ,"\0" "nohiddenglob"
518 ,"\0" "nohidsysglob"
519#endif
367}; 520};
368//bash 4.4.23 also has these opts (with these defaults): 521//bash 4.4.23 also has these opts (with these defaults):
369//braceexpand on 522//braceexpand on
@@ -437,6 +590,11 @@ struct parsefile {
437 590
438 /* Number of outstanding calls to pungetc. */ 591 /* Number of outstanding calls to pungetc. */
439 int unget; 592 int unget;
593
594#if ENABLE_PLATFORM_MINGW32
595 /* True if a trailing CR from a previous read was left unprocessed. */
596 int cr;
597#endif
440}; 598};
441 599
442 600
@@ -460,17 +618,31 @@ struct jmploc {
460struct globals_misc { 618struct globals_misc {
461 uint8_t exitstatus; /* exit status of last command */ 619 uint8_t exitstatus; /* exit status of last command */
462 uint8_t back_exitstatus;/* exit status of backquoted command */ 620 uint8_t back_exitstatus;/* exit status of backquoted command */
621#if !ENABLE_PLATFORM_MINGW32
463 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ 622 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
623#endif
464 smallint inps4; /* Prevent PS4 nesting. */ 624 smallint inps4; /* Prevent PS4 nesting. */
625#if !ENABLE_PLATFORM_MINGW32
465 smallint vforked; 626 smallint vforked;
627#endif
466 int savestatus; /* exit status of last command outside traps */ 628 int savestatus; /* exit status of last command outside traps */
467 int rootpid; /* pid of main shell */ 629 int rootpid; /* pid of main shell */
468 /* shell level: 0 for the main shell, 1 for its children, and so on */ 630 /* shell level: 0 for the main shell, 1 for its children, and so on */
469 int shlvl; 631 int shlvl;
632#if ENABLE_PLATFORM_MINGW32
633 int loopnest; /* current loop nesting level */
634#endif
470#define rootshell (!shlvl) 635#define rootshell (!shlvl)
471 int errlinno; 636 int errlinno;
472 637
473 char *minusc; /* argument to -c option */ 638 char *minusc; /* argument to -c option */
639#if ENABLE_PLATFORM_MINGW32
640 char *dirarg; /* argument to -d option */
641 char *title; /* argument to -t option */
642#if ENABLE_SUW32
643 int delayexit; /* set by -N option */
644# endif
645#endif
474 646
475 char *curdir; // = nullstr; /* current working directory */ 647 char *curdir; // = nullstr; /* current working directory */
476 char *physdir; // = nullstr; /* physical working directory */ 648 char *physdir; // = nullstr; /* physical working directory */
@@ -486,8 +658,12 @@ struct globals_misc {
486 * but we do read it async. 658 * but we do read it async.
487 */ 659 */
488 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ 660 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
661#if !ENABLE_PLATFORM_MINGW32
489 volatile /*sig_atomic_t*/ smallint gotsigchld; /* 1 = got SIGCHLD */ 662 volatile /*sig_atomic_t*/ smallint gotsigchld; /* 1 = got SIGCHLD */
490 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ 663 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
664#else
665 volatile /*sig_atomic_t*/ smallint waitcmd_int; /* SIGINT in wait */
666#endif
491 smallint exception_type; /* kind of exception: */ 667 smallint exception_type; /* kind of exception: */
492#define EXINT 0 /* SIGINT received */ 668#define EXINT 0 /* SIGINT received */
493#define EXERROR 1 /* a generic error */ 669#define EXERROR 1 /* a generic error */
@@ -522,8 +698,21 @@ struct globals_misc {
522# define nolog optlist[16 + BASH_PIPEFAIL] 698# define nolog optlist[16 + BASH_PIPEFAIL]
523# define debug optlist[17 + BASH_PIPEFAIL] 699# define debug optlist[17 + BASH_PIPEFAIL]
524#endif 700#endif
701#if ENABLE_PLATFORM_MINGW32
702# define winxp optlist[16 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
703# if ENABLE_ASH_NOCONSOLE
704# define noconsole optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
705# define noiconify optlist[18 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
706# endif
707# if ENABLE_ASH_GLOB_OPTIONS
708# define nocaseglob optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
709# define nohiddenglob optlist[18 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
710# define nohidsysglob optlist[19 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
711# endif
712#endif
525 713
526 /* trap handler commands */ 714 /* trap handler commands */
715#if !ENABLE_PLATFORM_MINGW32
527 /* 716 /*
528 * Sigmode records the current value of the signal handlers for the various 717 * Sigmode records the current value of the signal handlers for the various
529 * modes. A value of zero means that the current handler is not known. 718 * modes. A value of zero means that the current handler is not known.
@@ -537,6 +726,7 @@ struct globals_misc {
537 726
538 /* indicates specified signal received */ 727 /* indicates specified signal received */
539 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ 728 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
729#endif
540 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ 730 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
541 char *trap[NSIG + 1]; 731 char *trap[NSIG + 1];
542/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */ 732/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
@@ -567,12 +757,24 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
567#define back_exitstatus (G_misc.back_exitstatus ) 757#define back_exitstatus (G_misc.back_exitstatus )
568#define job_warning (G_misc.job_warning) 758#define job_warning (G_misc.job_warning)
569#define inps4 (G_misc.inps4 ) 759#define inps4 (G_misc.inps4 )
760#if !ENABLE_PLATFORM_MINGW32
570#define vforked (G_misc.vforked ) 761#define vforked (G_misc.vforked )
762#else
763#define vforked 0
764#endif
571#define savestatus (G_misc.savestatus ) 765#define savestatus (G_misc.savestatus )
572#define rootpid (G_misc.rootpid ) 766#define rootpid (G_misc.rootpid )
573#define shlvl (G_misc.shlvl ) 767#define shlvl (G_misc.shlvl )
574#define errlinno (G_misc.errlinno ) 768#define errlinno (G_misc.errlinno )
769#if ENABLE_PLATFORM_MINGW32
770#define loopnest (G_misc.loopnest )
771#endif
575#define minusc (G_misc.minusc ) 772#define minusc (G_misc.minusc )
773#if ENABLE_PLATFORM_MINGW32
774#define dirarg (G_misc.dirarg )
775#define title (G_misc.title )
776#define delayexit (G_misc.delayexit )
777#endif
576#define curdir (G_misc.curdir ) 778#define curdir (G_misc.curdir )
577#define physdir (G_misc.physdir ) 779#define physdir (G_misc.physdir )
578#define arg0 (G_misc.arg0 ) 780#define arg0 (G_misc.arg0 )
@@ -580,6 +782,9 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
580#define exception_type (G_misc.exception_type ) 782#define exception_type (G_misc.exception_type )
581#define suppress_int (G_misc.suppress_int ) 783#define suppress_int (G_misc.suppress_int )
582#define pending_int (G_misc.pending_int ) 784#define pending_int (G_misc.pending_int )
785#if ENABLE_PLATFORM_MINGW32
786#define waitcmd_int (G_misc.waitcmd_int )
787#endif
583#define gotsigchld (G_misc.gotsigchld ) 788#define gotsigchld (G_misc.gotsigchld )
584#define pending_sig (G_misc.pending_sig ) 789#define pending_sig (G_misc.pending_sig )
585#define nullstr (G_misc.nullstr ) 790#define nullstr (G_misc.nullstr )
@@ -596,6 +801,13 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
596#define groupinfo (G_misc.groupinfo ) 801#define groupinfo (G_misc.groupinfo )
597#define random_gen (G_misc.random_gen ) 802#define random_gen (G_misc.random_gen )
598#define backgndpid (G_misc.backgndpid ) 803#define backgndpid (G_misc.backgndpid )
804
805#if ENABLE_PLATFORM_MINGW32
806#undef got_sigchld
807#undef pending_sig
808#define pending_sig (0)
809#endif
810
599#define INIT_G_misc() do { \ 811#define INIT_G_misc() do { \
600 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \ 812 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \
601 savestatus = -1; \ 813 savestatus = -1; \
@@ -614,6 +826,9 @@ static void trace_printf(const char *fmt, ...);
614static void trace_vprintf(const char *fmt, va_list va); 826static void trace_vprintf(const char *fmt, va_list va);
615# define TRACE(param) trace_printf param 827# define TRACE(param) trace_printf param
616# define TRACEV(param) trace_vprintf param 828# define TRACEV(param) trace_vprintf param
829# if ENABLE_PLATFORM_MINGW32 && defined(close)
830# undef close
831# endif
617# define close(fd) do { \ 832# define close(fd) do { \
618 int dfd = (fd); \ 833 int dfd = (fd); \
619 if (close(dfd) < 0) \ 834 if (close(dfd) < 0) \
@@ -647,6 +862,7 @@ var_end(const char *var)
647 return var; 862 return var;
648} 863}
649 864
865#if !ENABLE_PLATFORM_MINGW32
650/* Our signal logic never blocks individual signals 866/* Our signal logic never blocks individual signals
651 * using signal mask - only by setting SIG_IGN handler. 867 * using signal mask - only by setting SIG_IGN handler.
652 * Therefore just unmasking all of them instead of "restore old mask" 868 * Therefore just unmasking all of them instead of "restore old mask"
@@ -658,6 +874,7 @@ sigclearmask(void)
658{ 874{
659 sigprocmask_allsigs(SIG_UNBLOCK); 875 sigprocmask_allsigs(SIG_UNBLOCK);
660} 876}
877#endif
661 878
662/* Reset handler when entering a subshell */ 879/* Reset handler when entering a subshell */
663static void 880static void
@@ -718,6 +935,14 @@ raise_exception(int e)
718} while (0) 935} while (0)
719#endif 936#endif
720 937
938#if ENABLE_PLATFORM_MINGW32
939static void
940write_ctrl_c(void)
941{
942 console_write("^C", 2);
943}
944#endif
945
721/* 946/*
722 * Called when a SIGINT is received. (If the user specifies 947 * Called when a SIGINT is received. (If the user specifies
723 * that SIGINT is to be trapped or ignored using the trap builtin, then 948 * that SIGINT is to be trapped or ignored using the trap builtin, then
@@ -730,16 +955,28 @@ static void
730raise_interrupt(void) 955raise_interrupt(void)
731{ 956{
732 pending_int = 0; 957 pending_int = 0;
958#if !ENABLE_PLATFORM_MINGW32
733 /* Signal is not automatically unmasked after it is raised, 959 /* Signal is not automatically unmasked after it is raised,
734 * do it ourself - unmask all signals */ 960 * do it ourself - unmask all signals */
735 sigprocmask_allsigs(SIG_UNBLOCK); 961 sigprocmask_allsigs(SIG_UNBLOCK);
962#endif
736 /* pending_sig = 0; - now done in signal_handler() */ 963 /* pending_sig = 0; - now done in signal_handler() */
737 964
738 if (!(rootshell && iflag)) { 965 if (!(rootshell && iflag)) {
966#if !ENABLE_PLATFORM_MINGW32
739 /* Kill ourself with SIGINT */ 967 /* Kill ourself with SIGINT */
740 signal(SIGINT, SIG_DFL); 968 signal(SIGINT, SIG_DFL);
741 raise(SIGINT); 969 raise(SIGINT);
970#else
971 fflush_all();
972 kill(-getpid(), SIGINT);
973 _exit(SIGINT << 24);
974#endif
742 } 975 }
976#if ENABLE_PLATFORM_MINGW32
977 if (iflag)
978 write_ctrl_c();
979#endif
743 /* bash: ^C even on empty command line sets $? */ 980 /* bash: ^C even on empty command line sets $? */
744 exitstatus = SIGINT + 128; 981 exitstatus = SIGINT + 128;
745 raise_exception(EXINT); 982 raise_exception(EXINT);
@@ -2094,6 +2331,18 @@ maybe_single_quote(const char *s)
2094 return single_quote(s); 2331 return single_quote(s);
2095} 2332}
2096 2333
2334#if ENABLE_PLATFORM_MINGW32
2335/* Copy path to a string on the stack long enough to allow a file extension
2336 * to be added. */
2337static char *
2338stack_add_ext_space(const char *path)
2339{
2340 char *p = growstackto(strlen(path) + 5);
2341 strcpy(p, path);
2342 return p;
2343}
2344#endif
2345
2097 2346
2098/* ============ nextopt */ 2347/* ============ nextopt */
2099 2348
@@ -2216,6 +2465,9 @@ struct localvar {
2216#else 2465#else
2217# define VDYNAMIC 0 2466# define VDYNAMIC 0
2218#endif 2467#endif
2468#if ENABLE_PLATFORM_MINGW32
2469# define VIMPORT 0x400 /* variable was imported from environment */
2470#endif
2219 2471
2220/* Need to be before varinit_data[] */ 2472/* Need to be before varinit_data[] */
2221#if ENABLE_LOCALE_SUPPORT 2473#if ENABLE_LOCALE_SUPPORT
@@ -2244,6 +2496,24 @@ static void change_seconds(const char *) FAST_FUNC;
2244static void change_realtime(const char *) FAST_FUNC; 2496static void change_realtime(const char *) FAST_FUNC;
2245#endif 2497#endif
2246 2498
2499#if ENABLE_PLATFORM_MINGW32
2500static void FAST_FUNC
2501change_terminal_mode(const char *newval UNUSED_PARAM)
2502{
2503 terminal_mode(TRUE);
2504}
2505
2506static void clearcmdentry(void);
2507static void FAST_FUNC
2508change_override_applets(const char *newval UNUSED_PARAM)
2509{
2510 clearcmdentry();
2511}
2512
2513# define LINENO_INDEX (5 + 2 * ENABLE_ASH_MAIL + ENABLE_ASH_GETOPTS)
2514# define FUNCNAME_INDEX (LINENO_INDEX + 1)
2515#endif
2516
2247static const struct { 2517static const struct {
2248 int flags; 2518 int flags;
2249 const char *var_text; 2519 const char *var_text;
@@ -2281,6 +2551,12 @@ static const struct {
2281#if ENABLE_FEATURE_EDITING_SAVEHISTORY 2551#if ENABLE_FEATURE_EDITING_SAVEHISTORY
2282 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL }, 2552 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
2283#endif 2553#endif
2554#if ENABLE_PLATFORM_MINGW32
2555 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_SKIP_ANSI_EMULATION, change_terminal_mode },
2556 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_TERMINAL_MODE, change_terminal_mode },
2557 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_OVERRIDE_APPLETS, change_override_applets },
2558 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_CRITICAL_ERROR_DIALOGS, change_critical_error_dialogs },
2559#endif
2284}; 2560};
2285 2561
2286struct redirtab; 2562struct redirtab;
@@ -2504,6 +2780,65 @@ bltinlookup(const char *name)
2504 return lookupvar(name); 2780 return lookupvar(name);
2505} 2781}
2506 2782
2783#if ENABLE_PLATFORM_MINGW32
2784static char *
2785fix_pathvar(const char *path, int len)
2786{
2787 char *newpath = xstrdup(path);
2788 char *p;
2789 int modified = FALSE;
2790
2791 p = newpath + len;
2792 while (*p) {
2793 if (*p != ':' && *p != ';') {
2794 /* skip drive */
2795 if (isalpha(*p) && p[1] == ':')
2796 p += 2;
2797 /* skip through path component */
2798 for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2799 continue;
2800 }
2801 /* *p is ':', ';' or '\0' here */
2802 if (*p == ':') {
2803 *p++ = ';';
2804 modified = TRUE;
2805 }
2806 else if (*p == ';') {
2807 ++p;
2808 }
2809 }
2810
2811 if (!modified) {
2812 free(newpath);
2813 newpath = NULL;
2814 }
2815 return newpath;
2816}
2817
2818#define BB_VAR_EXACT 1 /* exact match for name */
2819#define BB_VAR_ASSIGN -1 /* matches name followed by '=' */
2820
2821/* Match variables that should be placed in the environment immediately
2822 * they're exported and removed immediately they're no longer exported */
2823static int
2824is_bb_var(const char *s)
2825{
2826 const char *p;
2827 int len;
2828
2829 for (p = bbvar; *p; p += len + 1) {
2830 len = strlen(p);
2831 if (strncmp(s, p, len) == 0) {
2832 if (s[len] == '\0')
2833 return BB_VAR_EXACT;
2834 else if (s[len] == '=')
2835 return BB_VAR_ASSIGN;
2836 }
2837 }
2838 return FALSE;
2839}
2840#endif
2841
2507/* 2842/*
2508 * Same as setvar except that the variable and value are passed in 2843 * Same as setvar except that the variable and value are passed in
2509 * the first argument as name=value. Since the first argument will 2844 * the first argument as name=value. Since the first argument will
@@ -2515,6 +2850,26 @@ static struct var *
2515setvareq(char *s, int flags) 2850setvareq(char *s, int flags)
2516{ 2851{
2517 struct var *vp, **vpp; 2852 struct var *vp, **vpp;
2853#if ENABLE_PLATFORM_MINGW32
2854 const char *paths = "PATH=\0""CDPATH=\0""MANPATH=\0";
2855 const char *p;
2856 int len;
2857
2858 for (p = paths; *p; p += len + 1) {
2859 len = strlen(p);
2860 if (strncmp(s, p, len) == 0) {
2861 char *newpath = fix_pathvar(s, len);
2862 if (newpath) {
2863 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2864 free(s);
2865 flags |= VNOSAVE;
2866 flags &= ~(VTEXTFIXED|VSTACK);
2867 s = newpath;
2868 }
2869 break;
2870 }
2871 }
2872#endif
2518 2873
2519 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2874 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2520 vpp = findvar(s); 2875 vpp = findvar(s);
@@ -2539,6 +2894,11 @@ setvareq(char *s, int flags)
2539 if (!(vp->flags & (VTEXTFIXED|VSTACK))) 2894 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2540 free((char*)vp->var_text); 2895 free((char*)vp->var_text);
2541 2896
2897#if ENABLE_PLATFORM_MINGW32
2898 if ((flags & VUNSET) && (vp->flags & VEXPORT) &&
2899 is_bb_var(s) == BB_VAR_EXACT)
2900 unsetenv(s);
2901#endif
2542 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) { 2902 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2543 *vpp = vp->next; 2903 *vpp = vp->next;
2544 free(vp); 2904 free(vp);
@@ -2568,6 +2928,10 @@ setvareq(char *s, int flags)
2568 s = ckstrdup(s); 2928 s = ckstrdup(s);
2569 vp->var_text = s; 2929 vp->var_text = s;
2570 vp->flags = flags; 2930 vp->flags = flags;
2931#if ENABLE_PLATFORM_MINGW32
2932 if ((flags & VEXPORT) && is_bb_var(s) == BB_VAR_ASSIGN)
2933 putenv(s);
2934#endif
2571 2935
2572 out: 2936 out:
2573 return vp; 2937 return vp;
@@ -2689,6 +3053,65 @@ listvars(int on, int off, struct strlist *lp, char ***end)
2689 return grabstackstr(ep); 3053 return grabstackstr(ep);
2690} 3054}
2691 3055
3056#if ENABLE_PLATFORM_MINGW32
3057/* Adjust directory separator in variables imported from the environment */
3058static void
3059setwinxp(int on)
3060{
3061 static smallint is_winxp = 1;
3062 struct var **vpp;
3063 struct var *vp;
3064
3065 if (on == is_winxp)
3066 return;
3067 is_winxp = on;
3068
3069 for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
3070 for (vp = *vpp; vp; vp = vp->next) {
3071 if ((vp->flags & VIMPORT)) {
3072 char *end = strchr(vp->var_text, '=');
3073 if (!end || is_prefixed_with(vp->var_text, "COMSPEC=") ||
3074 is_prefixed_with(vp->var_text, "SYSTEMROOT="))
3075 continue;
3076 if (!on)
3077 bs_to_slash(end + 1);
3078 else
3079 slash_to_bs(end + 1);
3080 }
3081 }
3082 }
3083}
3084
3085# if ENABLE_ASH_NOCONSOLE
3086/*
3087 * Console state is either:
3088 * 0 normal
3089 * 1 iconified/hidden
3090 * 2 unknown
3091 */
3092static int console_state(void)
3093{
3094 DECLARE_PROC_ADDR(BOOL, ShowWindow, HWND, int);
3095
3096 if (INIT_PROC_ADDR(user32.dll, ShowWindow)) {
3097 BOOL visible = IsWindowVisible(GetConsoleWindow());
3098 BOOL iconified = IsIconic(GetConsoleWindow());
3099
3100 return !visible || iconified;
3101 }
3102 return 2;
3103}
3104
3105static void hide_console(int hide)
3106{
3107 // Switch console state if it's known and isn't the required state
3108 if (console_state() == !hide)
3109 ShowWindow(GetConsoleWindow(), hide ?
3110 (noiconify ? SW_HIDE : SW_MINIMIZE) : SW_NORMAL);
3111}
3112# endif
3113#endif
3114
2692 3115
2693/* ============ Path search helper */ 3116/* ============ Path search helper */
2694static const char * 3117static const char *
@@ -2732,7 +3155,7 @@ static const char *pathopt; /* set by padvance */
2732static int 3155static int
2733padvance_magic(const char **path, const char *name, int magic) 3156padvance_magic(const char **path, const char *name, int magic)
2734{ 3157{
2735 const char *term = "%:"; 3158 const char *term = "%"PATH_SEP_STR;
2736 const char *lpathopt; 3159 const char *lpathopt;
2737 const char *p; 3160 const char *p;
2738 char *q; 3161 char *q;
@@ -2749,14 +3172,14 @@ padvance_magic(const char **path, const char *name, int magic)
2749 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) { 3172 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2750 lpathopt = start + 1; 3173 lpathopt = start + 1;
2751 start = p; 3174 start = p;
2752 term = ":"; 3175 term = PATH_SEP_STR;
2753 } 3176 }
2754 3177
2755 len = strcspn(start, term); 3178 len = strcspn(start, term);
2756 p = start + len; 3179 p = start + len;
2757 3180
2758 if (*p == '%') { 3181 if (*p == '%') {
2759 size_t extra = strchrnul(p, ':') - p; 3182 size_t extra = strchrnul(p, PATH_SEP) - p;
2760 3183
2761 if (legal_pathopt(p + 1, term, magic)) 3184 if (legal_pathopt(p + 1, term, magic))
2762 lpathopt = p + 1; 3185 lpathopt = p + 1;
@@ -2767,14 +3190,18 @@ padvance_magic(const char **path, const char *name, int magic)
2767 } 3190 }
2768 3191
2769 pathopt = lpathopt; 3192 pathopt = lpathopt;
2770 *path = *p == ':' ? p + 1 : NULL; 3193 *path = *p == PATH_SEP ? p + 1 : NULL;
2771 3194
2772 /* "2" is for '/' and '\0' */ 3195 /* "2" is for '/' and '\0' */
2773 qlen = len + strlen(name) + 2; 3196 /* reserve space for suffix on WIN32 */
3197 qlen = len + strlen(name) + 2 IF_PLATFORM_MINGW32(+ 4);
2774 q = growstackto(qlen); 3198 q = growstackto(qlen);
2775 3199
2776 if (len) { 3200 if (len) {
2777 q = mempcpy(q, start, len); 3201 q = mempcpy(q, start, len);
3202#if ENABLE_PLATFORM_MINGW32
3203 if (q[-1] != '/' && q[-1] != '\\')
3204#endif
2778 *q++ = '/'; 3205 *q++ = '/';
2779 } 3206 }
2780 strcpy(q, name); 3207 strcpy(q, name);
@@ -2865,6 +3292,7 @@ setprompt_if(smallint do_set, int whichprompt)
2865 3292
2866#define CD_PHYSICAL 1 3293#define CD_PHYSICAL 1
2867#define CD_PRINT 2 3294#define CD_PRINT 2
3295#define CD_PRINT_ALL 4
2868 3296
2869static int 3297static int
2870cdopt(void) 3298cdopt(void)
@@ -2873,7 +3301,14 @@ cdopt(void)
2873 int i, j; 3301 int i, j;
2874 3302
2875 j = 'L'; 3303 j = 'L';
3304#if ENABLE_PLATFORM_MINGW32
3305 while ((i = nextopt("LPa")) != '\0') {
3306 if (i == 'a')
3307 flags |= CD_PRINT_ALL;
3308 else
3309#else
2876 while ((i = nextopt("LP")) != '\0') { 3310 while ((i = nextopt("LP")) != '\0') {
3311#endif
2877 if (i != j) { 3312 if (i != j) {
2878 flags ^= CD_PHYSICAL; 3313 flags ^= CD_PHYSICAL;
2879 j = i; 3314 j = i;
@@ -2890,6 +3325,130 @@ cdopt(void)
2890static const char * 3325static const char *
2891updatepwd(const char *dir) 3326updatepwd(const char *dir)
2892{ 3327{
3328#if ENABLE_PLATFORM_MINGW32
3329 /*
3330 * Due to Windows drive notion, getting pwd is a completely
3331 * different thing. Handle it in a separate routine
3332 */
3333
3334 char *new;
3335 char *p;
3336 char *cdcomppath;
3337 const char *lim;
3338 int len;
3339 char buffer[PATH_MAX];
3340 /*
3341 * There are five cases that make some kind of sense
3342 *
3343 * Absolute paths:
3344 * c:/path
3345 * //host/share
3346 *
3347 * Relative to current working directory of other drive:
3348 * c:path
3349 *
3350 * Relative to current root (drive/share):
3351 * /path
3352 *
3353 * Relative to current working directory of current root (drive/share):
3354 * path
3355 */
3356 enum {ABS_DRIVE, ABS_SHARE, REL_OTHER, REL_ROOT, REL_CWD} target;
3357
3358 /* skip multiple leading separators unless dir is a UNC path */
3359 if (is_dir_sep(*dir) && unc_root_len(dir) == 0) {
3360 while (is_dir_sep(dir[1]))
3361 ++dir;
3362 }
3363
3364 len = strlen(dir);
3365 if (len >= 2 && has_dos_drive_prefix(dir))
3366 target = len >= 3 && is_dir_sep(dir[2]) ? ABS_DRIVE : REL_OTHER;
3367 else if (unc_root_len(dir) != 0)
3368 target = ABS_SHARE;
3369 else if (is_dir_sep(*dir))
3370 target = REL_ROOT;
3371 else
3372 target = REL_CWD;
3373
3374 cdcomppath = sstrdup(dir);
3375 STARTSTACKSTR(new);
3376
3377 switch (target) {
3378 case REL_OTHER:
3379 /* c:path */
3380 if (get_drive_cwd(dir, buffer, PATH_MAX) == NULL)
3381 return 0;
3382 new = stack_putstr(buffer, new);
3383 len = 2;
3384 cdcomppath += len;
3385 dir += len;
3386 break;
3387 case REL_CWD:
3388 case REL_ROOT:
3389 /* path or /path */
3390 len = root_len(curdir);
3391 if (len == 0)
3392 return 0;
3393 new = target == REL_CWD ? stack_putstr(curdir, new) :
3394 stnputs(curdir, len, new);
3395 break;
3396 default:
3397 /* //host/share or c:/path */
3398 len = root_len(dir);
3399 if (len == 0)
3400 return 0;
3401 new = stnputs(dir, len, new);
3402 cdcomppath += len;
3403 dir += len;
3404 break;
3405 }
3406
3407 new = makestrspace(strlen(dir) + 2, new);
3408 lim = (char *)stackblock() + len + 1;
3409
3410 if (!is_dir_sep(*dir)) {
3411 if (!is_dir_sep(new[-1]))
3412 USTPUTC('/', new);
3413 if (new > lim && is_dir_sep(*lim))
3414 lim++;
3415 } else {
3416 USTPUTC('/', new);
3417 cdcomppath++;
3418 if (is_dir_sep(dir[1]) && !is_dir_sep(dir[2])) {
3419 USTPUTC('/', new);
3420 cdcomppath++;
3421 lim++;
3422 }
3423 }
3424 p = strtok(cdcomppath, "/\\");
3425 while (p) {
3426 switch (*p) {
3427 case '.':
3428 if (p[1] == '.' && p[2] == '\0') {
3429 while (new > lim) {
3430 STUNPUTC(new);
3431 if (is_dir_sep(new[-1]))
3432 break;
3433 }
3434 break;
3435 }
3436 if (p[1] == '\0')
3437 break;
3438 /* fall through */
3439 default:
3440 new = stack_putstr(p, new);
3441 USTPUTC('/', new);
3442 }
3443 p = strtok(NULL, "/\\");
3444 }
3445 if (new > lim)
3446 STUNPUTC(new);
3447 *new = 0;
3448 strip_dot_space((char *)stackblock());
3449 fix_path_case((char *)stackblock());
3450 return bs_to_slash((char *)stackblock());
3451#else
2893 char *new; 3452 char *new;
2894 char *p; 3453 char *p;
2895 char *cdcomppath; 3454 char *cdcomppath;
@@ -2943,6 +3502,7 @@ updatepwd(const char *dir)
2943 STUNPUTC(new); 3502 STUNPUTC(new);
2944 *new = 0; 3503 *new = 0;
2945 return stackblock(); 3504 return stackblock();
3505#endif
2946} 3506}
2947 3507
2948/* 3508/*
@@ -3038,7 +3598,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3038 } 3598 }
3039 if (!dest) 3599 if (!dest)
3040 dest = nullstr; 3600 dest = nullstr;
3041 if (*dest == '/') 3601 if (!is_relative_path(dest))
3042 goto step6; 3602 goto step6;
3043 if (*dest == '.') { 3603 if (*dest == '.') {
3044 c = dest[1]; 3604 c = dest[1];
@@ -3061,7 +3621,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3061 p = stalloc(len); 3621 p = stalloc(len);
3062 3622
3063 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 3623 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
3064 if (c && c != ':') 3624 if (c && c != PATH_SEP)
3065 flags |= CD_PRINT; 3625 flags |= CD_PRINT;
3066 docd: 3626 docd:
3067 if (!docd(p, flags)) 3627 if (!docd(p, flags))
@@ -3083,6 +3643,26 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3083 return 0; 3643 return 0;
3084} 3644}
3085 3645
3646#if ENABLE_PLATFORM_MINGW32
3647static void
3648print_all_cwd(void)
3649{
3650 FILE *mnt;
3651 struct mntent *entry;
3652 char buffer[PATH_MAX];
3653
3654 mnt = setmntent(bb_path_mtab_file, "r");
3655 if (mnt) {
3656 while ((entry=getmntent(mnt)) != NULL) {
3657 entry->mnt_dir[2] = '\0';
3658 if (get_drive_cwd(entry->mnt_dir, buffer, PATH_MAX) != NULL)
3659 out1fmt("%s\n", buffer);
3660 }
3661 endmntent(mnt);
3662 }
3663}
3664#endif
3665
3086static int FAST_FUNC 3666static int FAST_FUNC
3087pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 3667pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3088{ 3668{
@@ -3090,6 +3670,12 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3090 const char *dir = curdir; 3670 const char *dir = curdir;
3091 3671
3092 flags = cdopt(); 3672 flags = cdopt();
3673#if ENABLE_PLATFORM_MINGW32
3674 if (flags & CD_PRINT_ALL) {
3675 print_all_cwd();
3676 return 0;
3677 }
3678#endif
3093 if (flags) { 3679 if (flags) {
3094 if (physdir == nullstr) 3680 if (physdir == nullstr)
3095 setpwd(dir, 0); 3681 setpwd(dir, 0);
@@ -3714,7 +4300,12 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3714struct procstat { 4300struct procstat {
3715 pid_t ps_pid; /* process id */ 4301 pid_t ps_pid; /* process id */
3716 int ps_status; /* last process status from wait() */ 4302 int ps_status; /* last process status from wait() */
4303#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3717 char *ps_cmd; /* text of command being run */ 4304 char *ps_cmd; /* text of command being run */
4305#endif
4306#if ENABLE_PLATFORM_MINGW32
4307 HANDLE ps_proc;
4308#endif
3718}; 4309};
3719 4310
3720struct job { 4311struct job {
@@ -3730,8 +4321,10 @@ struct job {
3730#define JOBDONE 2 /* all procs are completed */ 4321#define JOBDONE 2 /* all procs are completed */
3731 unsigned 4322 unsigned
3732 state: 8, 4323 state: 8,
3733#if JOBS 4324#if JOBS || ENABLE_PLATFORM_MINGW32
3734 sigint: 1, /* job was killed by SIGINT */ 4325 sigint: 1, /* job was killed by SIGINT */
4326#endif
4327#if JOBS
3735 jobctl: 1, /* job running under job control */ 4328 jobctl: 1, /* job running under job control */
3736#endif 4329#endif
3737 waited: 1, /* true if this entry has been waited for */ 4330 waited: 1, /* true if this entry has been waited for */
@@ -3740,17 +4333,23 @@ struct job {
3740 struct job *prev_job; /* previous job */ 4333 struct job *prev_job; /* previous job */
3741}; 4334};
3742 4335
4336#if !ENABLE_PLATFORM_MINGW32
3743static int forkshell(struct job *, union node *, int); 4337static int forkshell(struct job *, union node *, int);
4338#endif
3744static int waitforjob(struct job *); 4339static int waitforjob(struct job *);
3745 4340
3746#if !JOBS 4341#if !JOBS && !JOBS_WIN32
3747enum { jobctl = 0 }; 4342enum { jobctl = 0 };
3748#define setjobctl(on) do {} while (0) 4343#define setjobctl(on) do {} while (0)
3749#else 4344#elif JOBS_WIN32
4345static smallint jobctl; //references:8
4346#define setjobctl(on) do { if (rootshell) jobctl = on; } while (0)
4347#else /* JOBS */
3750static smallint jobctl; //references:8 4348static smallint jobctl; //references:8
3751static void setjobctl(int); 4349static void setjobctl(int);
3752#endif 4350#endif
3753 4351
4352#if !ENABLE_PLATFORM_MINGW32
3754/* 4353/*
3755 * Ignore a signal. 4354 * Ignore a signal.
3756 */ 4355 */
@@ -3910,6 +4509,10 @@ setsignal(int signo)
3910 4509
3911 sigaction_set(signo, &act); 4510 sigaction_set(signo, &act);
3912} 4511}
4512#else
4513#define setsignal(s)
4514#define ignoresig(s)
4515#endif
3913 4516
3914/* mode flags for set_curjob */ 4517/* mode flags for set_curjob */
3915#define CUR_DELETE 2 4518#define CUR_DELETE 2
@@ -4043,7 +4646,7 @@ set_curjob(struct job *jp, unsigned mode)
4043 } 4646 }
4044} 4647}
4045 4648
4046#if JOBS || DEBUG 4649#if JOBS || ENABLE_PLATFORM_MINGW32 || DEBUG
4047static int 4650static int
4048jobno(const struct job *jp) 4651jobno(const struct job *jp)
4049{ 4652{
@@ -4061,7 +4664,9 @@ static struct job *
4061getjob(const char *name, int getctl) 4664getjob(const char *name, int getctl)
4062{ 4665{
4063 struct job *jp; 4666 struct job *jp;
4667#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4064 struct job *found; 4668 struct job *found;
4669#endif
4065 const char *err_msg = "%s: no such job"; 4670 const char *err_msg = "%s: no such job";
4066 unsigned num; 4671 unsigned num;
4067 int c; 4672 int c;
@@ -4106,6 +4711,7 @@ getjob(const char *name, int getctl)
4106 } 4711 }
4107 } 4712 }
4108 4713
4714#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4109 found = NULL; 4715 found = NULL;
4110 while (jp) { 4716 while (jp) {
4111 if (*p == '?' 4717 if (*p == '?'
@@ -4122,6 +4728,9 @@ getjob(const char *name, int getctl)
4122 if (!found) 4728 if (!found)
4123 goto err; 4729 goto err;
4124 jp = found; 4730 jp = found;
4731#else
4732 goto err;
4733#endif
4125 4734
4126 gotit: 4735 gotit:
4127#if JOBS 4736#if JOBS
@@ -4140,14 +4749,18 @@ getjob(const char *name, int getctl)
4140static void 4749static void
4141freejob(struct job *jp) 4750freejob(struct job *jp)
4142{ 4751{
4752#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4143 struct procstat *ps; 4753 struct procstat *ps;
4144 int i; 4754 int i;
4755#endif
4145 4756
4146 INTOFF; 4757 INTOFF;
4758#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4147 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { 4759 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
4148 if (ps->ps_cmd != nullstr) 4760 if (ps->ps_cmd != nullstr)
4149 free(ps->ps_cmd); 4761 free(ps->ps_cmd);
4150 } 4762 }
4763#endif
4151 if (jp->ps != &jp->ps0) 4764 if (jp->ps != &jp->ps0)
4152 free(jp->ps); 4765 free(jp->ps);
4153 jp->used = 0; 4766 jp->used = 0;
@@ -4255,7 +4868,9 @@ setjobctl(int on)
4255 ttyfd = fd; 4868 ttyfd = fd;
4256 jobctl = on; 4869 jobctl = on;
4257} 4870}
4871#endif
4258 4872
4873#if JOBS || JOBS_WIN32
4259static int FAST_FUNC 4874static int FAST_FUNC
4260killcmd(int argc, char **argv) 4875killcmd(int argc, char **argv)
4261{ 4876{
@@ -4285,8 +4900,10 @@ killcmd(int argc, char **argv)
4285 * sh -c 'true|sleep 1 & sleep 2; kill %1' 4900 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4286 */ 4901 */
4287 n = jp->nprocs; /* can't be 0 (I hope) */ 4902 n = jp->nprocs; /* can't be 0 (I hope) */
4903#if !ENABLE_PLATFORM_MINGW32
4288 if (jp->jobctl) 4904 if (jp->jobctl)
4289 n = 1; 4905 n = 1;
4906#endif
4290 dst = alloca(n * sizeof(int)*4); 4907 dst = alloca(n * sizeof(int)*4);
4291 argv[i] = dst; 4908 argv[i] = dst;
4292 for (j = 0; j < n; j++) { 4909 for (j = 0; j < n; j++) {
@@ -4301,7 +4918,11 @@ killcmd(int argc, char **argv)
4301 * leading space. Needed to not confuse 4918 * leading space. Needed to not confuse
4302 * negative pids with "kill -SIGNAL_NO" syntax 4919 * negative pids with "kill -SIGNAL_NO" syntax
4303 */ 4920 */
4921#if !ENABLE_PLATFORM_MINGW32
4304 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); 4922 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4923#else
4924 dst += sprintf(dst, " -%u", (int)ps->ps_pid);
4925#endif
4305 } 4926 }
4306 *dst = '\0'; 4927 *dst = '\0';
4307 } 4928 }
@@ -4309,7 +4930,9 @@ killcmd(int argc, char **argv)
4309 } 4930 }
4310 return kill_main(argc, argv); 4931 return kill_main(argc, argv);
4311} 4932}
4933#endif
4312 4934
4935#if JOBS
4313static void 4936static void
4314showpipe(struct job *jp /*, FILE *out*/) 4937showpipe(struct job *jp /*, FILE *out*/)
4315{ 4938{
@@ -4414,6 +5037,69 @@ sprint_status48(char *os, int status, int sigonly)
4414 return s - os; 5037 return s - os;
4415} 5038}
4416 5039
5040#if ENABLE_PLATFORM_MINGW32
5041static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
5042{
5043 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
5044# if ENABLE_FEATURE_EDITING
5045 bb_got_signal = SIGINT; /* for read_line_input: "we got a signal" */
5046# endif
5047 waitcmd_int = -waitcmd_int;
5048 if (!trap[SIGINT]) {
5049 if (!suppress_int && !(rootshell && iflag))
5050 raise_interrupt();
5051 pending_int = 1;
5052 }
5053 return TRUE;
5054 }
5055 return FALSE;
5056}
5057
5058/*
5059 * Windows does not know about parent-child relationship
5060 * They don't support waitpid(-1)
5061 */
5062static pid_t
5063waitpid_child(int *status, DWORD blocking)
5064{
5065 struct job *jb;
5066 int pid_nr = 0;
5067 static HANDLE *proclist = NULL;
5068 static int pid_max = 0;
5069 pid_t pid = -1;
5070 DWORD win_status, idx;
5071 int i;
5072
5073 for (jb = curjob; jb; jb = jb->prev_job) {
5074 if (jb->state != JOBDONE) {
5075 if (pid_nr + jb->nprocs > pid_max) {
5076 pid_max = pid_nr + jb->nprocs;
5077 proclist = ckrealloc(proclist, sizeof(*proclist) * pid_max);
5078 }
5079
5080 for (i = 0; i < jb->nprocs; ++i) {
5081 if (jb->ps[i].ps_proc) {
5082 proclist[pid_nr++] = jb->ps[i].ps_proc;
5083 }
5084 }
5085 }
5086 }
5087
5088 if (pid_nr) {
5089 do {
5090 idx = WaitForMultipleObjects(pid_nr, proclist, FALSE, blocking);
5091 if (idx < pid_nr) {
5092 GetExitCodeProcess(proclist[idx], &win_status);
5093 *status = exit_code_to_wait_status(win_status);
5094 pid = GetProcessId(proclist[idx]);
5095 break;
5096 }
5097 } while (blocking && !pending_int && waitcmd_int != 1);
5098 }
5099 return pid;
5100}
5101#endif
5102
4417/* Inside dowait(): */ 5103/* Inside dowait(): */
4418#define DOWAIT_NONBLOCK 0 /* waitpid() will use WNOHANG and won't wait for signals */ 5104#define DOWAIT_NONBLOCK 0 /* waitpid() will use WNOHANG and won't wait for signals */
4419#define DOWAIT_BLOCK 1 /* waitpid() will NOT use WNOHANG */ 5105#define DOWAIT_BLOCK 1 /* waitpid() will NOT use WNOHANG */
@@ -4425,6 +5111,7 @@ sprint_status48(char *os, int status, int sigonly)
4425static int 5111static int
4426waitproc(int block, int *status) 5112waitproc(int block, int *status)
4427{ 5113{
5114#if !ENABLE_PLATFORM_MINGW32
4428 sigset_t oldmask; 5115 sigset_t oldmask;
4429 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; 5116 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4430 int err; 5117 int err;
@@ -4464,6 +5151,11 @@ waitproc(int block, int *status)
4464 /* If we fall off the loop, err is 0, which means we got a !SIGCHLD signal */ 5151 /* If we fall off the loop, err is 0, which means we got a !SIGCHLD signal */
4465 5152
4466 return err; 5153 return err;
5154#else
5155 // Only DOWAIT_NONBLOCK is non-blocking, other values block.
5156 *status = 0;
5157 return waitpid_child(status, block != DOWAIT_NONBLOCK);
5158#endif
4467} 5159}
4468 5160
4469static int waitone(int block, struct job *job) 5161static int waitone(int block, struct job *job)
@@ -4517,6 +5209,10 @@ static int waitone(int block, struct job *job)
4517 TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->ps_status, status)); 5209 TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->ps_status, status));
4518 sp->ps_status = status; 5210 sp->ps_status = status;
4519 thisjob = jp; 5211 thisjob = jp;
5212#if ENABLE_PLATFORM_MINGW32
5213 CloseHandle(sp->ps_proc);
5214 sp->ps_proc = NULL;
5215#endif
4520 } 5216 }
4521 if (sp->ps_status == -1) 5217 if (sp->ps_status == -1)
4522 jobstate = JOBRUNNING; 5218 jobstate = JOBRUNNING;
@@ -4577,6 +5273,7 @@ static int waitone(int block, struct job *job)
4577 5273
4578static int dowait(int block, struct job *jp) 5274static int dowait(int block, struct job *jp)
4579{ 5275{
5276#if !ENABLE_PLATFORM_MINGW32
4580 smallint gotchld = *(volatile smallint *)&gotsigchld; 5277 smallint gotchld = *(volatile smallint *)&gotsigchld;
4581 int rpid; 5278 int rpid;
4582 int pid; 5279 int pid;
@@ -4598,9 +5295,17 @@ static int dowait(int block, struct job *jp)
4598 } while (pid >= 0); 5295 } while (pid >= 0);
4599 5296
4600 return rpid; 5297 return rpid;
5298#else
5299 int pid = 1;
5300
5301 while ((jp ? jp->state == JOBRUNNING : pid > 0) && waitcmd_int != 1)
5302 pid = waitone(block, jp);
5303
5304 return pid;
5305#endif
4601} 5306}
4602 5307
4603#if JOBS 5308#if JOBS || JOBS_WIN32
4604static void 5309static void
4605showjob(struct job *jp, int mode) 5310showjob(struct job *jp, int mode)
4606{ 5311{
@@ -4615,7 +5320,7 @@ showjob(struct job *jp, int mode)
4615 5320
4616 if (mode & SHOW_ONLY_PGID) { /* jobs -p */ 5321 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
4617 /* just output process (group) id of pipeline */ 5322 /* just output process (group) id of pipeline */
4618 fprintf(out, "%d\n", ps->ps_pid); 5323 fprintf(out, "%"PID_FMT"d\n", ps->ps_pid);
4619 return; 5324 return;
4620 } 5325 }
4621 5326
@@ -4628,7 +5333,7 @@ showjob(struct job *jp, int mode)
4628 s[col - 3] = '-'; 5333 s[col - 3] = '-';
4629 5334
4630 if (mode & SHOW_PIDS) 5335 if (mode & SHOW_PIDS)
4631 col += fmtstr(s + col, 16, "%d ", ps->ps_pid); 5336 col += fmtstr(s + col, 16, "%"PID_FMT"d ", ps->ps_pid);
4632 5337
4633 psend = ps + jp->nprocs; 5338 psend = ps + jp->nprocs;
4634 5339
@@ -4637,8 +5342,10 @@ showjob(struct job *jp, int mode)
4637 col += sizeof("Running") - 1; 5342 col += sizeof("Running") - 1;
4638 } else { 5343 } else {
4639 int status = psend[-1].ps_status; 5344 int status = psend[-1].ps_status;
5345#if !ENABLE_PLATFORM_MINGW32
4640 if (jp->state == JOBSTOPPED) 5346 if (jp->state == JOBSTOPPED)
4641 status = jp->stopstatus; 5347 status = jp->stopstatus;
5348#endif
4642 col += sprint_status48(s + col, status, 0); 5349 col += sprint_status48(s + col, status, 0);
4643 } 5350 }
4644 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ 5351 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
@@ -4657,14 +5364,18 @@ showjob(struct job *jp, int mode)
4657 s[0] = '\0'; 5364 s[0] = '\0';
4658 col = 33; 5365 col = 33;
4659 if (mode & SHOW_PIDS) 5366 if (mode & SHOW_PIDS)
4660 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; 5367 col = fmtstr(s, 48, "\n%*c%"PID_FMT"d ", indent_col, ' ', ps->ps_pid) - 1;
4661 start: 5368 start:
5369#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4662 fprintf(out, "%s%*c%s%s", 5370 fprintf(out, "%s%*c%s%s",
4663 s, 5371 s,
4664 33 - col >= 0 ? 33 - col : 0, ' ', 5372 33 - col >= 0 ? 33 - col : 0, ' ',
4665 ps == jp->ps ? "" : "| ", 5373 ps == jp->ps ? "" : "| ",
4666 ps->ps_cmd 5374 ps->ps_cmd
4667 ); 5375 );
5376#else
5377 fprintf(out, "%s", s);
5378#endif
4668 } while (++ps != psend); 5379 } while (++ps != psend);
4669 newline_and_flush(out); 5380 newline_and_flush(out);
4670 5381
@@ -4749,7 +5460,7 @@ getstatus(struct job *job)
4749 { 5460 {
4750 /* XXX: limits number of signals */ 5461 /* XXX: limits number of signals */
4751 retval = WTERMSIG(status); 5462 retval = WTERMSIG(status);
4752#if JOBS 5463#if JOBS || ENABLE_PLATFORM_MINGW32
4753 if (retval == SIGINT) 5464 if (retval == SIGINT)
4754 job->sigint = 1; 5465 job->sigint = 1;
4755#endif 5466#endif
@@ -4800,6 +5511,9 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
4800 * with an exit status greater than 128, immediately after which 5511 * with an exit status greater than 128, immediately after which
4801 * the trap is executed." 5512 * the trap is executed."
4802 */ 5513 */
5514#if ENABLE_PLATFORM_MINGW32
5515 waitcmd_int = -1;
5516#endif
4803#if BASH_WAIT_N 5517#if BASH_WAIT_N
4804 status = dowait(DOWAIT_CHILD_OR_SIG | DOWAIT_JOBSTATUS, NULL); 5518 status = dowait(DOWAIT_CHILD_OR_SIG | DOWAIT_JOBSTATUS, NULL);
4805#else 5519#else
@@ -4809,8 +5523,16 @@ waitcmd(int argc UNUSED_PARAM, char **argv)
4809 * dowait() returns pid > 0. Check this case, 5523 * dowait() returns pid > 0. Check this case,
4810 * not "if (dowait() < 0)"! 5524 * not "if (dowait() < 0)"!
4811 */ 5525 */
5526#if ENABLE_PLATFORM_MINGW32
5527 if (waitcmd_int == 1) {
5528 write_ctrl_c();
5529 return 128 | SIGINT;
5530 }
5531 waitcmd_int = 0;
5532#else
4812 if (pending_sig) 5533 if (pending_sig)
4813 goto sigout; 5534 goto sigout;
5535#endif
4814#if BASH_WAIT_N 5536#if BASH_WAIT_N
4815 if (one) { 5537 if (one) {
4816 /* wait -n waits for one _job_, not one _process_. 5538 /* wait -n waits for one _job_, not one _process_.
@@ -4921,7 +5643,7 @@ makejob(int nprocs)
4921 break; 5643 break;
4922 if (jp->state != JOBDONE || !jp->waited) 5644 if (jp->state != JOBDONE || !jp->waited)
4923 continue; 5645 continue;
4924#if JOBS 5646#if JOBS || JOBS_WIN32
4925 if (jobctl) 5647 if (jobctl)
4926 continue; 5648 continue;
4927#endif 5649#endif
@@ -4947,7 +5669,7 @@ makejob(int nprocs)
4947 return jp; 5669 return jp;
4948} 5670}
4949 5671
4950#if JOBS 5672#if JOBS || JOBS_WIN32
4951/* 5673/*
4952 * Return a string identifying a command (to be printed by the 5674 * Return a string identifying a command (to be printed by the
4953 * jobs command). 5675 * jobs command).
@@ -5287,6 +6009,7 @@ clear_traps(void)
5287 INTON; 6009 INTON;
5288} 6010}
5289 6011
6012#if !ENABLE_PLATFORM_MINGW32
5290/* Lives far away from here, needed for forkchild */ 6013/* Lives far away from here, needed for forkchild */
5291static void closescript(void); 6014static void closescript(void);
5292 6015
@@ -5425,14 +6148,22 @@ forkchild(struct job *jp, union node *n, int mode)
5425 for (jp = curjob; jp; jp = jp->prev_job) 6148 for (jp = curjob; jp; jp = jp->prev_job)
5426 freejob(jp); 6149 freejob(jp);
5427} 6150}
6151#endif
5428 6152
5429/* Called after fork(), in parent */ 6153/* Called after fork(), in parent */
5430#if !JOBS 6154#if !JOBS && !JOBS_WIN32
5431#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) 6155#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5432#endif 6156#endif
5433static void 6157static void
6158#if !ENABLE_PLATFORM_MINGW32
5434forkparent(struct job *jp, union node *n, int mode, pid_t pid) 6159forkparent(struct job *jp, union node *n, int mode, pid_t pid)
6160#else
6161forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
6162#endif
5435{ 6163{
6164#if ENABLE_PLATFORM_MINGW32
6165 pid_t pid = GetProcessId(proc);
6166#else
5436 if (pid < 0) { 6167 if (pid < 0) {
5437 TRACE(("Fork failed, errno=%d", errno)); 6168 TRACE(("Fork failed, errno=%d", errno));
5438 if (jp) 6169 if (jp)
@@ -5440,6 +6171,7 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5440 ash_msg_and_raise_perror("can't fork"); 6171 ash_msg_and_raise_perror("can't fork");
5441 /* NOTREACHED */ 6172 /* NOTREACHED */
5442 } 6173 }
6174#endif
5443 6175
5444 TRACE(("In parent shell: child = %d\n", pid)); 6176 TRACE(("In parent shell: child = %d\n", pid));
5445 if (!jp) /* jp is NULL when called by openhere() for heredoc support */ 6177 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
@@ -5459,19 +6191,29 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5459 if (mode == FORK_BG) { 6191 if (mode == FORK_BG) {
5460 backgndpid = pid; /* set $! */ 6192 backgndpid = pid; /* set $! */
5461 set_curjob(jp, CUR_RUNNING); 6193 set_curjob(jp, CUR_RUNNING);
6194#if ENABLE_PLATFORM_MINGW32
6195 if (iflag && jp && jp->nprocs == 0)
6196 fprintf(stderr, "[%d] %"PID_FMT"d\n", jobno(jp), pid);
6197#endif
5462 } 6198 }
5463 if (jp) { 6199 if (jp) {
5464 struct procstat *ps = &jp->ps[jp->nprocs++]; 6200 struct procstat *ps = &jp->ps[jp->nprocs++];
5465 ps->ps_pid = pid; 6201 ps->ps_pid = pid;
5466 ps->ps_status = -1; 6202 ps->ps_status = -1;
6203#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
5467 ps->ps_cmd = nullstr; 6204 ps->ps_cmd = nullstr;
5468#if JOBS 6205#endif
6206#if ENABLE_PLATFORM_MINGW32
6207 ps->ps_proc = proc;
6208#endif
6209#if JOBS || JOBS_WIN32
5469 if (jobctl && n) 6210 if (jobctl && n)
5470 ps->ps_cmd = commandtext(n); 6211 ps->ps_cmd = commandtext(n);
5471#endif 6212#endif
5472 } 6213 }
5473} 6214}
5474 6215
6216#if !ENABLE_PLATFORM_MINGW32
5475/* jp and n are NULL when called by openhere() for heredoc support */ 6217/* jp and n are NULL when called by openhere() for heredoc support */
5476static int 6218static int
5477forkshell(struct job *jp, union node *n, int mode) 6219forkshell(struct job *jp, union node *n, int mode)
@@ -5517,6 +6259,7 @@ vforkexec(union node *n, char **argv, const char *path, int idx)
5517 6259
5518 return jp; 6260 return jp;
5519} 6261}
6262#endif
5520 6263
5521/* 6264/*
5522 * Wait for job to finish. 6265 * Wait for job to finish.
@@ -5580,6 +6323,10 @@ waitforjob(struct job *jp)
5580 return exitstatus; 6323 return exitstatus;
5581 6324
5582 st = getstatus(jp); 6325 st = getstatus(jp);
6326#if ENABLE_PLATFORM_MINGW32
6327 if (!jp->sigint && iflag && rootshell)
6328 pending_int = 0;
6329#endif
5583#if JOBS 6330#if JOBS
5584 if (jp->jobctl) { 6331 if (jp->jobctl) {
5585 xtcsetpgrp(ttyfd, rootpid); 6332 xtcsetpgrp(ttyfd, rootpid);
@@ -5605,6 +6352,7 @@ waitforjob(struct job *jp)
5605/* 6352/*
5606 * return 1 if there are stopped jobs, otherwise 0 6353 * return 1 if there are stopped jobs, otherwise 0
5607 */ 6354 */
6355#if !ENABLE_PLATFORM_MINGW32
5608static int 6356static int
5609stoppedjobs(void) 6357stoppedjobs(void)
5610{ 6358{
@@ -5623,6 +6371,17 @@ stoppedjobs(void)
5623 out: 6371 out:
5624 return retval; 6372 return retval;
5625} 6373}
6374#else
6375static int
6376stoppedjobs(void)
6377{
6378 if (iflag && curjob) {
6379 out2str("You have background jobs.\n");
6380 return 1;
6381 }
6382 return 0;
6383}
6384#endif
5626 6385
5627/* 6386/*
5628 * Code for dealing with input/output redirection. 6387 * Code for dealing with input/output redirection.
@@ -5641,11 +6400,21 @@ stoppedjobs(void)
5641static int 6400static int
5642write2pipe(int pip[2], const char *p, size_t len) 6401write2pipe(int pip[2], const char *p, size_t len)
5643{ 6402{
6403 IF_PLATFORM_MINGW32(struct forkshell fs);
6404
5644 if (len <= PIPE_BUF) { 6405 if (len <= PIPE_BUF) {
5645 xwrite(pip[1], p, len); 6406 xwrite(pip[1], p, len);
5646 goto out; 6407 goto out;
5647 } 6408 }
5648 6409
6410#if ENABLE_PLATFORM_MINGW32
6411 memset(&fs, 0, sizeof(fs));
6412 fs.fpid = FS_OPENHERE;
6413 fs.fd[0] = pip[0];
6414 fs.fd[1] = pip[1];
6415 fs.path = p;
6416 spawn_forkshell(&fs, NULL, NULL, FORK_NOJOB);
6417#else
5649 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { 6418 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
5650 /* child */ 6419 /* child */
5651 close(pip[0]); 6420 close(pip[0]);
@@ -5657,6 +6426,7 @@ write2pipe(int pip[2], const char *p, size_t len)
5657 xwrite(pip[1], p, len); 6426 xwrite(pip[1], p, len);
5658 _exit_SUCCESS(); 6427 _exit_SUCCESS();
5659 } 6428 }
6429#endif
5660 out: 6430 out:
5661 close(pip[1]); 6431 close(pip[1]);
5662 return pip[0]; 6432 return pip[0];
@@ -5711,6 +6481,10 @@ openredirect(union node *redir)
5711 flags = O_RDONLY; 6481 flags = O_RDONLY;
5712 do_open: 6482 do_open:
5713 f = sh_open(redir->nfile.expfname, flags, 0); 6483 f = sh_open(redir->nfile.expfname, flags, 0);
6484#if ENABLE_PLATFORM_MINGW32
6485 if (redir->nfile.type == NAPPEND)
6486 lseek(f, 0, SEEK_END);
6487#endif
5714 break; 6488 break;
5715 case NFROMSTR: 6489 case NFROMSTR:
5716 f = openherestr(redir->nfile.expfname); 6490 f = openherestr(redir->nfile.expfname);
@@ -5967,6 +6741,12 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
5967 if (fd == preverrout_fd) 6741 if (fd == preverrout_fd)
5968 preverrout_fd = new_fd; 6742 preverrout_fd = new_fd;
5969 6743
6744#if ENABLE_PLATFORM_MINGW32 && !defined(_UCRT)
6745 // Workaround for problems with stderr in MSVCRT
6746 if (fd == fileno(stderr))
6747 setvbuf(stderr, NULL, _IONBF, 0);
6748#endif
6749
5970 return 0; /* "we did not close fd" */ 6750 return 0; /* "we did not close fd" */
5971} 6751}
5972 6752
@@ -6320,6 +7100,9 @@ ifsbreakup(char *string, struct arglist *arglist)
6320 const char *ifs, *realifs; 7100 const char *ifs, *realifs;
6321 int ifsspc; 7101 int ifsspc;
6322 int nulonly; 7102 int nulonly;
7103#if ENABLE_PLATFORM_MINGW32
7104 int lshift = 0;
7105#endif
6323 7106
6324 start = string; 7107 start = string;
6325 if (ifslastp != NULL) { 7108 if (ifslastp != NULL) {
@@ -6330,7 +7113,33 @@ ifsbreakup(char *string, struct arglist *arglist)
6330 do { 7113 do {
6331 int afternul; 7114 int afternul;
6332 7115
7116#if ENABLE_PLATFORM_MINGW32
7117 /* Adjust region offsets for left-shifted string. */
7118 ifsp->begoff -= lshift;
7119 ifsp->endoff -= lshift;
7120#endif
6333 p = string + ifsp->begoff; 7121 p = string + ifsp->begoff;
7122#if ENABLE_PLATFORM_MINGW32
7123 if (ifsp->endoff > ifsp->begoff + 1) {
7124 /* Transform CRLF to LF. Skip regions having zero or
7125 * one characters: they can't contain CRLF. If the
7126 * region shrinks shift the rest of the string left. */
7127 int oldlen = ifsp->endoff - ifsp->begoff;
7128 int newlen = remove_cr(p, oldlen);
7129 int delta = oldlen - newlen;
7130
7131 if (delta > 0) {
7132 char *t = string + ifsp->endoff;
7133 char *s = string + ifsp->endoff - delta;
7134
7135 while (*t)
7136 *s++ = *t++;
7137 *s = '\0';
7138 lshift += delta;
7139 ifsp->endoff -= delta;
7140 }
7141 }
7142#endif
6334 afternul = nulonly; 7143 afternul = nulonly;
6335 nulonly = ifsp->nulonly; 7144 nulonly = ifsp->nulonly;
6336 ifs = nulonly ? nullstr : realifs; 7145 ifs = nulonly ? nullstr : realifs;
@@ -6775,6 +7584,7 @@ evalbackcmd(union node *n, struct backcmd *result
6775 const int ip = 0; 7584 const int ip = 0;
6776 const int ic = 1; 7585 const int ic = 1;
6777#endif 7586#endif
7587 IF_PLATFORM_MINGW32(struct forkshell fs);
6778 7588
6779 result->fd = -1; 7589 result->fd = -1;
6780 result->buf = NULL; 7590 result->buf = NULL;
@@ -6788,6 +7598,15 @@ evalbackcmd(union node *n, struct backcmd *result
6788 ash_msg_and_raise_perror("can't create pipe"); 7598 ash_msg_and_raise_perror("can't create pipe");
6789 /* process substitution uses NULL job, like openhere() */ 7599 /* process substitution uses NULL job, like openhere() */
6790 jp = (ctl == CTLBACKQ) ? makejob(1) : NULL; 7600 jp = (ctl == CTLBACKQ) ? makejob(1) : NULL;
7601#if ENABLE_PLATFORM_MINGW32
7602 memset(&fs, 0, sizeof(fs));
7603 fs.fpid = FS_EVALBACKCMD;
7604 fs.n = n;
7605 fs.fd[0] = pip[0];
7606 fs.fd[1] = pip[1];
7607 fs.fd[2] = ctl;
7608 spawn_forkshell(&fs, jp, n, FORK_NOJOB);
7609#else
6791 if (forkshell(jp, n, FORK_NOJOB) == 0) { 7610 if (forkshell(jp, n, FORK_NOJOB) == 0) {
6792 /* child */ 7611 /* child */
6793 reset_exception_handler(); 7612 reset_exception_handler();
@@ -6812,6 +7631,7 @@ evalbackcmd(union node *n, struct backcmd *result
6812 evaltreenr(n, EV_EXIT); 7631 evaltreenr(n, EV_EXIT);
6813 /* NOTREACHED */ 7632 /* NOTREACHED */
6814 } 7633 }
7634#endif
6815 /* parent */ 7635 /* parent */
6816#if BASH_PROCESS_SUBST 7636#if BASH_PROCESS_SUBST
6817 if (ctl != CTLBACKQ) { 7637 if (ctl != CTLBACKQ) {
@@ -6890,8 +7710,14 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
6890 7710
6891 /* Eat all trailing newlines */ 7711 /* Eat all trailing newlines */
6892 dest = expdest; 7712 dest = expdest;
6893 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) 7713 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) {
6894 STUNPUTC(dest); 7714 STUNPUTC(dest);
7715#if ENABLE_PLATFORM_MINGW32
7716 if (dest > ((char *)stackblock() + startloc) && dest[-1] == '\r') {
7717 STUNPUTC(dest);
7718 }
7719#endif
7720 }
6895 expdest = dest; 7721 expdest = dest;
6896 7722
6897 if (!(flag & EXP_QUOTED)) 7723 if (!(flag & EXP_QUOTED))
@@ -8038,6 +8864,26 @@ expandmeta(struct strlist *str /*, int flag*/)
8038#else 8864#else
8039/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */ 8865/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
8040 8866
8867#if ENABLE_ASH_GLOB_OPTIONS
8868static int FAST_FUNC
8869ash_accept_glob(const char *name)
8870{
8871 struct stat st;
8872
8873 if (nohiddenglob || nohidsysglob) {
8874 if (!lstat(name, &st)) {
8875 if ((st.st_attr & FILE_ATTRIBUTE_HIDDEN)) {
8876 if (nohiddenglob ||
8877 (st.st_attr & FILE_ATTRIBUTE_SYSTEM)) {
8878 return FALSE;
8879 }
8880 }
8881 }
8882 }
8883 return TRUE;
8884}
8885#endif
8886
8041/* 8887/*
8042 * Do metacharacter (i.e. *, ?, [...]) expansion. 8888 * Do metacharacter (i.e. *, ?, [...]) expansion.
8043 */ 8889 */
@@ -8065,6 +8911,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
8065 8911
8066 metaflag = 0; 8912 metaflag = 0;
8067 start = name; 8913 start = name;
8914#if ENABLE_PLATFORM_MINGW32
8915 if (expdir_len == 0 && has_dos_drive_prefix(start) && start[2] != '/')
8916 start += 2;
8917#endif
8068 for (p = name; esc = 0, *p; p += esc + 1) { 8918 for (p = name; esc = 0, *p; p += esc + 1) {
8069 if (*p == '*' || *p == '?') 8919 if (*p == '*' || *p == '?')
8070 metaflag = 1; 8920 metaflag = 1;
@@ -8139,8 +8989,16 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
8139 while (!pending_int && (dp = readdir(dirp)) != NULL) { 8989 while (!pending_int && (dp = readdir(dirp)) != NULL) {
8140 if (dp->d_name[0] == '.' && !matchdot) 8990 if (dp->d_name[0] == '.' && !matchdot)
8141 continue; 8991 continue;
8992#if ENABLE_ASH_GLOB_OPTIONS
8993# undef pmatch
8994# define pmatch(a, b) !fnmatch((a), (b), nocaseglob ? FNM_CASEFOLD : 0)
8995#endif
8142 if (pmatch(start, dp->d_name)) { 8996 if (pmatch(start, dp->d_name)) {
8143 if (atend) { 8997 if (atend) {
8998#if ENABLE_ASH_GLOB_OPTIONS
8999 if (!ash_accept_glob(dp->d_name))
9000 continue;
9001#endif
8144 strcpy(enddir, dp->d_name); 9002 strcpy(enddir, dp->d_name);
8145 addfname(expdir); 9003 addfname(expdir);
8146 } else { 9004 } else {
@@ -8168,6 +9026,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
8168 endname[-esc - 1] = esc ? '\\' : '/'; 9026 endname[-esc - 1] = esc ? '\\' : '/';
8169#undef expdir 9027#undef expdir
8170#undef expdir_max 9028#undef expdir_max
9029#if ENABLE_ASH_GLOB_OPTIONS
9030# undef pmatch
9031# define pmatch(a, b) !fnmatch((a), (b), 0)
9032#endif
8171} 9033}
8172 9034
8173static struct strlist * 9035static struct strlist *
@@ -8440,14 +9302,40 @@ static int builtinloc = -1; /* index in path of %builtin, or -1 */
8440 9302
8441 9303
8442static void 9304static void
9305#if ENABLE_PLATFORM_MINGW32
9306tryexec(IF_FEATURE_SH_STANDALONE(int applet_no, const char *path, int noexec,)
9307 const char *cmd, char **argv, char **envp)
9308#else
8443tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp) 9309tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp)
9310#endif
8444{ 9311{
9312#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
9313 interp_t interp;
9314#endif
8445#if ENABLE_FEATURE_SH_STANDALONE 9315#if ENABLE_FEATURE_SH_STANDALONE
8446 if (applet_no >= 0) { 9316 if (applet_no >= 0) {
9317# if ENABLE_PLATFORM_MINGW32
9318 /* Treat all applets as NOEXEC, including the shell itself
9319 * if we were called from forkshell_shellexec(). */
9320 run_noexec:
9321 if (applet_main[applet_no] != ash_main || noexec) {
9322 /* mingw-w64's getopt() uses __argv[0] as the program name */
9323 __argv[0] = (char *)cmd;
9324 /* 'which' wants to know if it was invoked from a standalone
9325 * shell. 'Which' in argv[0] indicates this. */
9326 if (strcmp(argv[0], "which") == 0) {
9327 argv[0] = (char *)"Which";
9328 }
9329# else
8447 if (!vforked && APPLET_IS_NOEXEC(applet_no)) { 9330 if (!vforked && APPLET_IS_NOEXEC(applet_no)) {
9331# endif
9332#if !ENABLE_PLATFORM_MINGW32 || !defined(_UCRT)
9333 /* If building for UCRT move this up into shellexec() to
9334 * work around a bug. */
8448 clearenv(); 9335 clearenv();
8449 while (*envp) 9336 while (*envp)
8450 putenv(*envp++); 9337 putenv(*envp++);
9338#endif
8451 popredir(/*drop:*/ 1); 9339 popredir(/*drop:*/ 1);
8452 run_noexec_applet_and_exit(applet_no, cmd, argv); 9340 run_noexec_applet_and_exit(applet_no, cmd, argv);
8453 } 9341 }
@@ -8458,6 +9346,44 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8458 } 9346 }
8459#endif 9347#endif
8460 9348
9349#if ENABLE_PLATFORM_MINGW32
9350 /* Workaround for libtool, which assumes the host is an MSYS2
9351 * environment and requires special-case escaping for cmd.exe.
9352 * https://github.com/skeeto/w64devkit/issues/50 */
9353 if (string_array_len(argv) >= 3 &&
9354 strcmp(argv[0], "cmd") == 0 &&
9355 strcmp(argv[1], "//c") == 0 &&
9356 strcmp(argv[2], "echo") == 0) {
9357 argv[1]++; /* drop extra slash */
9358 }
9359
9360 /* cmd was allocated on the stack with room for an extension */
9361 add_win32_extension((char *)cmd);
9362
9363# if ENABLE_FEATURE_SH_STANDALONE
9364 /* If the command is a script with an interpreter which is an
9365 * applet, we can run it as if it were a noexec applet. */
9366 if (parse_interpreter(cmd, &interp)) {
9367 applet_no = find_applet_by_name_for_sh(interp.name, path);
9368 if (applet_no >= 0) {
9369 argv[0] = (char *)cmd;
9370 /* evalcommand()/spawn_forkshell() add two elements before argv */
9371 if (interp.opts) {
9372 argv--;
9373 argv[0] = (char *)interp.opts;
9374 }
9375 argv--;
9376 cmd = argv[0] = (char *)interp.name;
9377 /* Identify the index of the script file in argv */
9378 set_interp(1 + (interp.opts != NULL));
9379 goto run_noexec;
9380 }
9381 }
9382# endif
9383
9384 execve(cmd, argv, envp);
9385 /* skip POSIX-mandated retry on ENOEXEC */
9386#else /* !ENABLE_PLATFORM_MINGW32 */
8461 repeat: 9387 repeat:
8462#ifdef SYSV 9388#ifdef SYSV
8463 do { 9389 do {
@@ -8493,14 +9419,25 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8493 argv[0] = (char*) "ash"; 9419 argv[0] = (char*) "ash";
8494 goto repeat; 9420 goto repeat;
8495 } 9421 }
9422#endif /* !ENABLE_PLATFORM_MINGW32 */
8496} 9423}
8497 9424
9425#if !ENABLE_PLATFORM_MINGW32 || !ENABLE_FEATURE_SH_STANDALONE
9426# define shellexec(prg, a, pth, i, n) shellexec(prg, a, pth, i)
9427#endif
9428
8498/* 9429/*
8499 * Exec a program. Never returns. If you change this routine, you may 9430 * Exec a program. Never returns. If you change this routine, you may
8500 * have to change the find_command routine as well. 9431 * have to change the find_command routine as well.
8501 * argv[-1] must exist and be writable! See tryexec() for why. 9432 * argv[-1] must exist and be writable! See tryexec() for why.
8502 */ 9433 */
8503static void shellexec(char *prog, char **argv, const char *path, int idx) 9434#if ENABLE_PLATFORM_MINGW32
9435static struct builtincmd *find_builtin(const char *name);
9436static void shellexec(char *prog, char **argv, const char *path, int idx,
9437 int noexec) NORETURN;
9438#endif
9439static void shellexec(char *prog, char **argv, const char *path, int idx,
9440 int noexec)
8504{ 9441{
8505 char *cmdname; 9442 char *cmdname;
8506 int e; 9443 int e;
@@ -8509,12 +9446,30 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8509 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ 9446 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
8510 9447
8511 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 9448 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
9449#if ENABLE_FEATURE_SH_STANDALONE && ENABLE_PLATFORM_MINGW32 && defined(_UCRT)
9450 /* Avoid UCRT bug by updating parent's environment and passing a
9451 * NULL environment pointer to execve(). */
9452 clearenv();
9453 while (*envp)
9454 putenv(*envp++);
9455 envp = NULL;
9456#endif
9457#if !ENABLE_PLATFORM_MINGW32
8512 if (strchr(prog, '/') != NULL 9458 if (strchr(prog, '/') != NULL
9459#else
9460 if (has_path(prog)
9461#endif
8513#if ENABLE_FEATURE_SH_STANDALONE 9462#if ENABLE_FEATURE_SH_STANDALONE
8514 || (applet_no = find_applet_by_name(prog)) >= 0 9463 || (applet_no = find_applet_by_name_for_sh(prog, path)) >= 0
8515#endif 9464#endif
8516 ) { 9465 ) {
9466#if ENABLE_PLATFORM_MINGW32
9467 char *progext = stack_add_ext_space(prog);
9468 tryexec(IF_FEATURE_SH_STANDALONE(applet_no, path, noexec,)
9469 progext, argv, envp);
9470#else
8517 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp); 9471 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
9472#endif
8518 if (applet_no >= 0) { 9473 if (applet_no >= 0) {
8519 /* We tried execing ourself, but it didn't work. 9474 /* We tried execing ourself, but it didn't work.
8520 * Maybe /proc/self/exe doesn't exist? 9475 * Maybe /proc/self/exe doesn't exist?
@@ -8523,13 +9478,33 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8523 goto try_PATH; 9478 goto try_PATH;
8524 } 9479 }
8525 e = errno; 9480 e = errno;
9481#if ENABLE_PLATFORM_MINGW32
9482 if (unix_path(prog)) {
9483 const char *name = bb_basename(prog);
9484# if ENABLE_FEATURE_SH_STANDALONE
9485 if ((applet_no = find_applet_by_name_for_sh(name, path)) >= 0) {
9486 tryexec(applet_no, path, noexec, name, argv, envp);
9487 e = errno;
9488 }
9489# endif
9490 if (!find_builtin(name)) {
9491 argv[0] = (char *)name;
9492 goto try_PATH;
9493 }
9494 }
9495#endif
8526 } else { 9496 } else {
8527 try_PATH: 9497 try_PATH:
8528 e = ENOENT; 9498 e = ENOENT;
8529 while (padvance(&path, argv[0]) >= 0) { 9499 while (padvance(&path, argv[0]) >= 0) {
8530 cmdname = stackblock(); 9500 cmdname = stackblock();
8531 if (--idx < 0 && pathopt == NULL) { 9501 if (--idx < 0 && pathopt == NULL) {
9502#if ENABLE_PLATFORM_MINGW32
9503 tryexec(IF_FEATURE_SH_STANDALONE(-1, path, noexec,)
9504 cmdname, argv, envp);
9505#else
8532 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); 9506 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
9507#endif
8533 if (errno != ENOENT && errno != ENOTDIR) 9508 if (errno != ENOENT && errno != ENOTDIR)
8534 e = errno; 9509 e = errno;
8535 } 9510 }
@@ -8568,6 +9543,9 @@ printentry(struct tblentry *cmdp)
8568 padvance(&path, cmdp->cmdname); 9543 padvance(&path, cmdp->cmdname);
8569 } while (--idx >= 0); 9544 } while (--idx >= 0);
8570 name = stackblock(); 9545 name = stackblock();
9546#if ENABLE_PLATFORM_MINGW32
9547 add_win32_extension(name);
9548#endif
8571 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); 9549 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8572} 9550}
8573 9551
@@ -8768,7 +9746,7 @@ changepath(const char *newval)
8768 bltin = idx; 9746 bltin = idx;
8769 break; 9747 break;
8770 } 9748 }
8771 new = strchr(new, ':'); 9749 new = strchr(new, PATH_SEP);
8772 if (!new) 9750 if (!new)
8773 break; 9751 break;
8774 idx++; 9752 idx++;
@@ -8945,14 +9923,37 @@ describe_command(char *command, const char *path, int describe_command_verbose)
8945 case CMDNORMAL: { 9923 case CMDNORMAL: {
8946 int j = entry.u.index; 9924 int j = entry.u.index;
8947 char *p; 9925 char *p;
9926#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
9927 if (j < -1) {
9928 p = (char *)bb_basename(command);
9929 if (describe_command_verbose) {
9930 out1fmt(" is a builtin applet");
9931 } else {
9932 out1str(applet_to_exe(p));
9933 }
9934 break;
9935 }
9936#endif
8948 if (j < 0) { 9937 if (j < 0) {
9938#if ENABLE_PLATFORM_MINGW32
9939 p = stack_add_ext_space(command);
9940#else
8949 p = command; 9941 p = command;
9942#endif
8950 } else { 9943 } else {
9944#if ENABLE_PLATFORM_MINGW32
9945 if (unix_path(command))
9946 command = (char *)bb_basename(command);
9947#endif
8951 do { 9948 do {
8952 padvance(&path, command); 9949 padvance(&path, command);
8953 } while (--j >= 0); 9950 } while (--j >= 0);
8954 p = stackblock(); 9951 p = stackblock();
8955 } 9952 }
9953#if ENABLE_PLATFORM_MINGW32
9954 add_win32_extension(p);
9955 bs_to_slash(p);
9956#endif
8956 if (describe_command_verbose) { 9957 if (describe_command_verbose) {
8957 out1fmt(" is %s", p); 9958 out1fmt(" is %s", p);
8958 } else { 9959 } else {
@@ -9107,6 +10108,15 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9107/*static int funcstringsize; // size of strings in node */ 10108/*static int funcstringsize; // size of strings in node */
9108static void *funcblock; /* block to allocate function from */ 10109static void *funcblock; /* block to allocate function from */
9109static char *funcstring_end; /* end of block to allocate strings from */ 10110static char *funcstring_end; /* end of block to allocate strings from */
10111#if ENABLE_PLATFORM_MINGW32
10112static int fs_size;
10113static void *fs_start;
10114# if FORKSHELL_DEBUG
10115static void *fs_funcstring;
10116static const char **annot;
10117static char *annot_free;
10118# endif
10119#endif
9110 10120
9111static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 10121static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
9112 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 10122 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -9240,14 +10250,79 @@ calcsize(int funcblocksize, union node *n)
9240} 10250}
9241 10251
9242static char * 10252static char *
9243nodeckstrdup(char *s) 10253nodeckstrdup(const char *s)
9244{ 10254{
10255#if ENABLE_PLATFORM_MINGW32
10256 if(!s)
10257 return NULL;
10258#endif
9245 funcstring_end -= SHELL_ALIGN(strlen(s) + 1); 10259 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
9246 return strcpy(funcstring_end, s); 10260 return strcpy(funcstring_end, s);
9247} 10261}
9248 10262
9249static union node *copynode(union node *); 10263static union node *copynode(union node *);
9250 10264
10265#if ENABLE_PLATFORM_MINGW32
10266# if FORKSHELL_DEBUG
10267# define MARK_PTR(dst,note,flag) forkshell_mark_ptr((void *)&dst, note, flag)
10268# else
10269# define MARK_PTR(dst,note,flag) forkshell_mark_ptr((void *)&dst)
10270# endif
10271
10272#define NO_FREE 0
10273#define FREE 1
10274
10275#if FORKSHELL_DEBUG
10276static void forkshell_mark_ptr(void *dst, const char *note, int flag)
10277#else
10278static void forkshell_mark_ptr(void *dst)
10279#endif
10280{
10281 char *lrelocate = (char *)fs_start + fs_size;
10282 int index = ((char *)dst - (char *)fs_start)/sizeof(char *);
10283
10284 lrelocate[index/8] |= 1 << (index % 8);
10285
10286#if FORKSHELL_DEBUG
10287 if (dst < fs_start || dst >= fs_funcstring) {
10288 fprintf(stderr, "dst (%p) out of range (%p %p)\n",
10289 dst, fs_start, fs_funcstring);
10290 }
10291 if (annot) {
10292 if (annot[index]) {
10293 fprintf(stderr, "duplicate annotation: %s %s\n",
10294 annot[index], note);
10295 }
10296 annot[index] = note;
10297 annot_free[index] = flag;
10298 }
10299#endif
10300}
10301
10302# define SAVE_PTR(dst,note,flag) { \
10303 if (fs_size) { \
10304 MARK_PTR(dst,note,flag); \
10305 } \
10306}
10307# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2) { \
10308 if (fs_size) { \
10309 MARK_PTR(dst1,note1,flag1); \
10310 MARK_PTR(dst2,note2,flag2); \
10311 } \
10312}
10313# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3) { \
10314 if (fs_size) { \
10315 MARK_PTR(dst1,note1,flag1); \
10316 MARK_PTR(dst2,note2,flag2); \
10317 MARK_PTR(dst3,note3,flag3); \
10318 } \
10319}
10320#else
10321# define SAVE_PTR(dst,note,flag)
10322# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2)
10323# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3)
10324#endif
10325
9251static struct nodelist * 10326static struct nodelist *
9252copynodelist(struct nodelist *lp) 10327copynodelist(struct nodelist *lp)
9253{ 10328{
@@ -9259,6 +10334,8 @@ copynodelist(struct nodelist *lp)
9259 *lpp = funcblock; 10334 *lpp = funcblock;
9260 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); 10335 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
9261 (*lpp)->n = copynode(lp->n); 10336 (*lpp)->n = copynode(lp->n);
10337 SAVE_PTR2((*lpp)->n, "(*lpp)->next", NO_FREE,
10338 (*lpp)->next, "(*lpp)->next", NO_FREE);
9262 lp = lp->next; 10339 lp = lp->next;
9263 lpp = &(*lpp)->next; 10340 lpp = &(*lpp)->next;
9264 } 10341 }
@@ -9282,10 +10359,14 @@ copynode(union node *n)
9282 new->ncmd.args = copynode(n->ncmd.args); 10359 new->ncmd.args = copynode(n->ncmd.args);
9283 new->ncmd.assign = copynode(n->ncmd.assign); 10360 new->ncmd.assign = copynode(n->ncmd.assign);
9284 new->ncmd.linno = n->ncmd.linno; 10361 new->ncmd.linno = n->ncmd.linno;
10362 SAVE_PTR3(new->ncmd.redirect, "ncmd.redirect", NO_FREE,
10363 new->ncmd.args, "ncmd.args", NO_FREE,
10364 new->ncmd.assign, "ncmd.assign", NO_FREE);
9285 break; 10365 break;
9286 case NPIPE: 10366 case NPIPE:
9287 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); 10367 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
9288 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; 10368 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
10369 SAVE_PTR(new->npipe.cmdlist, "npipe.cmdlist", NO_FREE);
9289 break; 10370 break;
9290 case NREDIR: 10371 case NREDIR:
9291 case NBACKGND: 10372 case NBACKGND:
@@ -9293,6 +10374,8 @@ copynode(union node *n)
9293 new->nredir.redirect = copynode(n->nredir.redirect); 10374 new->nredir.redirect = copynode(n->nredir.redirect);
9294 new->nredir.n = copynode(n->nredir.n); 10375 new->nredir.n = copynode(n->nredir.n);
9295 new->nredir.linno = n->nredir.linno; 10376 new->nredir.linno = n->nredir.linno;
10377 SAVE_PTR2(new->nredir.redirect, "nredir.redirect", NO_FREE,
10378 new->nredir.n, "nredir.n", NO_FREE);
9296 break; 10379 break;
9297 case NAND: 10380 case NAND:
9298 case NOR: 10381 case NOR:
@@ -9301,37 +10384,58 @@ copynode(union node *n)
9301 case NUNTIL: 10384 case NUNTIL:
9302 new->nbinary.ch2 = copynode(n->nbinary.ch2); 10385 new->nbinary.ch2 = copynode(n->nbinary.ch2);
9303 new->nbinary.ch1 = copynode(n->nbinary.ch1); 10386 new->nbinary.ch1 = copynode(n->nbinary.ch1);
10387 SAVE_PTR2(new->nbinary.ch1, "nbinary.ch1", NO_FREE,
10388 new->nbinary.ch2, "nbinary.ch2", NO_FREE);
9304 break; 10389 break;
9305 case NIF: 10390 case NIF:
9306 new->nif.elsepart = copynode(n->nif.elsepart); 10391 new->nif.elsepart = copynode(n->nif.elsepart);
9307 new->nif.ifpart = copynode(n->nif.ifpart); 10392 new->nif.ifpart = copynode(n->nif.ifpart);
9308 new->nif.test = copynode(n->nif.test); 10393 new->nif.test = copynode(n->nif.test);
10394 SAVE_PTR3(new->nif.elsepart, "nif.elsepart", NO_FREE,
10395 new->nif.ifpart, "nif.ifpart", NO_FREE,
10396 new->nif.test, "nif.test", NO_FREE);
9309 break; 10397 break;
9310 case NFOR: 10398 case NFOR:
9311 new->nfor.var = nodeckstrdup(n->nfor.var); 10399 new->nfor.var = nodeckstrdup(n->nfor.var);
9312 new->nfor.body = copynode(n->nfor.body); 10400 new->nfor.body = copynode(n->nfor.body);
9313 new->nfor.args = copynode(n->nfor.args); 10401 new->nfor.args = copynode(n->nfor.args);
9314 new->nfor.linno = n->nfor.linno; 10402 new->nfor.linno = n->nfor.linno;
10403 SAVE_PTR3(new->nfor.var,
10404 xasprintf("nfor.var '%s'", n->nfor.var ?: "NULL"), FREE,
10405 new->nfor.body, "nfor.body", NO_FREE,
10406 new->nfor.args, "nfor.args", NO_FREE);
9315 break; 10407 break;
9316 case NCASE: 10408 case NCASE:
9317 new->ncase.cases = copynode(n->ncase.cases); 10409 new->ncase.cases = copynode(n->ncase.cases);
9318 new->ncase.expr = copynode(n->ncase.expr); 10410 new->ncase.expr = copynode(n->ncase.expr);
9319 new->ncase.linno = n->ncase.linno; 10411 new->ncase.linno = n->ncase.linno;
10412 SAVE_PTR2(new->ncase.cases, "ncase.cases", NO_FREE,
10413 new->ncase.expr, "ncase.expr", NO_FREE);
9320 break; 10414 break;
9321 case NCLIST: 10415 case NCLIST:
9322 new->nclist.body = copynode(n->nclist.body); 10416 new->nclist.body = copynode(n->nclist.body);
9323 new->nclist.pattern = copynode(n->nclist.pattern); 10417 new->nclist.pattern = copynode(n->nclist.pattern);
9324 new->nclist.next = copynode(n->nclist.next); 10418 new->nclist.next = copynode(n->nclist.next);
10419 SAVE_PTR3(new->nclist.body, "nclist.body", NO_FREE,
10420 new->nclist.pattern, "nclist.pattern", NO_FREE,
10421 new->nclist.next, "nclist.next", NO_FREE);
9325 break; 10422 break;
9326 case NDEFUN: 10423 case NDEFUN:
9327 new->ndefun.body = copynode(n->ndefun.body); 10424 new->ndefun.body = copynode(n->ndefun.body);
9328 new->ndefun.text = nodeckstrdup(n->ndefun.text); 10425 new->ndefun.text = nodeckstrdup(n->ndefun.text);
9329 new->ndefun.linno = n->ndefun.linno; 10426 new->ndefun.linno = n->ndefun.linno;
10427 SAVE_PTR2(new->ndefun.body, "ndefun.body", NO_FREE,
10428 new->ndefun.text,
10429 xasprintf("ndefun.text '%s'", n->ndefun.text ?: "NULL"), FREE);
9330 break; 10430 break;
9331 case NARG: 10431 case NARG:
9332 new->narg.backquote = copynodelist(n->narg.backquote); 10432 new->narg.backquote = copynodelist(n->narg.backquote);
9333 new->narg.text = nodeckstrdup(n->narg.text); 10433 new->narg.text = nodeckstrdup(n->narg.text);
9334 new->narg.next = copynode(n->narg.next); 10434 new->narg.next = copynode(n->narg.next);
10435 SAVE_PTR3(new->narg.backquote, "narg.backquote", NO_FREE,
10436 new->narg.text,
10437 xasprintf("narg.text '%s'", n->narg.text ?: "NULL"), FREE,
10438 new->narg.next, "narg.next", NO_FREE);
9335 break; 10439 break;
9336 case NTO: 10440 case NTO:
9337#if BASH_REDIR_OUTPUT 10441#if BASH_REDIR_OUTPUT
@@ -9345,6 +10449,8 @@ copynode(union node *n)
9345 new->nfile.fname = copynode(n->nfile.fname); 10449 new->nfile.fname = copynode(n->nfile.fname);
9346 new->nfile.fd = n->nfile.fd; 10450 new->nfile.fd = n->nfile.fd;
9347 new->nfile.next = copynode(n->nfile.next); 10451 new->nfile.next = copynode(n->nfile.next);
10452 SAVE_PTR2(new->nfile.fname, "nfile.fname", NO_FREE,
10453 new->nfile.next, "nfile.next", NO_FREE);
9348 break; 10454 break;
9349 case NTOFD: 10455 case NTOFD:
9350 case NFROMFD: 10456 case NFROMFD:
@@ -9352,15 +10458,20 @@ copynode(union node *n)
9352 new->ndup.dupfd = n->ndup.dupfd; 10458 new->ndup.dupfd = n->ndup.dupfd;
9353 new->ndup.fd = n->ndup.fd; 10459 new->ndup.fd = n->ndup.fd;
9354 new->ndup.next = copynode(n->ndup.next); 10460 new->ndup.next = copynode(n->ndup.next);
10461 SAVE_PTR2(new->ndup.vname, "ndup.vname", NO_FREE,
10462 new->ndup.next, "ndup.next", NO_FREE);
9355 break; 10463 break;
9356 case NHERE: 10464 case NHERE:
9357 case NXHERE: 10465 case NXHERE:
9358 new->nhere.doc = copynode(n->nhere.doc); 10466 new->nhere.doc = copynode(n->nhere.doc);
9359 new->nhere.fd = n->nhere.fd; 10467 new->nhere.fd = n->nhere.fd;
9360 new->nhere.next = copynode(n->nhere.next); 10468 new->nhere.next = copynode(n->nhere.next);
10469 SAVE_PTR2(new->nhere.doc, "nhere.doc", NO_FREE,
10470 new->nhere.next, "nhere.next", NO_FREE);
9361 break; 10471 break;
9362 case NNOT: 10472 case NNOT:
9363 new->nnot.com = copynode(n->nnot.com); 10473 new->nnot.com = copynode(n->nnot.com);
10474 SAVE_PTR(new->nnot.com, "nnot.com", NO_FREE);
9364 break; 10475 break;
9365 }; 10476 };
9366 new->type = n->type; 10477 new->type = n->type;
@@ -9381,6 +10492,7 @@ copyfunc(union node *n)
9381 f = ckzalloc(blocksize /* + funcstringsize */); 10492 f = ckzalloc(blocksize /* + funcstringsize */);
9382 funcblock = (char *) f + offsetof(struct funcnode, n); 10493 funcblock = (char *) f + offsetof(struct funcnode, n);
9383 funcstring_end = (char *) f + blocksize; 10494 funcstring_end = (char *) f + blocksize;
10495 IF_PLATFORM_MINGW32(fs_size = 0);
9384 copynode(n); 10496 copynode(n);
9385 /* f->count = 0; - ckzalloc did it */ 10497 /* f->count = 0; - ckzalloc did it */
9386 return f; 10498 return f;
@@ -9408,12 +10520,15 @@ defun(union node *func)
9408#define SKIPFUNCDEF (1 << 3) 10520#define SKIPFUNCDEF (1 << 3)
9409static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 10521static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
9410static int skipcount; /* number of levels to skip */ 10522static int skipcount; /* number of levels to skip */
10523#if ENABLE_PLATFORM_POSIX
9411static int loopnest; /* current loop nesting level */ 10524static int loopnest; /* current loop nesting level */
10525#endif
9412static int funcline; /* starting line number of current function, or 0 if not in a function */ 10526static int funcline; /* starting line number of current function, or 0 if not in a function */
9413 10527
9414/* Forward decl way out to parsing code - dotrap needs it */ 10528/* Forward decl way out to parsing code - dotrap needs it */
9415static int evalstring(char *s, int flags); 10529static int evalstring(char *s, int flags);
9416 10530
10531#if !ENABLE_PLATFORM_MINGW32
9417/* Called to execute a trap. 10532/* Called to execute a trap.
9418 * Single callsite - at the end of evaltree(). 10533 * Single callsite - at the end of evaltree().
9419 * If we return non-zero, evaltree raises EXEXIT exception. 10534 * If we return non-zero, evaltree raises EXEXIT exception.
@@ -9472,6 +10587,47 @@ dotrap(void)
9472 savestatus = last_status; 10587 savestatus = last_status;
9473 TRACE(("dotrap returns\n")); 10588 TRACE(("dotrap returns\n"));
9474} 10589}
10590#else
10591static void
10592dotrap(void)
10593{
10594 int status, last_status;
10595 char *p;
10596
10597 if (!pending_int && waitcmd_int != 1) {
10598 waitcmd_int = 0;
10599 return;
10600 }
10601
10602 status = savestatus;
10603 last_status = status;
10604 if (status < 0) {
10605 status = exitstatus;
10606 savestatus = status;
10607 }
10608 pending_int = waitcmd_int = 0;
10609 barrier();
10610
10611 TRACE(("dotrap entered\n"));
10612 if (evalskip) {
10613 pending_int = 1;
10614 return;
10615 }
10616
10617 p = trap[SIGINT];
10618 if (p) {
10619 TRACE(("sig %d is active, will run handler '%s'\n", SIGINT, p));
10620 trap_depth++;
10621 evalstring(p, 0);
10622 trap_depth--;
10623 if (evalskip != SKIPFUNC)
10624 exitstatus = status;
10625 }
10626
10627 savestatus = last_status;
10628 TRACE(("dotrap returns\n"));
10629}
10630#endif
9475 10631
9476/* forward declarations - evaluation is fairly recursive business... */ 10632/* forward declarations - evaluation is fairly recursive business... */
9477static int evalloop(union node *, int); 10633static int evalloop(union node *, int);
@@ -9758,19 +10914,37 @@ evalcase(union node *n, int flags)
9758static int 10914static int
9759evalsubshell(union node *n, int flags) 10915evalsubshell(union node *n, int flags)
9760{ 10916{
10917 IF_PLATFORM_MINGW32(struct forkshell fs;)
9761 struct job *jp; 10918 struct job *jp;
9762 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ 10919 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9763 int status; 10920 int status;
9764 10921
9765 errlinno = lineno = n->nredir.linno; 10922 errlinno = lineno = n->nredir.linno;
9766 10923
10924#if ENABLE_PLATFORM_MINGW32
10925 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) {
10926 reset_exception_handler();
10927 expredir(n->nredir.redirect);
10928 redirect(n->nredir.redirect, 0);
10929 evaltreenr(n->nredir.n, flags);
10930 /* never returns */
10931 }
10932#else
9767 expredir(n->nredir.redirect); 10933 expredir(n->nredir.redirect);
9768 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) 10934 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
9769 goto nofork; 10935 goto nofork;
10936#endif
9770 INTOFF; 10937 INTOFF;
9771 if (backgnd == FORK_FG) 10938 if (backgnd == FORK_FG)
9772 get_tty_state(); 10939 get_tty_state();
9773 jp = makejob(1); 10940 jp = makejob(1);
10941#if ENABLE_PLATFORM_MINGW32
10942 memset(&fs, 0, sizeof(fs));
10943 fs.fpid = FS_EVALSUBSHELL;
10944 fs.n = n;
10945 fs.flags = flags;
10946 spawn_forkshell(&fs, jp, n, backgnd);
10947#else
9774 if (forkshell(jp, n, backgnd) == 0) { 10948 if (forkshell(jp, n, backgnd) == 0) {
9775 /* child */ 10949 /* child */
9776 INTON; 10950 INTON;
@@ -9783,6 +10957,7 @@ evalsubshell(union node *n, int flags)
9783 evaltreenr(n->nredir.n, flags); 10957 evaltreenr(n->nredir.n, flags);
9784 /* never returns */ 10958 /* never returns */
9785 } 10959 }
10960#endif
9786 /* parent */ 10961 /* parent */
9787 status = 0; 10962 status = 0;
9788 if (backgnd == FORK_FG) 10963 if (backgnd == FORK_FG)
@@ -9864,6 +11039,7 @@ expredir(union node *n)
9864static int 11039static int
9865evalpipe(union node *n, int flags) 11040evalpipe(union node *n, int flags)
9866{ 11041{
11042 IF_PLATFORM_MINGW32(struct forkshell fs;)
9867 struct job *jp; 11043 struct job *jp;
9868 struct nodelist *lp; 11044 struct nodelist *lp;
9869 int pipelen; 11045 int pipelen;
@@ -9890,6 +11066,16 @@ evalpipe(union node *n, int flags)
9890 ash_msg_and_raise_perror("can't create pipe"); 11066 ash_msg_and_raise_perror("can't create pipe");
9891 } 11067 }
9892 } 11068 }
11069#if ENABLE_PLATFORM_MINGW32
11070 memset(&fs, 0, sizeof(fs));
11071 fs.fpid = FS_EVALPIPE;
11072 fs.flags = flags;
11073 fs.n = lp->n;
11074 fs.fd[0] = pip[0];
11075 fs.fd[1] = pip[1];
11076 fs.fd[2] = prevfd;
11077 spawn_forkshell(&fs, jp, lp->n, n->npipe.pipe_backgnd);
11078#else
9893 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 11079 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
9894 /* child */ 11080 /* child */
9895 reset_exception_handler(); 11081 reset_exception_handler();
@@ -9908,6 +11094,7 @@ evalpipe(union node *n, int flags)
9908 evaltreenr(lp->n, flags); 11094 evaltreenr(lp->n, flags);
9909 /* never returns */ 11095 /* never returns */
9910 } 11096 }
11097#endif
9911 /* parent */ 11098 /* parent */
9912 if (prevfd >= 0) 11099 if (prevfd >= 0)
9913 close(prevfd); 11100 close(prevfd);
@@ -9965,6 +11152,9 @@ setinteractive(int on)
9965 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP); 11152 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
9966# if ENABLE_FEATURE_TAB_COMPLETION 11153# if ENABLE_FEATURE_TAB_COMPLETION
9967 line_input_state->get_exe_name = ash_command_name; 11154 line_input_state->get_exe_name = ash_command_name;
11155# if ENABLE_ASH_GLOB_OPTIONS
11156 line_input_state->sh_accept_glob = ash_accept_glob;
11157# endif
9968# endif 11158# endif
9969# if EDITING_HAS_sh_get_var 11159# if EDITING_HAS_sh_get_var
9970 line_input_state->sh_get_var = lookupvar; 11160 line_input_state->sh_get_var = lookupvar;
@@ -9992,6 +11182,12 @@ optschanged(void)
9992#else 11182#else
9993 viflag = 0; /* forcibly keep the option off */ 11183 viflag = 0; /* forcibly keep the option off */
9994#endif 11184#endif
11185#if ENABLE_ASH_NOCONSOLE
11186 hide_console(noconsole);
11187#endif
11188#if ENABLE_PLATFORM_MINGW32
11189 setwinxp(winxp);
11190#endif
9995} 11191}
9996 11192
9997struct localvar_list { 11193struct localvar_list {
@@ -10011,6 +11207,9 @@ poplocalvars(int keep)
10011 struct localvar_list *ll; 11207 struct localvar_list *ll;
10012 struct localvar *lvp, *next; 11208 struct localvar *lvp, *next;
10013 struct var *vp; 11209 struct var *vp;
11210#if ENABLE_PLATFORM_MINGW32
11211 int var_type;
11212#endif
10014 11213
10015 INTOFF; 11214 INTOFF;
10016 ll = localvar_stack; 11215 ll = localvar_stack;
@@ -10053,6 +11252,17 @@ poplocalvars(int keep)
10053 free((char*)vp->var_text); 11252 free((char*)vp->var_text);
10054 vp->flags = lvp->flags; 11253 vp->flags = lvp->flags;
10055 vp->var_text = lvp->text; 11254 vp->var_text = lvp->text;
11255#if ENABLE_PLATFORM_MINGW32
11256 var_type = is_bb_var(lvp->text);
11257 if (var_type == BB_VAR_ASSIGN && (lvp->flags & VEXPORT))
11258 putenv(lvp->text);
11259 else if (var_type) {
11260 char *var = xstrdup(lvp->text);
11261 *strchrnul(var, '=') = '\0';
11262 unsetenv(var);
11263 free(var);
11264 }
11265#endif
10056 } 11266 }
10057 free(lvp); 11267 free(lvp);
10058 } 11268 }
@@ -10275,7 +11485,7 @@ execcmd(int argc UNUSED_PARAM, char **argv)
10275 prog = argv[0]; 11485 prog = argv[0];
10276 if (optionarg) 11486 if (optionarg)
10277 argv[0] = optionarg; 11487 argv[0] = optionarg;
10278 shellexec(prog, argv, pathval(), 0); 11488 shellexec(prog, argv, pathval(), 0, FALSE);
10279 /* NOTREACHED */ 11489 /* NOTREACHED */
10280 } 11490 }
10281 return 0; 11491 return 0;
@@ -10328,6 +11538,9 @@ static int readcmd(int, char **) FAST_FUNC;
10328static int setcmd(int, char **) FAST_FUNC; 11538static int setcmd(int, char **) FAST_FUNC;
10329static int shiftcmd(int, char **) FAST_FUNC; 11539static int shiftcmd(int, char **) FAST_FUNC;
10330static int timescmd(int, char **) FAST_FUNC; 11540static int timescmd(int, char **) FAST_FUNC;
11541#if ENABLE_PLATFORM_MINGW32
11542static int titlecmd(int, char **) FAST_FUNC;
11543#endif
10331static int trapcmd(int, char **) FAST_FUNC; 11544static int trapcmd(int, char **) FAST_FUNC;
10332static int umaskcmd(int, char **) FAST_FUNC; 11545static int umaskcmd(int, char **) FAST_FUNC;
10333static int unsetcmd(int, char **) FAST_FUNC; 11546static int unsetcmd(int, char **) FAST_FUNC;
@@ -10400,7 +11613,7 @@ static const struct builtincmd builtintab[] = {
10400#if MAX_HISTORY 11613#if MAX_HISTORY
10401 { BUILTIN_NOSPEC "history" , historycmd }, 11614 { BUILTIN_NOSPEC "history" , historycmd },
10402#endif 11615#endif
10403#if JOBS 11616#if JOBS || JOBS_WIN32
10404 { BUILTIN_REGULAR "jobs" , jobscmd }, 11617 { BUILTIN_REGULAR "jobs" , jobscmd },
10405 { BUILTIN_REGULAR "kill" , killcmd }, 11618 { BUILTIN_REGULAR "kill" , killcmd },
10406#endif 11619#endif
@@ -10427,6 +11640,9 @@ static const struct builtincmd builtintab[] = {
10427 { BUILTIN_REGULAR "test" , testcmd }, 11640 { BUILTIN_REGULAR "test" , testcmd },
10428#endif 11641#endif
10429 { BUILTIN_SPEC_REG "times" , timescmd }, 11642 { BUILTIN_SPEC_REG "times" , timescmd },
11643#if ENABLE_PLATFORM_MINGW32
11644 { BUILTIN_REGULAR "title" , titlecmd },
11645#endif
10430 { BUILTIN_SPEC_REG "trap" , trapcmd }, 11646 { BUILTIN_SPEC_REG "trap" , trapcmd },
10431 { BUILTIN_REGULAR "true" , truecmd }, 11647 { BUILTIN_REGULAR "true" , truecmd },
10432 { BUILTIN_REGULAR "type" , typecmd }, 11648 { BUILTIN_REGULAR "type" , typecmd },
@@ -10445,7 +11661,7 @@ static const struct builtincmd builtintab[] = {
10445 /* [ */ 1 * ENABLE_ASH_TEST + \ 11661 /* [ */ 1 * ENABLE_ASH_TEST + \
10446 /* [[ */ 1 * BASH_TEST2 + \ 11662 /* [[ */ 1 * BASH_TEST2 + \
10447 /* alias */ 1 * ENABLE_ASH_ALIAS + \ 11663 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10448 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ 11664 /* bg */ 1 * JOBS + \
10449 /* break cd cddir */ 3) 11665 /* break cd cddir */ 3)
10450#define EVALCMD (COMMANDCMD + \ 11666#define EVALCMD (COMMANDCMD + \
10451 /* command */ 1 * ENABLE_ASH_CMDCMD + \ 11667 /* command */ 1 * ENABLE_ASH_CMDCMD + \
@@ -10526,6 +11742,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10526 * as POSIX mandates */ 11742 * as POSIX mandates */
10527 return back_exitstatus; 11743 return back_exitstatus;
10528} 11744}
11745
10529static int 11746static int
10530evalcommand(union node *cmd, int flags) 11747evalcommand(union node *cmd, int flags)
10531{ 11748{
@@ -10625,9 +11842,19 @@ evalcommand(union node *cmd, int flags)
10625 11842
10626 localvar_stop = pushlocalvars(vlocal); 11843 localvar_stop = pushlocalvars(vlocal);
10627 11844
11845#if ENABLE_PLATFORM_MINGW32
11846# if ENABLE_FEATURE_SH_STANDALONE
11847 /* Reserve two extra spots at the front for shellexec. */
11848 nargv = stalloc(sizeof(char *) * (argc + 3));
11849 argv = nargv = nargv + 2;
11850# else
11851 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
11852# endif
11853#else
10628 /* Reserve one extra spot at the front for shellexec. */ 11854 /* Reserve one extra spot at the front for shellexec. */
10629 nargv = stalloc(sizeof(char *) * (argc + 2)); 11855 nargv = stalloc(sizeof(char *) * (argc + 2));
10630 argv = ++nargv; 11856 argv = ++nargv;
11857#endif
10631 for (sp = arglist.list; sp; sp = sp->next) { 11858 for (sp = arglist.list; sp; sp = sp->next) {
10632 TRACE(("evalcommand arg: %s\n", sp->text)); 11859 TRACE(("evalcommand arg: %s\n", sp->text));
10633 *nargv++ = sp->text; 11860 *nargv++ = sp->text;
@@ -10736,9 +11963,11 @@ evalcommand(union node *cmd, int flags)
10736 11963
10737 default: { 11964 default: {
10738 11965
11966//TODO: find a better solution for Windows on ARM than ignoring NOFORK
10739#if ENABLE_FEATURE_SH_STANDALONE \ 11967#if ENABLE_FEATURE_SH_STANDALONE \
10740 && ENABLE_FEATURE_SH_NOFORK \ 11968 && ENABLE_FEATURE_SH_NOFORK \
10741 && NUM_APPLETS > 1 11969 && NUM_APPLETS > 1 \
11970 && !(defined(_ARM64_) && !defined(_UCRT) && ENABLE_PLATFORM_MINGW32)
10742/* (1) BUG: if variables are set, we need to fork, or save/restore them 11971/* (1) BUG: if variables are set, we need to fork, or save/restore them
10743 * around run_nofork_applet() call. 11972 * around run_nofork_applet() call.
10744 * (2) Should this check also be done in forkshell()? 11973 * (2) Should this check also be done in forkshell()?
@@ -10748,6 +11977,9 @@ evalcommand(union node *cmd, int flags)
10748 int applet_no = (- cmdentry.u.index - 2); 11977 int applet_no = (- cmdentry.u.index - 2);
10749 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { 11978 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
10750 char **sv_environ; 11979 char **sv_environ;
11980#if ENABLE_PLATFORM_MINGW32
11981 char *sv_argv0;
11982#endif
10751 11983
10752 INTOFF; 11984 INTOFF;
10753 sv_environ = environ; 11985 sv_environ = environ;
@@ -10760,8 +11992,16 @@ evalcommand(union node *cmd, int flags)
10760 * and/or wait for user input ineligible for NOFORK: 11992 * and/or wait for user input ineligible for NOFORK:
10761 * for example, "yes" or "rm" (rm -i waits for input). 11993 * for example, "yes" or "rm" (rm -i waits for input).
10762 */ 11994 */
11995#if ENABLE_PLATFORM_MINGW32
11996 sv_argv0 = __argv[0];
11997 argv[0] = (char *)bb_basename(argv[0]);
11998 __argv[0] = argv[0];
11999#endif
10763 exitstatus = run_nofork_applet(applet_no, argv); 12000 exitstatus = run_nofork_applet(applet_no, argv);
10764 environ = sv_environ; 12001 environ = sv_environ;
12002#if ENABLE_PLATFORM_MINGW32
12003 __argv[0] = sv_argv0;
12004#endif
10765 /* 12005 /*
10766 * Try enabling NOFORK for "yes" applet. 12006 * Try enabling NOFORK for "yes" applet.
10767 * ^C _will_ stop it (write returns EINTR), 12007 * ^C _will_ stop it (write returns EINTR),
@@ -10780,6 +12020,22 @@ evalcommand(union node *cmd, int flags)
10780 * in a script or a subshell does not need forking, 12020 * in a script or a subshell does not need forking,
10781 * we can just exec it. 12021 * we can just exec it.
10782 */ 12022 */
12023#if ENABLE_PLATFORM_MINGW32
12024 if (!(flags & EV_EXIT) || may_have_traps IF_SUW32(|| delayexit)) {
12025 /* No, forking off a child is necessary */
12026 struct forkshell fs;
12027
12028 INTOFF;
12029 memset(&fs, 0, sizeof(fs));
12030 fs.fpid = FS_SHELLEXEC;
12031 fs.argv = argv;
12032 fs.path = (char*)path;
12033 fs.fd[0] = cmdentry.u.index;
12034 jp = makejob(/*cmd,*/ 1);
12035 spawn_forkshell(&fs, jp, cmd, FORK_FG);
12036 break;
12037 }
12038#else
10783 if (!(flags & EV_EXIT) || may_have_traps) { 12039 if (!(flags & EV_EXIT) || may_have_traps) {
10784 /* No, forking off a child is necessary */ 12040 /* No, forking off a child is necessary */
10785 INTOFF; 12041 INTOFF;
@@ -10787,7 +12043,8 @@ evalcommand(union node *cmd, int flags)
10787 jp = vforkexec(cmd, argv, path, cmdentry.u.index); 12043 jp = vforkexec(cmd, argv, path, cmdentry.u.index);
10788 break; 12044 break;
10789 } 12045 }
10790 shellexec(argv[0], argv, path, cmdentry.u.index); 12046#endif
12047 shellexec(argv[0], argv, path, cmdentry.u.index, FALSE);
10791 /* NOTREACHED */ 12048 /* NOTREACHED */
10792 } /* default */ 12049 } /* default */
10793 case CMDBUILTIN: 12050 case CMDBUILTIN:
@@ -10998,6 +12255,54 @@ static void popstring(void)
10998 INTON; 12255 INTON;
10999} 12256}
11000 12257
12258#if ENABLE_PLATFORM_MINGW32
12259/*
12260 * Wrapper around nonblock_immune_read() to remove CRs, but only from
12261 * CRLF pairs. The tricky part is handling a CR at the end of the buffer.
12262 */
12263static inline ssize_t
12264nonblock_immune_wrapper(struct parsefile *pf, char *buffer, size_t count)
12265{
12266 int nr, injected_cr;
12267
12268 // Inject unprocessed CR from previous read into the buffer.
12269 if (pf->cr)
12270 *buffer = '\r';
12271 retry:
12272 nr = nonblock_immune_read(pf->pf_fd, buffer + pf->cr, count - pf->cr);
12273 if (nr < 0)
12274 return nr;
12275
12276 injected_cr = pf->cr;
12277 nr += pf->cr;
12278 pf->cr = 0;
12279
12280 if (nr > 0) {
12281 nr = remove_cr(buffer, nr);
12282 // remove_cr() won't reduce size to zero, so [nr - 1] is OK.
12283 if (buffer[nr - 1] == '\r') {
12284 if (nr > 1) {
12285 // Ignore trailing CR for now: we'll deal with it later.
12286 pf->cr = 1;
12287 --nr;
12288 } else if (injected_cr) { // nr == 1
12289 // Buffer only contains an injected CR. This means the
12290 // read returned EOF. Return the buffer as-is. The
12291 // next call will detect EOF.
12292 } else {
12293 // Buffer only contains a CR from the most recent read.
12294 // Try another read, treating the CR as injected. We'll
12295 // either get more characters or EOF. Either way we
12296 // won't end up here again.
12297 pf->cr = 1;
12298 goto retry;
12299 }
12300 }
12301 }
12302 return nr;
12303}
12304#endif
12305
11001static int 12306static int
11002preadfd(void) 12307preadfd(void)
11003{ 12308{
@@ -11008,7 +12313,11 @@ preadfd(void)
11008#if ENABLE_FEATURE_EDITING 12313#if ENABLE_FEATURE_EDITING
11009 /* retry: */ 12314 /* retry: */
11010 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 12315 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
12316#if ENABLE_PLATFORM_MINGW32
12317 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
12318#else
11011 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 12319 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
12320#endif
11012 else { 12321 else {
11013# if ENABLE_ASH_IDLE_TIMEOUT 12322# if ENABLE_ASH_IDLE_TIMEOUT
11014 int timeout = -1; 12323 int timeout = -1;
@@ -11047,17 +12356,29 @@ preadfd(void)
11047 INTON; /* here non-blocked SIGINT will longjmp */ 12356 INTON; /* here non-blocked SIGINT will longjmp */
11048 if (nr == 0) { 12357 if (nr == 0) {
11049 /* ^C pressed, "convert" to SIGINT */ 12358 /* ^C pressed, "convert" to SIGINT */
12359# if !ENABLE_PLATFORM_MINGW32
11050 write(STDOUT_FILENO, "^C\n", 3); 12360 write(STDOUT_FILENO, "^C\n", 3);
11051 raise(SIGINT); /* here non-blocked SIGINT will longjmp */ 12361 raise(SIGINT); /* here non-blocked SIGINT will longjmp */
11052 /* raise(SIGINT) did not work! (e.g. if SIGINT 12362 /* raise(SIGINT) did not work! (e.g. if SIGINT
11053 * is SIG_IGNed on startup, it stays SIG_IGNed) 12363 * is SIG_IGNed on startup, it stays SIG_IGNed)
11054 */ 12364 */
12365# else
12366 write(STDOUT_FILENO, "^C\n", 3);
12367# endif
11055 if (trap[SIGINT]) { 12368 if (trap[SIGINT]) {
12369# if ENABLE_PLATFORM_MINGW32
12370 pending_int = 1;
12371 dotrap();
12372# endif
11056 empty_line_input: 12373 empty_line_input:
11057 buf[0] = '\n'; 12374 buf[0] = '\n';
11058 buf[1] = '\0'; 12375 buf[1] = '\0';
11059 return 1; 12376 return 1;
11060 } 12377 }
12378# if ENABLE_PLATFORM_MINGW32
12379 else
12380 raise_interrupt();
12381# endif
11061 exitstatus = 128 + SIGINT; 12382 exitstatus = 128 + SIGINT;
11062 /* bash behavior on ^C + ignored SIGINT: */ 12383 /* bash behavior on ^C + ignored SIGINT: */
11063 goto again; 12384 goto again;
@@ -11081,7 +12402,11 @@ preadfd(void)
11081 } 12402 }
11082 } 12403 }
11083#else 12404#else
12405# if ENABLE_PLATFORM_MINGW32
12406 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
12407# else
11084 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 12408 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
12409# endif
11085#endif 12410#endif
11086 12411
11087#if 0 /* disabled: nonblock_immune_read() handles this problem */ 12412#if 0 /* disabled: nonblock_immune_read() handles this problem */
@@ -11380,6 +12705,7 @@ popallfiles(void)
11380 unwindfiles(&basepf); 12705 unwindfiles(&basepf);
11381} 12706}
11382 12707
12708#if !ENABLE_PLATFORM_MINGW32
11383/* 12709/*
11384 * Close the file(s) that the shell is reading commands from. Called 12710 * Close the file(s) that the shell is reading commands from. Called
11385 * after a fork is done. 12711 * after a fork is done.
@@ -11393,6 +12719,7 @@ closescript(void)
11393 g_parsefile->pf_fd = 0; 12719 g_parsefile->pf_fd = 0;
11394 } 12720 }
11395} 12721}
12722#endif
11396 12723
11397/* 12724/*
11398 * Like setinputfile, but takes an open file descriptor. Call this with 12725 * Like setinputfile, but takes an open file descriptor. Call this with
@@ -11625,8 +12952,19 @@ options(int *login_sh)
11625 int val; 12952 int val;
11626 int c; 12953 int c;
11627 12954
11628 if (login_sh != NULL) /* if we came from startup code */ 12955#if ENABLE_ASH_NOCONSOLE
12956 noconsole = console_state();
12957#endif
12958 if (login_sh != NULL) { /* if we came from startup code */
11629 minusc = NULL; 12959 minusc = NULL;
12960#if ENABLE_PLATFORM_MINGW32
12961 dirarg = NULL;
12962 title = NULL;
12963# if ENABLE_SUW32
12964 delayexit = 0;
12965# endif
12966#endif
12967 }
11630 while ((p = *argptr) != NULL) { 12968 while ((p = *argptr) != NULL) {
11631 c = *p++; 12969 c = *p++;
11632 if (c != '-' && c != '+') 12970 if (c != '-' && c != '+')
@@ -11656,6 +12994,31 @@ options(int *login_sh)
11656 cflag = 1; 12994 cflag = 1;
11657 continue; 12995 continue;
11658 } 12996 }
12997#if ENABLE_PLATFORM_MINGW32
12998 /* Undocumented flags;
12999 * -d force current directory
13000 * -t title to display in console window
13001 * -N prompt user before exit
13002 * Must appear before -s or -c. */
13003 if (c == 'd' && val == 1) {
13004 if (*argptr == NULL)
13005 ash_msg_and_raise_error(bb_msg_requires_arg, "-d");
13006 dirarg = *argptr++;
13007 continue;
13008 }
13009 if (c == 't' && val == 1) {
13010 if (*argptr == NULL)
13011 ash_msg_and_raise_error(bb_msg_requires_arg, "-t");
13012 title = *argptr++;
13013 continue;
13014 }
13015# if ENABLE_SUW32
13016 if (c == 'N' && val == 1) {
13017 delayexit = 1;
13018 continue;
13019 }
13020# endif
13021#endif
11659 if (c == 's') { /* -s, +s */ 13022 if (c == 's') { /* -s, +s */
11660 sflag = 1; 13023 sflag = 1;
11661 continue; 13024 continue;
@@ -13699,6 +15062,9 @@ evalstring(char *s, int flags)
13699 int status; 15062 int status;
13700 15063
13701 s = sstrdup(s); 15064 s = sstrdup(s);
15065#if ENABLE_PLATFORM_MINGW32
15066 remove_cr(s, strlen(s)+1);
15067#endif
13702 setinputstring(s); 15068 setinputstring(s);
13703 setstackmark(&smark); 15069 setstackmark(&smark);
13704 15070
@@ -13786,7 +15152,7 @@ cmdloop(int top)
13786 int skip; 15152 int skip;
13787 15153
13788 setstackmark(&smark); 15154 setstackmark(&smark);
13789#if JOBS 15155#if JOBS || JOBS_WIN32
13790 if (jobctl) 15156 if (jobctl)
13791 showjobs(SHOW_CHANGED|SHOW_STDERR); 15157 showjobs(SHOW_CHANGED|SHOW_STDERR);
13792#endif 15158#endif
@@ -13794,6 +15160,9 @@ cmdloop(int top)
13794 if (iflag && top) { 15160 if (iflag && top) {
13795 inter++; 15161 inter++;
13796 chkmail(); 15162 chkmail();
15163#if ENABLE_PLATFORM_MINGW32
15164 terminal_mode(TRUE);
15165#endif
13797 } 15166 }
13798 n = parsecmd(inter); 15167 n = parsecmd(inter);
13799#if DEBUG 15168#if DEBUG
@@ -13817,8 +15186,10 @@ cmdloop(int top)
13817 } else { 15186 } else {
13818 int i; 15187 int i;
13819 15188
15189#if !ENABLE_PLATFORM_MINGW32
13820 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ 15190 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13821 job_warning >>= 1; 15191 job_warning >>= 1;
15192#endif
13822 numeof = 0; 15193 numeof = 0;
13823 i = evaltree(n, 0); 15194 i = evaltree(n, 0);
13824 if (n) 15195 if (n)
@@ -13848,7 +15219,7 @@ find_dot_file(char *basename)
13848 int len; 15219 int len;
13849 15220
13850 /* don't try this for absolute or relative paths */ 15221 /* don't try this for absolute or relative paths */
13851 if (strchr(basename, '/')) 15222 if (strchr(basename, '/') IF_PLATFORM_MINGW32(|| strchr(basename, '\\')))
13852 return basename; 15223 return basename;
13853 15224
13854 path = pathval(); 15225 path = pathval();
@@ -14013,6 +15384,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
14013 struct builtincmd *bcmd; 15384 struct builtincmd *bcmd;
14014 int len; 15385 int len;
14015 15386
15387#if !ENABLE_PLATFORM_MINGW32
14016 /* If name contains a slash, don't use PATH or hash table */ 15388 /* If name contains a slash, don't use PATH or hash table */
14017 if (strchr(name, '/') != NULL) { 15389 if (strchr(name, '/') != NULL) {
14018 entry->u.index = -1; 15390 entry->u.index = -1;
@@ -14032,6 +15404,35 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
14032 entry->cmdtype = CMDNORMAL; 15404 entry->cmdtype = CMDNORMAL;
14033 return; 15405 return;
14034 } 15406 }
15407#else /* ENABLE_PLATFORM_MINGW32 */
15408 /* If name contains a slash or drive prefix, don't use PATH or hash table */
15409 if (has_path(name)) {
15410 entry->u.index = -1;
15411 entry->cmdtype = CMDNORMAL;
15412 fullname = stack_add_ext_space(name);
15413 if (add_win32_extension(fullname)) {
15414 return;
15415 } else if (unix_path(name)) {
15416 name = (char *)bb_basename(name);
15417 if (
15418# if ENABLE_FEATURE_SH_STANDALONE
15419 find_applet_by_name_for_sh(name, path) >= 0 ||
15420# endif
15421 !find_builtin(bb_basename(name))
15422 ) {
15423 act |= DO_NOFUNC;
15424 } else if (act & DO_ABS) {
15425 entry->cmdtype = CMDUNKNOWN;
15426 return;
15427 }
15428 } else if (act & DO_ABS) {
15429 entry->cmdtype = CMDUNKNOWN;
15430 return;
15431 } else {
15432 return;
15433 }
15434 }
15435#endif /* ENABLE_PLATFORM_MINGW32 */
14035 15436
14036/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ 15437/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
14037 15438
@@ -14086,7 +15487,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
14086 15487
14087#if ENABLE_FEATURE_SH_STANDALONE 15488#if ENABLE_FEATURE_SH_STANDALONE
14088 { 15489 {
14089 int applet_no = find_applet_by_name(name); 15490 int applet_no = find_applet_by_name_for_sh(name, path);
14090 if (applet_no >= 0) { 15491 if (applet_no >= 0) {
14091 entry->cmdtype = CMDNORMAL; 15492 entry->cmdtype = CMDNORMAL;
14092 entry->u.index = -2 - applet_no; 15493 entry->u.index = -2 - applet_no;
@@ -14125,12 +15526,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
14125 } 15526 }
14126 } 15527 }
14127 /* if rehash, don't redo absolute path names */ 15528 /* if rehash, don't redo absolute path names */
14128 if (fullname[0] == '/' && idx <= prev) { 15529 if (!is_relative_path(fullname) && idx <= prev) {
14129 if (idx < prev) 15530 if (idx < prev)
14130 continue; 15531 continue;
14131 TRACE(("searchexec \"%s\": no change\n", name)); 15532 TRACE(("searchexec \"%s\": no change\n", name));
14132 goto success; 15533 goto success;
14133 } 15534 }
15535#if ENABLE_PLATFORM_MINGW32
15536 add_win32_extension(fullname);
15537#endif
14134 while (stat(fullname, &statb) < 0) { 15538 while (stat(fullname, &statb) < 0) {
14135#ifdef SYSV 15539#ifdef SYSV
14136 if (errno == EINTR) 15540 if (errno == EINTR)
@@ -14268,19 +15672,45 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14268 if (LONE_DASH(action)) 15672 if (LONE_DASH(action))
14269 action = NULL; 15673 action = NULL;
14270 else { 15674 else {
15675#if !ENABLE_PLATFORM_MINGW32
14271 if (action[0]) /* not NULL and not "" and not "-" */ 15676 if (action[0]) /* not NULL and not "" and not "-" */
14272 may_have_traps = 1; 15677 may_have_traps = 1;
15678#endif
14273 action = ckstrdup(action); 15679 action = ckstrdup(action);
14274 } 15680 }
14275 } 15681 }
14276 free(trap[signo]); 15682 free(trap[signo]);
14277 trap[signo] = action; 15683 trap[signo] = action;
15684#if ENABLE_PLATFORM_MINGW32
15685 if (signo == SIGINT) {
15686 // trap '' INT disables Ctrl-C, anything else enables it
15687 if (action && action[0] == '\0') {
15688 SetConsoleCtrlHandler(NULL, TRUE);
15689# if ENABLE_FEATURE_EDITING
15690 if (line_input_state) {
15691 line_input_state->flags |= IGNORE_CTRL_C;
15692 }
15693# endif
15694 } else {
15695 SetConsoleCtrlHandler(NULL, FALSE);
15696# if ENABLE_FEATURE_EDITING
15697 if (line_input_state) {
15698 line_input_state->flags &= ~IGNORE_CTRL_C;
15699 }
15700# endif
15701 }
15702 }
15703#else
14278 if (signo != 0 && signo < NSIG) 15704 if (signo != 0 && signo < NSIG)
14279 setsignal(signo); 15705 setsignal(signo);
15706#endif
14280 INTON; 15707 INTON;
14281 next: 15708 next:
14282 ap++; 15709 ap++;
14283 } 15710 }
15711#if ENABLE_PLATFORM_MINGW32
15712 may_have_traps = trap[SIGINT] && trap[SIGINT][0] != '\0';
15713#endif
14284 return exitcode; 15714 return exitcode;
14285} 15715}
14286 15716
@@ -14309,10 +15739,12 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14309 { 15739 {
14310 const char *a = applet_names; 15740 const char *a = applet_names;
14311 while (*a) { 15741 while (*a) {
14312 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a); 15742 if (prefer_applet(a, pathval())) {
14313 if (col > 60) { 15743 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
14314 out1fmt("\n"); 15744 if (col > 60) {
14315 col = 0; 15745 out1fmt("\n");
15746 col = 0;
15747 }
14316 } 15748 }
14317 while (*a++ != '\0') 15749 while (*a++ != '\0')
14318 continue; 15750 continue;
@@ -14374,7 +15806,24 @@ exportcmd(int argc UNUSED_PARAM, char **argv)
14374 } else { 15806 } else {
14375 vp = *findvar(name); 15807 vp = *findvar(name);
14376 if (vp) { 15808 if (vp) {
15809#if ENABLE_PLATFORM_MINGW32
15810 if (is_bb_var(name) == BB_VAR_EXACT) {
15811 if (flag_off == ~VEXPORT)
15812 unsetenv(name);
15813 else if (flag == VEXPORT && !(vp->flags & VUNSET))
15814 putenv(vp->var_text);
15815 }
15816#endif
14377 vp->flags = ((vp->flags | flag) & flag_off); 15817 vp->flags = ((vp->flags | flag) & flag_off);
15818#if ENABLE_PLATFORM_MINGW32
15819 /* Unexporting a variable imported from the
15820 * environment restores its original value and
15821 * removes the VIMPORT flag. */
15822 if ((vp->flags & VIMPORT) && (flag_off == ~VEXPORT)) {
15823 vp->flags &= ~VIMPORT;
15824 p = getenv(name);
15825 } else
15826#endif
14378 continue; 15827 continue;
14379 } 15828 }
14380 } 15829 }
@@ -14465,6 +15914,21 @@ timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14465 return 0; 15914 return 0;
14466} 15915}
14467 15916
15917#if ENABLE_PLATFORM_MINGW32
15918static int FAST_FUNC
15919titlecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15920{
15921 if (*argptr == NULL) {
15922 char buffer[256];
15923 if (get_title(buffer, sizeof(buffer)))
15924 puts(buffer);
15925 } else {
15926 set_title(*argptr);
15927 }
15928 return 0;
15929}
15930#endif
15931
14468#if ENABLE_FEATURE_SH_MATH 15932#if ENABLE_FEATURE_SH_MATH
14469/* 15933/*
14470 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell. 15934 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
@@ -14557,6 +16021,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14557 r = shell_builtin_read(&params); 16021 r = shell_builtin_read(&params);
14558 INTON; 16022 INTON;
14559 16023
16024#if !ENABLE_PLATFORM_MINGW32
14560 if ((uintptr_t)r == 1 && errno == EINTR) { 16025 if ((uintptr_t)r == 1 && errno == EINTR) {
14561 /* To get SIGCHLD: sleep 1 & read x; echo $x 16026 /* To get SIGCHLD: sleep 1 & read x; echo $x
14562 * Correct behavior is to not exit "read" 16027 * Correct behavior is to not exit "read"
@@ -14569,6 +16034,29 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14569 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */ 16034 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
14570 /* The actual value observed with bash 5.2.15: */ 16035 /* The actual value observed with bash 5.2.15: */
14571 return 128 + SIGALRM; 16036 return 128 + SIGALRM;
16037#else /* ENABLE_PLATFORM_MINGW32 */
16038 if ((uintptr_t)r == 2) {
16039 /* Timeout, return 128 + SIGALRM */
16040 return 142;
16041 } else if ((uintptr_t)r == 3) {
16042 /* ^C pressed, propagate event */
16043 if (trap[SIGINT]) {
16044 write_ctrl_c();
16045 pending_int = 1;
16046 dotrap();
16047 if (!(rootshell && iflag))
16048 return (uintptr_t)0;
16049 else
16050 goto again;
16051 } else if (iflag) {
16052 raise_interrupt();
16053 } else {
16054 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
16055 exitshell();
16056 }
16057 return (uintptr_t)r;
16058 }
16059#endif
14572 16060
14573 if ((uintptr_t)r > 1) 16061 if ((uintptr_t)r > 1)
14574 ash_msg_and_raise_error(r); 16062 ash_msg_and_raise_error(r);
@@ -14630,6 +16118,9 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14630 if (!isdigit(modestr[0])) 16118 if (!isdigit(modestr[0]))
14631 mask ^= 0777; 16119 mask ^= 0777;
14632 umask(mask); 16120 umask(mask);
16121#if ENABLE_PLATFORM_MINGW32
16122 setvareq(xasprintf("BB_UMASK=0%o", mask), VEXPORT|VNOSAVE);
16123#endif
14633 } 16124 }
14634 return 0; 16125 return 0;
14635} 16126}
@@ -14730,6 +16221,13 @@ exitshell(void)
14730 /*free(p); - we'll exit soon */ 16221 /*free(p); - we'll exit soon */
14731 } 16222 }
14732 out: 16223 out:
16224#if ENABLE_SUW32
16225 if (delayexit) {
16226#define EXIT_MSG "Press any key to exit..."
16227 console_write(EXIT_MSG, sizeof(EXIT_MSG) - 1);
16228 _getch();
16229 }
16230#endif
14733 exitreset(); 16231 exitreset();
14734 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}". 16232 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14735 * our setjobctl(0) does not panic if tcsetpgrp fails inside it. 16233 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
@@ -14740,22 +16238,99 @@ exitshell(void)
14740 /* NOTREACHED */ 16238 /* NOTREACHED */
14741} 16239}
14742 16240
16241#if ENABLE_PLATFORM_MINGW32
16242/* We need to see if HOME is *really* unset */
16243# undef getenv
16244static void setvar_if_unset(const char *key, const char *value)
16245{
16246 if (!getenv(key) || getuid() == 0)
16247 setvar(key, value, VEXPORT);
16248}
16249#endif
16250
14743/* Don't inline: conserve stack of caller from having our locals too */ 16251/* Don't inline: conserve stack of caller from having our locals too */
14744static NOINLINE void 16252static NOINLINE void
14745init(void) 16253init(void)
14746{ 16254{
16255#if ENABLE_PLATFORM_MINGW32
16256 int import = 0;
16257#endif
16258
14747 /* we will never free this */ 16259 /* we will never free this */
14748 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ); 16260 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ);
14749 basepf.linno = 1; 16261 basepf.linno = 1;
14750 16262
16263#if !ENABLE_PLATFORM_MINGW32
14751 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 16264 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
14752 setsignal(SIGCHLD); 16265 setsignal(SIGCHLD);
16266#endif
14753 16267
14754 { 16268 {
14755 char **envp; 16269 char **envp;
14756 const char *p; 16270 const char *p;
14757 16271
14758 initvar(); 16272 initvar();
16273
16274#if ENABLE_PLATFORM_MINGW32
16275 /*
16276 * case insensitive env names from Windows world
16277 *
16278 * Some standard env names such as PATH is named Path and so on
16279 * ash itself is case sensitive, so "Path" will confuse it, as
16280 * MSVC getenv() is case insensitive.
16281 *
16282 * We may end up having both Path and PATH. Then Path will be chosen
16283 * because it appears first.
16284 */
16285 if (windows_env()) {
16286 /*
16287 * If we get here it's because the environment suggests we
16288 * haven't been invoked from an earlier instance of BusyBox.
16289 */
16290 char *start, *end;
16291 struct passwd *pw;
16292
16293 /* mintty sets HOME: unset it */
16294 const char *tty = getenv("TERM_PROGRAM");
16295 if (tty && strcmp(tty, "mintty") == 0) {
16296 unsetenv("HOME");
16297 }
16298
16299 import = VIMPORT;
16300 for (envp = environ; envp && *envp; envp++) {
16301 if (!(end=strchr(*envp, '=')))
16302 continue;
16303
16304 /* check for invalid characters in name */
16305 start = (char *)endofname(*envp);
16306 if (*start != '=') {
16307 /* Make a copy of the original variable */
16308 setvareq(xstrdup(*envp), VEXPORT|VNOSAVE);
16309
16310 /* Replace invalid characters with underscores */
16311 for (; start < end; start++) {
16312 if (!isalnum(*start)) {
16313 *start = '_';
16314 }
16315 }
16316 }
16317
16318 /* make all variable names uppercase */
16319 for (start = *envp;start < end;start++)
16320 *start = toupper(*start);
16321 }
16322
16323 /* Initialise some variables normally set at login, but
16324 * only if someone hasn't already set them or we're root. */
16325 pw = getpwuid(getuid());
16326 if (pw) {
16327 setvar_if_unset("USER", pw->pw_name);
16328 setvar_if_unset("LOGNAME", pw->pw_name);
16329 setvar_if_unset("HOME", pw->pw_dir);
16330 }
16331 setvar_if_unset("SHELL", DEFAULT_SHELL);
16332 }
16333#endif
14759 for (envp = environ; envp && *envp; envp++) { 16334 for (envp = environ; envp && *envp; envp++) {
14760/* Used to have 16335/* Used to have
14761 * p = endofname(*envp); 16336 * p = endofname(*envp);
@@ -14769,7 +16344,11 @@ init(void)
14769 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this 16344 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14770 */ 16345 */
14771 if (strchr(*envp, '=')) { 16346 if (strchr(*envp, '=')) {
16347#if !ENABLE_PLATFORM_MINGW32
14772 setvareq(*envp, VEXPORT|VTEXTFIXED); 16348 setvareq(*envp, VEXPORT|VTEXTFIXED);
16349#else
16350 setvareq(*envp, VEXPORT|import);
16351#endif
14773 } 16352 }
14774 } 16353 }
14775 16354
@@ -14816,7 +16395,11 @@ procargs(char **xargv)
14816 int i; 16395 int i;
14817 int login_sh; 16396 int login_sh;
14818 16397
16398#if ENABLE_PLATFORM_MINGW32
16399 login_sh = applet_name[0] == 'l';
16400#else
14819 login_sh = /*xargv[0] &&*/ xargv[0][0] == '-'; 16401 login_sh = /*xargv[0] &&*/ xargv[0][0] == '-';
16402#endif
14820#if NUM_SCRIPTS > 0 16403#if NUM_SCRIPTS > 0
14821 if (minusc) 16404 if (minusc)
14822 goto setarg0; 16405 goto setarg0;
@@ -14839,7 +16422,9 @@ procargs(char **xargv)
14839 } 16422 }
14840 if (iflag == 2 /* no explicit -i given */ 16423 if (iflag == 2 /* no explicit -i given */
14841 && sflag == 1 /* -s given (or implied) */ 16424 && sflag == 1 /* -s given (or implied) */
16425#if !ENABLE_PLATFORM_MINGW32
14842 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */ 16426 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
16427#endif
14843 && isatty(0) && isatty(1) /* we are on tty */ 16428 && isatty(0) && isatty(1) /* we are on tty */
14844 ) { 16429 ) {
14845 iflag = 1; 16430 iflag = 1;
@@ -14859,6 +16444,9 @@ procargs(char **xargv)
14859 goto setarg0; 16444 goto setarg0;
14860 } else if (!sflag) { 16445 } else if (!sflag) {
14861 setinputfile(*xargv, 0); 16446 setinputfile(*xargv, 0);
16447#if ENABLE_PLATFORM_MINGW32
16448 bs_to_slash(*xargv);
16449#endif
14862 setarg0: 16450 setarg0:
14863 arg0 = *xargv++; 16451 arg0 = *xargv++;
14864 } 16452 }
@@ -14881,8 +16469,10 @@ procargs(char **xargv)
14881 * NB: must do it before setting up signals (in optschanged()) 16469 * NB: must do it before setting up signals (in optschanged())
14882 * and reading .profile etc (after we return from here): 16470 * and reading .profile etc (after we return from here):
14883 */ 16471 */
16472#if !ENABLE_PLATFORM_MINGW32
14884 if (iflag) 16473 if (iflag)
14885 signal(SIGHUP, SIG_DFL); 16474 signal(SIGHUP, SIG_DFL);
16475#endif
14886 16476
14887 optschanged(); 16477 optschanged();
14888 16478
@@ -14926,9 +16516,21 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14926 struct stackmark smark; 16516 struct stackmark smark;
14927 int login_sh; 16517 int login_sh;
14928 16518
16519#if ENABLE_PLATFORM_MINGW32
16520 INIT_G_memstack();
16521
16522 if (argc == 3 && !strcmp(argv[1], "--fs")) {
16523 forkshell_init(argv[2]);
16524 /* only reached in case of error */
16525 bb_error_msg_and_die("forkshell failed");
16526 }
16527#endif
16528
14929 /* Initialize global data */ 16529 /* Initialize global data */
14930 INIT_G_misc(); 16530 INIT_G_misc();
16531#if !ENABLE_PLATFORM_MINGW32
14931 INIT_G_memstack(); 16532 INIT_G_memstack();
16533#endif
14932 INIT_G_var(); 16534 INIT_G_var();
14933#if ENABLE_ASH_ALIAS 16535#if ENABLE_ASH_ALIAS
14934 INIT_G_alias(); 16536 INIT_G_alias();
@@ -14974,6 +16576,10 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14974 init(); 16576 init();
14975 setstackmark(&smark); 16577 setstackmark(&smark);
14976 16578
16579#if ENABLE_PLATFORM_MINGW32
16580 SetConsoleCtrlHandler(ctrl_handler, TRUE);
16581#endif
16582
14977#if NUM_SCRIPTS > 0 16583#if NUM_SCRIPTS > 0
14978 if (argc < 0) 16584 if (argc < 0)
14979 /* Non-NULL minusc tells procargs that an embedded script is being run */ 16585 /* Non-NULL minusc tells procargs that an embedded script is being run */
@@ -14985,11 +16591,45 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14985 trace_puts_args(argv); 16591 trace_puts_args(argv);
14986#endif 16592#endif
14987 16593
16594#if ENABLE_PLATFORM_MINGW32
16595 if (!dirarg && !login_sh && iflag) {
16596 char *cwd = getcwd(NULL, 0);
16597 if (cwd) {
16598 chdir(cwd);
16599 setpwd(NULL, 0);
16600 free(cwd);
16601 }
16602 }
16603
16604 if (title)
16605 set_title(title);
16606#endif
16607
14988 if (login_sh) { 16608 if (login_sh) {
14989 const char *hp; 16609 const char *hp;
14990 16610
16611#if ENABLE_PLATFORM_MINGW32
16612 if (!dirarg) {
16613 hp = lookupvar("HOME");
16614 if (hp == NULL || *hp == '\0')
16615 hp = xgetpwuid(getuid())->pw_dir;
16616 chdir(hp);
16617 setpwd(NULL, 0);
16618 }
16619#endif
16620
14991 state = 1; 16621 state = 1;
16622#if ENABLE_PLATFORM_MINGW32
16623 hp = concat_path_file(get_system_drive(), "/etc/profile");
16624 read_profile(hp);
16625 free((void *)hp);
16626
16627 hp = exe_relative_path("/etc/profile");
16628 read_profile(hp);
16629 free((void *)hp);
16630#else
14992 read_profile("/etc/profile"); 16631 read_profile("/etc/profile");
16632#endif
14993 state1: 16633 state1:
14994 state = 2; 16634 state = 2;
14995 hp = lookupvar("HOME"); 16635 hp = lookupvar("HOME");
@@ -14998,10 +16638,18 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14998 } 16638 }
14999 state2: 16639 state2:
15000 state = 3; 16640 state = 3;
16641#if ENABLE_PLATFORM_MINGW32
16642 if (dirarg) {
16643 chdir(dirarg);
16644 setpwd(NULL, 0);
16645 }
16646#endif
15001 if (iflag 16647 if (iflag
16648#if ENABLE_PLATFORM_POSIX
15002#ifndef linux 16649#ifndef linux
15003 && getuid() == geteuid() && getgid() == getegid() 16650 && getuid() == geteuid() && getgid() == getegid()
15004#endif 16651#endif
16652#endif
15005 ) { 16653 ) {
15006 const char *shinit = lookupvar("ENV"); 16654 const char *shinit = lookupvar("ENV");
15007 if (shinit != NULL && *shinit != '\0') 16655 if (shinit != NULL && *shinit != '\0')
@@ -15026,7 +16674,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
15026 // ash -sc 'echo $-' 16674 // ash -sc 'echo $-'
15027 // continue reading input from stdin after running 'echo'. 16675 // continue reading input from stdin after running 'echo'.
15028 // bash does not do this: it prints "hBcs" and exits. 16676 // bash does not do this: it prints "hBcs" and exits.
16677#if !ENABLE_PLATFORM_MINGW32
15029 evalstring(minusc, EV_EXIT); 16678 evalstring(minusc, EV_EXIT);
16679#else
16680 evalstring(minusc, sflag ? 0 : EV_EXIT);
16681#endif
15030 } 16682 }
15031 16683
15032 if (sflag || minusc == NULL) { 16684 if (sflag || minusc == NULL) {
@@ -15074,6 +16726,1110 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
15074 /* NOTREACHED */ 16726 /* NOTREACHED */
15075} 16727}
15076 16728
16729#if ENABLE_PLATFORM_MINGW32
16730static void
16731forkshell_openhere(struct forkshell *fs)
16732{
16733 const char *p = fs->path;
16734 size_t len = strlen(p);
16735 int pip[2];
16736
16737 pip[0] = fs->fd[0];
16738 pip[1] = fs->fd[1];
16739
16740 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16741
16742 close(pip[0]);
16743 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
16744 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
16745 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
16746 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
16747 signal(SIGPIPE, SIG_DFL);
16748 xwrite(pip[1], p, len);
16749 _exit_SUCCESS();
16750}
16751
16752static void
16753forkshell_evalbackcmd(struct forkshell *fs)
16754{
16755#if BASH_PROCESS_SUBST
16756 /* determine end of pipe used by parent (ip) and child (ic) */
16757 const int ctl = fs->fd[2];
16758 const int ip = (ctl == CTLTOPROC);
16759 const int ic = !(ctl == CTLTOPROC);
16760#else
16761 const int ip = 0;
16762 const int ic = 1;
16763#endif
16764 union node *n = fs->n;
16765 int pip[2];
16766
16767 pip[ip] = fs->fd[ip];
16768 pip[ic] = fs->fd[ic];
16769
16770 FORCEINTON;
16771 close(pip[ip]);
16772 if (pip[ic] != ic) {
16773 /*close(ic);*/
16774 dup2_or_raise(pip[ic], ic);
16775 close(pip[ic]);
16776 }
16777 eflag = 0;
16778 ifsfree();
16779 evaltreenr(n, EV_EXIT);
16780 /* NOTREACHED */
16781}
16782
16783static void
16784forkshell_evalsubshell(struct forkshell *fs)
16785{
16786 union node *n = fs->n;
16787 int flags = fs->flags;
16788
16789 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16790 INTON;
16791 flags |= EV_EXIT;
16792 if (fs->mode)
16793 flags &= ~EV_TESTED;
16794 expredir(n->nredir.redirect);
16795 redirect(n->nredir.redirect, 0);
16796 evaltreenr(n->nredir.n, flags);
16797 /* never returns */
16798}
16799
16800static void
16801forkshell_evalpipe(struct forkshell *fs)
16802{
16803 union node *n = fs->n;
16804 int flags = fs->flags;
16805 int prevfd = fs->fd[2];
16806 int pip[2] = {fs->fd[0], fs->fd[1]};
16807
16808 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16809 INTON;
16810 if (pip[1] >= 0) {
16811 close(pip[0]);
16812 }
16813 if (prevfd > 0) {
16814 dup2(prevfd, 0);
16815 close(prevfd);
16816 }
16817 if (pip[1] > 1) {
16818 dup2(pip[1], 1);
16819 close(pip[1]);
16820 }
16821 evaltreenr(n, flags);
16822}
16823
16824static void
16825forkshell_shellexec(struct forkshell *fs)
16826{
16827 int idx = fs->fd[0];
16828 char **argv = fs->argv;
16829 const char *path = fs->path;
16830
16831 FORCEINTON;
16832 shellexec(argv[0], argv, path, idx, TRUE);
16833}
16834
16835static void
16836forkshell_child(struct forkshell *fs)
16837{
16838 switch ( fs->fpid ) {
16839 case FS_OPENHERE:
16840 forkshell_openhere(fs);
16841 break;
16842 case FS_EVALBACKCMD:
16843 forkshell_evalbackcmd(fs);
16844 break;
16845 case FS_EVALSUBSHELL:
16846 forkshell_evalsubshell(fs);
16847 break;
16848 case FS_EVALPIPE:
16849 forkshell_evalpipe(fs);
16850 break;
16851 case FS_SHELLEXEC:
16852 forkshell_shellexec(fs);
16853 break;
16854 }
16855}
16856
16857/*
16858 * Reinitialise the builtin environment variables in varinit. Their
16859 * current settings have been copied from the parent in vartab. Look
16860 * these up using the names from varinit_data, copy the details from
16861 * vartab to varinit and replace the old copy in vartab with the new
16862 * one in varinit.
16863 *
16864 * Also reinitialise the function pointers and line number variable.
16865 */
16866static void
16867reinitvar(void)
16868{
16869 int i;
16870 const char *name;
16871 struct var **old;
16872
16873 for (i=0; i<ARRAY_SIZE(varinit); ++i) {
16874 if (i == LINENO_INDEX)
16875 name = "LINENO=";
16876 else if (i == FUNCNAME_INDEX)
16877 name = "FUNCNAME=";
16878 else
16879 name = varinit_data[i].var_text;
16880 if ((old = findvar(name)) != NULL) {
16881 varinit[i] = **old;
16882 *old = varinit+i;
16883 }
16884 varinit[i].var_func = varinit_data[i].var_func;
16885 }
16886 vlineno.var_text = linenovar;
16887 vfuncname.var_text = funcnamevar;
16888}
16889
16890static void
16891spawn_forkshell(struct forkshell *fs, struct job *jp, union node *n, int mode)
16892{
16893 struct forkshell *new;
16894 char buf[32];
16895 const char *argv[] = { "sh", "--fs", NULL, NULL };
16896 intptr_t ret;
16897
16898 new = forkshell_prepare(fs);
16899 if (new == NULL)
16900 goto fail;
16901
16902 new->mode = mode;
16903 new->nprocs = jp == NULL ? 0 : jp->nprocs;
16904#if JOBS_WIN32
16905 new->jpnull = jp == NULL;
16906#endif
16907 sprintf(buf, "%p", new->hMapFile);
16908 argv[2] = buf;
16909 ret = spawnve(P_NOWAIT, bb_busybox_exec_path, (char *const *)argv, NULL);
16910 CloseHandle(new->hMapFile);
16911 UnmapViewOfFile(new);
16912 if (ret == -1) {
16913 fail:
16914 if (jp)
16915 freejob(jp);
16916 ash_msg_and_raise_error("unable to spawn shell");
16917 }
16918 forkparent(jp, n, mode, (HANDLE)ret);
16919}
16920
16921/*
16922 * forkshell_prepare() and friends
16923 *
16924 * The sequence is as follows:
16925 * - funcblocksize is initialized
16926 * - forkshell_size(fs) is called to calculate the exact memory needed
16927 * - a new struct is allocated
16928 * - funcblock, funcstring, relocate are initialized from the new block
16929 * - forkshell_copy(fs) is called to copy recursively everything over
16930 * it will record all relocations along the way
16931 *
16932 * When this memory is mapped elsewhere, pointer fixup will be needed
16933 */
16934
16935/* redefine without test that fs_size is nonzero */
16936#undef SAVE_PTR
16937#undef SAVE_PTR2
16938#undef SAVE_PTR3
16939#define SAVE_PTR(dst,note,flag) {MARK_PTR(dst,note,flag);}
16940
16941static int align_len(const char *s)
16942{
16943 return s ? SHELL_ALIGN(strlen(s)+1) : 0;
16944}
16945
16946struct datasize {
16947 int funcblocksize;
16948 int funcstringsize;
16949};
16950
16951#define SLIST_SIZE_BEGIN(name,type) \
16952static struct datasize \
16953name(struct datasize ds, type *p) \
16954{ \
16955 while (p) { \
16956 ds.funcblocksize += sizeof(type);
16957 /* do something here with p */
16958#define SLIST_SIZE_END() \
16959 p = p->next; \
16960 } \
16961 return ds; \
16962}
16963
16964#define SLIST_COPY_BEGIN(name,type) \
16965static type * \
16966name(type *vp) \
16967{ \
16968 type *start; \
16969 type **vpp; \
16970 vpp = &start; \
16971 while (vp) { \
16972 *vpp = funcblock; \
16973 funcblock = (char *) funcblock + sizeof(type);
16974 /* do something here with vpp and vp */
16975#define SLIST_COPY_END() \
16976 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE); \
16977 vp = vp->next; \
16978 vpp = &(*vpp)->next; \
16979 } \
16980 *vpp = NULL; \
16981 return start; \
16982}
16983
16984/*
16985 * struct var
16986 */
16987SLIST_SIZE_BEGIN(var_size,struct var)
16988ds.funcstringsize += align_len(p->var_text);
16989SLIST_SIZE_END()
16990
16991SLIST_COPY_BEGIN(var_copy,struct var)
16992(*vpp)->var_text = nodeckstrdup(vp->var_text);
16993(*vpp)->flags = vp->flags;
16994(*vpp)->var_func = NULL;
16995SAVE_PTR((*vpp)->var_text, xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"), FREE);
16996SLIST_COPY_END()
16997
16998/*
16999 * struct tblentry
17000 */
17001static struct datasize
17002tblentry_size(struct datasize ds, struct tblentry *tep)
17003{
17004 while (tep) {
17005 ds.funcblocksize += sizeof(struct tblentry) + align_len(tep->cmdname);
17006 /* CMDBUILTIN, e->param.cmd needs no pointer relocation */
17007 if (tep->cmdtype == CMDFUNCTION) {
17008 ds.funcblocksize += offsetof(struct funcnode, n);
17009 ds.funcblocksize = calcsize(ds.funcblocksize, &tep->param.func->n);
17010 }
17011 tep = tep->next;
17012 }
17013 return ds;
17014}
17015
17016static struct tblentry *
17017tblentry_copy(struct tblentry *tep)
17018{
17019 struct tblentry *start;
17020 struct tblentry **newp;
17021 int size;
17022
17023 newp = &start;
17024 while (tep) {
17025 *newp = funcblock;
17026 size = sizeof(struct tblentry) + align_len(tep->cmdname);
17027
17028 funcblock = (char *) funcblock + size;
17029 memcpy(*newp, tep, sizeof(struct tblentry)+strlen(tep->cmdname));
17030 switch (tep->cmdtype) {
17031 case CMDBUILTIN:
17032 /* Save index of builtin, not pointer; fixed by forkshell_init() */
17033 (*newp)->param.index = tep->param.cmd - builtintab;
17034 break;
17035 case CMDFUNCTION:
17036 (*newp)->param.func = funcblock;
17037 funcblock = (char *) funcblock + offsetof(struct funcnode, n);
17038 copynode(&tep->param.func->n);
17039 SAVE_PTR((*newp)->param.func, "param.func", NO_FREE);
17040 break;
17041 default:
17042 break;
17043 }
17044 SAVE_PTR((*newp)->next, xasprintf("cmdname '%s'", tep->cmdname), FREE);
17045 tep = tep->next;
17046 newp = &(*newp)->next;
17047 }
17048 *newp = NULL;
17049 return start;
17050}
17051
17052static struct datasize
17053cmdtable_size(struct datasize ds)
17054{
17055 int i;
17056 ds.funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE;
17057 for (i = 0; i < CMDTABLESIZE; i++)
17058 ds = tblentry_size(ds, cmdtable[i]);
17059 return ds;
17060}
17061
17062static struct tblentry **
17063cmdtable_copy(void)
17064{
17065 struct tblentry **new = funcblock;
17066 int i;
17067
17068 funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE;
17069 for (i = 0; i < CMDTABLESIZE; i++) {
17070 new[i] = tblentry_copy(cmdtable[i]);
17071 SAVE_PTR(new[i], xasprintf("cmdtable[%d]", i), FREE);
17072 }
17073 return new;
17074}
17075
17076#if ENABLE_ASH_ALIAS
17077/*
17078 * struct alias
17079 */
17080SLIST_SIZE_BEGIN(alias_size,struct alias)
17081ds.funcstringsize += align_len(p->name);
17082ds.funcstringsize += align_len(p->val);
17083SLIST_SIZE_END()
17084
17085SLIST_COPY_BEGIN(alias_copy,struct alias)
17086(*vpp)->name = nodeckstrdup(vp->name);
17087(*vpp)->val = nodeckstrdup(vp->val);
17088(*vpp)->flag = vp->flag;
17089SAVE_PTR((*vpp)->name, xasprintf("(*vpp)->name '%s'", vp->name ?: "NULL"), FREE);
17090SAVE_PTR((*vpp)->val, xasprintf("(*vpp)->val '%s'", vp->val ?: "NULL"), FREE);
17091SLIST_COPY_END()
17092
17093static struct datasize
17094atab_size(struct datasize ds)
17095{
17096 int i;
17097 ds.funcblocksize += sizeof(struct alias *)*ATABSIZE;
17098 for (i = 0; i < ATABSIZE; i++)
17099 ds = alias_size(ds, atab[i]);
17100 return ds;
17101}
17102
17103static struct alias **
17104atab_copy(void)
17105{
17106 struct alias **new = funcblock;
17107 int i;
17108
17109 funcblock = (char *) funcblock + sizeof(struct alias *)*ATABSIZE;
17110 for (i = 0; i < ATABSIZE; i++) {
17111 new[i] = alias_copy(atab[i]);
17112 SAVE_PTR(new[i], xasprintf("atab[%d]", i), FREE);
17113 }
17114 return new;
17115}
17116#endif
17117
17118/*
17119 * char **
17120 */
17121static struct datasize
17122argv_size(struct datasize ds, char **p)
17123{
17124 if (p) {
17125 while (*p) {
17126 ds.funcblocksize += sizeof(char *);
17127 ds.funcstringsize += align_len(*p);
17128 p++;
17129 }
17130 // Allow two extra elements for tryexec().
17131 ds.funcblocksize += 3 * sizeof(char *);
17132 }
17133 return ds;
17134}
17135
17136static char **
17137argv_copy(char **p)
17138{
17139 char **new, **start = funcblock;
17140#if FORKSHELL_DEBUG
17141 int i = 0;
17142#endif
17143
17144 if (p) {
17145 // Allow two extra elements for tryexec().
17146 funcblock = (char *) funcblock + 2 * sizeof(char *);
17147 while (*p) {
17148 new = funcblock;
17149 funcblock = (char *) funcblock + sizeof(char *);
17150 *new = nodeckstrdup(*p);
17151 SAVE_PTR(*new, xasprintf("argv[%d] '%s'", i++, *p), FREE);
17152 p++;
17153 }
17154 new = funcblock;
17155 funcblock = (char *) funcblock + sizeof(char *);
17156 *new = NULL;
17157 return start + 2;
17158 }
17159 return NULL;
17160}
17161
17162#if MAX_HISTORY
17163static struct datasize
17164history_size(struct datasize ds)
17165{
17166 int i;
17167 line_input_t *st = line_input_state;
17168
17169 ds.funcblocksize += sizeof(char *) * st->cnt_history;
17170 for (i = 0; i < st->cnt_history; i++) {
17171 ds.funcstringsize += align_len(st->history[i]);
17172 }
17173 return ds;
17174}
17175
17176static char **
17177history_copy(void)
17178{
17179 line_input_t *st = line_input_state;
17180 char **new = funcblock;
17181 int i;
17182
17183 funcblock = (char *)funcblock + sizeof(char *) * st->cnt_history;
17184 for (i = 0; i < st->cnt_history; i++) {
17185 new[i] = nodeckstrdup(st->history[i]);
17186 SAVE_PTR(new[i],
17187 xasprintf("history[%d] '%s'", i, st->history[i]), FREE);
17188 }
17189 return new;
17190}
17191#endif
17192
17193#if JOBS_WIN32
17194/*
17195 * struct procstat
17196 */
17197static struct datasize
17198procstat_size(struct datasize ds, int nj)
17199{
17200 struct job *jp = jobtab + nj;
17201
17202 if (jp->ps != &jp->ps0)
17203 ds.funcblocksize += sizeof(struct procstat) * jp->nprocs;
17204
17205 for (int i = 0; i < jp->nprocs; i++)
17206 ds.funcstringsize += align_len(jp->ps[i].ps_cmd);
17207
17208 return ds;
17209}
17210
17211static struct procstat *
17212procstat_copy(int nj)
17213{
17214 struct job *jp = jobtab + nj;
17215 struct procstat *new = funcblock;
17216
17217 funcblock = (char *)funcblock + sizeof(struct procstat) * jp->nprocs;
17218 memcpy(new, jp->ps, sizeof(struct procstat) * jp->nprocs);
17219
17220 for (int i = 0; i < jp->nprocs; i++) {
17221 new[i].ps_cmd = nodeckstrdup(jp->ps[i].ps_cmd);
17222 SAVE_PTR(new[i].ps_cmd,
17223 xasprintf("jobtab[%d].ps[%d].ps_cmd '%s'",
17224 nj, i, jp->ps[i].ps_cmd), FREE);
17225 }
17226 return new;
17227}
17228
17229/*
17230 * struct jobs
17231 */
17232static struct datasize
17233jobtab_size(struct datasize ds)
17234{
17235 ds.funcblocksize += sizeof(struct job) * njobs;
17236 for (int i = 0; i < njobs; i++) {
17237 if (jobtab[i].used)
17238 ds = procstat_size(ds, i);
17239 }
17240 return ds;
17241}
17242
17243static struct job *
17244jobtab_copy(void)
17245{
17246 struct job *new = funcblock;
17247 int i;
17248
17249 funcblock = (char *)funcblock + sizeof(struct job) * njobs;
17250 memcpy(new, jobtab, sizeof(struct job) * njobs);
17251
17252 for (i = 0; i < njobs; i++) {
17253 if (!jobtab[i].used)
17254 continue;
17255
17256 if (jobtab[i].ps == &jobtab[i].ps0) {
17257 new[i].ps0.ps_cmd = nodeckstrdup(jobtab[i].ps0.ps_cmd);
17258 SAVE_PTR(new[i].ps0.ps_cmd,
17259 xasprintf("jobtab[%d].ps0.ps_cmd '%s'",
17260 i, jobtab[i].ps0.ps_cmd), FREE);
17261 new[i].ps = &new[i].ps0;
17262 } else if (jobtab[i].nprocs) {
17263 new[i].ps = procstat_copy(i);
17264 } else {
17265 new[i].ps = NULL;
17266 }
17267 SAVE_PTR(new[i].ps, xasprintf("jobtab[%d].ps", i), FREE);
17268
17269 if (jobtab[i].prev_job) {
17270 new[i].prev_job = new + (jobtab[i].prev_job - jobtab);
17271 SAVE_PTR(new[i].prev_job,
17272 xasprintf("jobtab[%d].prev_job", i), FREE);
17273 }
17274 }
17275 return new;
17276}
17277#endif
17278
17279/*
17280 * struct redirtab
17281 */
17282static int
17283redirtab_size(int funcblocksize, struct redirtab *rdtp)
17284{
17285 while (rdtp) {
17286 funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
17287 rdtp = rdtp->next;
17288 }
17289 return funcblocksize;
17290}
17291
17292static struct redirtab *
17293redirtab_copy(struct redirtab *rdtp)
17294{
17295 struct redirtab *start;
17296 struct redirtab **vpp;
17297
17298 vpp = &start;
17299 while (rdtp) {
17300 int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
17301 *vpp = funcblock;
17302 funcblock = (char *) funcblock + size;
17303 memcpy(*vpp, rdtp, size);
17304 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE);
17305 rdtp = rdtp->next;
17306 vpp = &(*vpp)->next;
17307 }
17308 *vpp = NULL;
17309 return start;
17310}
17311
17312static struct datasize
17313globals_var_size(struct datasize ds)
17314{
17315 int i;
17316
17317 ds.funcblocksize += sizeof(struct globals_var);
17318 ds.funcstringsize += align_len(funcname);
17319 ds = argv_size(ds, shellparam.p);
17320 ds.funcblocksize = redirtab_size(ds.funcblocksize, redirlist);
17321 for (i = 0; i < VTABSIZE; i++)
17322 ds = var_size(ds, vartab[i]);
17323 return ds;
17324}
17325
17326#undef funcname
17327#undef shellparam
17328#undef redirlist
17329#undef vartab
17330static struct globals_var *
17331globals_var_copy(void)
17332{
17333 int i;
17334 struct globals_var *gvp, *new;
17335
17336 gvp = ash_ptr_to_globals_var;
17337 new = funcblock;
17338 funcblock = (char *) funcblock + sizeof(struct globals_var);
17339 memcpy(new, gvp, sizeof(struct globals_var));
17340
17341 new->funcname = nodeckstrdup(gvp->funcname);
17342 SAVE_PTR(new->funcname, xasprintf("funcname '%s'", gvp->funcname ?: "NULL"), FREE);
17343
17344 /* shparam */
17345 new->shellparam.malloced = 0;
17346 new->shellparam.p = argv_copy(gvp->shellparam.p);
17347 SAVE_PTR(new->shellparam.p, "shellparam.p", NO_FREE);
17348
17349 new->redirlist = redirtab_copy(gvp->redirlist);
17350 SAVE_PTR(new->redirlist, "redirlist", NO_FREE);
17351
17352 for (i = 0; i < VTABSIZE; i++) {
17353 new->vartab[i] = var_copy(gvp->vartab[i]);
17354 SAVE_PTR(new->vartab[i], xasprintf("vartab[%d]", i), FREE);
17355 }
17356
17357 return new;
17358}
17359
17360static struct datasize
17361globals_misc_size(struct datasize ds)
17362{
17363 ds.funcblocksize += sizeof(struct globals_misc);
17364 ds.funcstringsize += align_len(minusc);
17365 if (curdir != nullstr)
17366 ds.funcstringsize += align_len(curdir);
17367 if (physdir != nullstr)
17368 ds.funcstringsize += align_len(physdir);
17369 ds.funcstringsize += align_len(arg0);
17370 ds.funcstringsize += align_len(commandname);
17371 for (int i = 0; i < ARRAY_SIZE(trap); i++)
17372 ds.funcstringsize += align_len(trap[i]);
17373 return ds;
17374}
17375
17376#undef minusc
17377#undef curdir
17378#undef physdir
17379#undef arg0
17380#undef commandname
17381#undef g_parsefile
17382#undef basepf
17383#undef nullstr
17384#undef trap
17385static struct globals_misc *
17386globals_misc_copy(void)
17387{
17388 struct globals_misc *p = ash_ptr_to_globals_misc;
17389 struct globals_misc *new = funcblock;
17390
17391 funcblock = (char *) funcblock + sizeof(struct globals_misc);
17392 memcpy(new, p, sizeof(struct globals_misc));
17393
17394 new->minusc = nodeckstrdup(p->minusc);
17395 new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr;
17396 new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr;
17397 new->arg0 = nodeckstrdup(p->arg0);
17398 new->commandname = nodeckstrdup(p->commandname);
17399 SAVE_PTR(new->minusc, xasprintf("minusc '%s'", p->minusc ?: "NULL"), FREE);
17400 SAVE_PTR(new->curdir,
17401 xasprintf("curdir '%s'", new->curdir ?: "NULL"), FREE);
17402 SAVE_PTR(new->physdir,
17403 xasprintf("physdir '%s'", new->physdir ?: "NULL"), FREE);
17404 SAVE_PTR(new->arg0, xasprintf("arg0 '%s'", p->arg0 ?: "NULL"), FREE);
17405 SAVE_PTR(new->commandname,
17406 xasprintf("commandname '%s'", p->commandname ?: "NULL"), FREE);
17407 for (int i = 0; i < ARRAY_SIZE(p->trap); i++) {
17408 new->trap[i] = nodeckstrdup(p->trap[i]);
17409 SAVE_PTR(new->trap[i], xasprintf("trap[%d]", i), FREE);
17410 }
17411 new->g_parsefile = NULL;
17412 memset(&new->basepf, 0, sizeof(struct parsefile));
17413 return new;
17414}
17415
17416static struct datasize
17417forkshell_size(struct forkshell *fs)
17418{
17419 struct datasize ds = {0, 0};
17420
17421 ds.funcstringsize += align_len(fs->path);
17422 if (fs->fpid == FS_OPENHERE)
17423 return ds;
17424
17425 ds = globals_misc_size(ds);
17426 ds = globals_var_size(ds);
17427 ds = cmdtable_size(ds);
17428
17429 ds.funcblocksize = calcsize(ds.funcblocksize, fs->n);
17430 ds = argv_size(ds, fs->argv);
17431
17432 if ((ENABLE_ASH_ALIAS || MAX_HISTORY || JOBS_WIN32) &&
17433 fs->fpid != FS_SHELLEXEC) {
17434#if ENABLE_ASH_ALIAS
17435 ds = atab_size(ds);
17436#endif
17437#if MAX_HISTORY
17438 if (line_input_state)
17439 ds = history_size(ds);
17440#endif
17441#if JOBS_WIN32
17442 ds = jobtab_size(ds);
17443#endif
17444 }
17445 return ds;
17446}
17447
17448static void
17449forkshell_copy(struct forkshell *fs, struct forkshell *new)
17450{
17451 memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */
17452
17453 new->path = nodeckstrdup(fs->path);
17454 SAVE_PTR(new->path, xasprintf("path '%s'", fs->path ?: "NULL"), FREE);
17455 if (fs->fpid == FS_OPENHERE)
17456 return;
17457
17458 new->gmp = globals_misc_copy();
17459 new->gvp = globals_var_copy();
17460 new->cmdtable = cmdtable_copy();
17461 SAVE_PTR(new->gmp, "gmp", NO_FREE);
17462 SAVE_PTR(new->gvp, "gvp", NO_FREE);
17463 SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE);
17464
17465 new->n = copynode(fs->n);
17466 new->argv = argv_copy(fs->argv);
17467 SAVE_PTR(new->n, "n", NO_FREE);
17468 SAVE_PTR(new->argv, "argv", NO_FREE);
17469
17470 if ((ENABLE_ASH_ALIAS || MAX_HISTORY || JOBS_WIN32) &&
17471 fs->fpid != FS_SHELLEXEC) {
17472#if ENABLE_ASH_ALIAS
17473 new->atab = atab_copy();
17474 SAVE_PTR(new->atab, "atab", NO_FREE);
17475#endif
17476#if MAX_HISTORY
17477 if (line_input_state) {
17478 new->history = history_copy();
17479 SAVE_PTR(new->history, "history", NO_FREE);
17480 new->cnt_history = line_input_state->cnt_history;
17481 }
17482#endif
17483#if JOBS_WIN32
17484 if (njobs) {
17485 new->jobtab = jobtab_copy();
17486 SAVE_PTR(new->jobtab, "jobtab", NO_FREE);
17487 new->njobs = njobs;
17488 if (curjob) {
17489 new->curjob = new->jobtab + (curjob - jobtab);
17490 SAVE_PTR(new->curjob, "curjob", NO_FREE);
17491 }
17492 }
17493#endif
17494 }
17495}
17496
17497#if FORKSHELL_DEBUG
17498#define NUM_BLOCKS FUNCSTRING
17499enum {GMP, GVP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, JOBTAB, FUNCSTRING};
17500
17501/* fp0 and notes can each be NULL */
17502static void
17503forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
17504{
17505 FILE *fp;
17506 void *lfuncblock;
17507 char *lfuncstring;
17508 char *lrelocate;
17509 char *s;
17510 int count, i, total, bitmapsize;
17511 int size[NUM_BLOCKS];
17512 char *lptr[NUM_BLOCKS+1];
17513 const char *fsname[] = {
17514 "FS_OPENHERE",
17515 "FS_EVALBACKCMD",
17516 "FS_EVALSUBSHELL",
17517 "FS_EVALPIPE",
17518 "FS_SHELLEXEC"
17519 };
17520
17521 if (fp0 != NULL) {
17522 fp = fp0;
17523 }
17524 else {
17525 char name[64];
17526 static int num = 0;
17527
17528 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
17529 if ((fp=fopen(name, "w")) == NULL)
17530 return;
17531 }
17532
17533 bitmapsize = (fs->relocatesize + 7)/8;
17534 total = sizeof(struct forkshell) + fs->funcblocksize +
17535 fs->funcstringsize + bitmapsize;
17536 fprintf(fp, "total size %6d = %d + %d + %d + %d = %d\n",
17537 fs->size + bitmapsize,
17538 (int)sizeof(struct forkshell), fs->funcblocksize,
17539 fs->funcstringsize, bitmapsize, total);
17540
17541 lfuncblock = (char *)(fs + 1);
17542 lfuncstring = (char *)lfuncblock + fs->funcblocksize;
17543 lrelocate = (char *)lfuncstring + fs->funcstringsize;
17544
17545 /* funcblocksize is zero for FS_OPENHERE */
17546 if (fs->funcblocksize != 0) {
17547 /* Depending on the configuration and the type of forkshell
17548 * some items may not be present. */
17549 lptr[FUNCSTRING] = lfuncstring;
17550#if JOBS_WIN32
17551 lptr[JOBTAB] = fs->jobtab ? (char *)fs->jobtab : lptr[FUNCSTRING];
17552#else
17553 lptr[JOBTAB] = lptr[FUNCSTRING];
17554#endif
17555#if MAX_HISTORY
17556 lptr[HISTORY] = fs->history ? (char *)fs->history : lptr[JOBTAB];
17557#else
17558 lptr[HISTORY] = lptr[JOBTAB];
17559#endif
17560 lptr[ATAB] = IF_ASH_ALIAS(fs->atab ? (char *)fs->atab :) lptr[HISTORY];
17561 lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB];
17562 lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV];
17563 lptr[CMDTABLE] = (char *)fs->cmdtable;
17564 lptr[GVP] = (char *)fs->gvp;
17565 lptr[GMP] = (char *)fs->gmp;
17566
17567 fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize);
17568 total = 0;
17569 for (i=0; i<NUM_BLOCKS; ++i) {
17570 size[i] = (int)(lptr[i+1] - lptr[i]);
17571 total += size[i];
17572 fprintf(fp, "%d %c ", size[i], i == NUM_BLOCKS - 1 ? '=' : '+');
17573 }
17574 fprintf(fp, "%d\n\n", total);
17575 }
17576 else {
17577 fprintf(fp, "\n");
17578 }
17579
17580 fprintf(fp, "%s\n\n", fsname[fs->fpid]);
17581 fprintf(fp, "--- relocate ---\n");
17582 count = 0;
17583 for (i = 0; i < fs->relocatesize; ++i) {
17584 if (lrelocate[i/8] & (1 << i % 8)) {
17585 char **ptr = (char **)((char *)fs + i * sizeof(char *));
17586 fprintf(fp, "%p %p %s\n", ptr, *ptr,
17587 notes && notes[i] ? notes[i] : "");
17588 ++count;
17589 }
17590 }
17591 fprintf(fp, "--- %d relocations ---\n\n", count);
17592
17593 fprintf(fp, "--- funcstring ---\n");
17594 count = 0;
17595 s = lfuncstring;
17596 while (s-lfuncstring < fs->funcstringsize) {
17597 if (!*s) {
17598 ++s;
17599 continue;
17600 }
17601 fprintf(fp, "%p '%s'\n", s, s);
17602 s += strlen(s)+1;
17603 ++count;
17604 }
17605 fprintf(fp, "--- %d strings ---\n", count);
17606
17607 if (fp0 == NULL)
17608 fclose(fp);
17609}
17610#endif
17611
17612static struct forkshell *
17613forkshell_prepare(struct forkshell *fs)
17614{
17615 struct forkshell *new;
17616 struct datasize ds;
17617 int size, relocatesize, bitmapsize;
17618 HANDLE h;
17619 SECURITY_ATTRIBUTES sa;
17620#if FORKSHELL_DEBUG
17621 char *relocate;
17622 char name[64];
17623 FILE *fp;
17624 static int num = 0;
17625#endif
17626
17627 /* calculate size of structure, funcblock and funcstring */
17628 ds = forkshell_size(fs);
17629 size = sizeof(struct forkshell) + ds.funcblocksize + ds.funcstringsize;
17630 relocatesize = (sizeof(struct forkshell) + ds.funcblocksize)/sizeof(char *);
17631 bitmapsize = (relocatesize + 7)/8;
17632
17633 /* Allocate shared memory region */
17634 memset(&sa, 0, sizeof(sa));
17635 sa.nLength = sizeof(sa);
17636 sa.lpSecurityDescriptor = NULL;
17637 sa.bInheritHandle = TRUE;
17638 h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0,
17639 size+bitmapsize, NULL);
17640
17641 /* Initialise pointers */
17642 new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
17643 if (new == NULL)
17644 return NULL;
17645 fs_size = size;
17646 fs_start = new;
17647 funcblock = (char *)(new + 1);
17648 funcstring_end = (char *)new + size;
17649#if FORKSHELL_DEBUG
17650 fs_funcstring = (char *)new + sizeof(struct forkshell) + ds.funcblocksize;
17651 relocate = (char *)new + size;
17652 annot = (const char **)xzalloc(sizeof(char *)*relocatesize);
17653 annot_free = xzalloc(relocatesize);
17654#endif
17655
17656 /* Now pack them all */
17657 forkshell_copy(fs, new);
17658
17659 /* Finish it up */
17660 new->size = size;
17661 new->relocatesize = relocatesize;
17662 new->old_base = (char *)new;
17663 new->hMapFile = h;
17664#if FORKSHELL_DEBUG
17665 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
17666 if ((fp=fopen(name, "w")) != NULL) {
17667 int i;
17668
17669 new->funcblocksize = (char *)funcblock - (char *)(new + 1);
17670 new->funcstringsize = (char *)new + size - funcstring_end;
17671
17672 /* perform some sanity checks on pointers */
17673 fprintf(fp, "forkshell %p %6d\n", new, (int)sizeof(*new));
17674 fprintf(fp, "funcblock %p %6d\n", new+1, new->funcblocksize);
17675 fprintf(fp, "funcstring %p %6d\n", funcstring_end,
17676 new->funcstringsize);
17677 if ((char *)funcblock != funcstring_end)
17678 fprintf(fp, " funcstring != end funcblock + 1 %p\n", funcblock);
17679 fprintf(fp, "relocate %p %6d\n\n", relocate, bitmapsize);
17680
17681 forkshell_print(fp, new, annot);
17682
17683 for (i = 0; i < relocatesize; ++i) {
17684 if (annot_free[i]) {
17685 free((void *)annot[i]);
17686 }
17687 }
17688 free(annot);
17689 free(annot_free);
17690 annot = NULL;
17691 fclose(fp);
17692 }
17693#endif
17694 return new;
17695}
17696
17697#undef trap_ptr
17698static void
17699forkshell_init(const char *idstr)
17700{
17701 struct forkshell *fs;
17702 void *map_handle;
17703 HANDLE h;
17704 int i;
17705 char **ptr;
17706 char *lrelocate;
17707
17708 if (sscanf(idstr, "%p", &map_handle) != 1)
17709 return;
17710
17711 h = (HANDLE)map_handle;
17712 fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
17713 if (!fs)
17714 return;
17715
17716 /* this memory can't be freed */
17717 sticky_mem_start = fs;
17718 sticky_mem_end = (char *) fs + fs->size;
17719
17720 /* pointer fixup */
17721 lrelocate = (char *)fs + fs->size;
17722 for (i = 0; i < fs->relocatesize; i++) {
17723 if (lrelocate[i/8] & (1 << i % 8)) {
17724 ptr = (char **)((char *)fs + i * sizeof(char *));
17725 if (*ptr)
17726 *ptr = (char *)fs + (*ptr - fs->old_base);
17727 }
17728 }
17729
17730 if (fs->fpid == FS_OPENHERE)
17731 goto end;
17732
17733 /* Now fix up stuff that can't be transferred */
17734 for (i = 0; i < CMDTABLESIZE; i++) {
17735 struct tblentry *e = fs->cmdtable[i];
17736 while (e) {
17737 if (e->cmdtype == CMDBUILTIN)
17738 e->param.cmd = builtintab + e->param.index;
17739 e = e->next;
17740 }
17741 }
17742 fs->gmp->trap_ptr = fs->gmp->trap;
17743
17744 /* from init() */
17745 fs->gmp->basepf.next_to_pgetc = fs->gmp->basepf.buf = ckzalloc(IBUFSIZ);
17746 fs->gmp->basepf.linno = 1;
17747 fs->gmp->g_parsefile = &fs->gmp->basepf;
17748
17749 /* Set global variables */
17750 ASSIGN_CONST_PTR(&ash_ptr_to_globals_misc, fs->gmp);
17751 ASSIGN_CONST_PTR(&ash_ptr_to_globals_var, fs->gvp);
17752 cmdtable = fs->cmdtable;
17753#if ENABLE_ASH_ALIAS
17754 atab = fs->atab; /* will be NULL for FS_SHELLEXEC */
17755#endif
17756#if MAX_HISTORY
17757 if (fs->cnt_history) {
17758 line_input_state = new_line_input_t(FOR_SHELL);
17759 line_input_state->cnt_history = fs->cnt_history;
17760 for (i = 0; i < line_input_state->cnt_history; i++)
17761 line_input_state->history[i] = fs->history[i];
17762 }
17763#endif
17764#if JOBS_WIN32
17765 jobtab = fs->jobtab;
17766 njobs = fs->njobs;
17767 curjob = fs->curjob;
17768#endif
17769
17770 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
17771
17772 reinitvar();
17773
17774 if (setjmp(main_handler.loc)) {
17775 exitreset();
17776 exitshell();
17777 }
17778 exception_handler = &main_handler;
17779
17780 shlvl++;
17781 if (fs->mode == FORK_BG) {
17782 SetConsoleCtrlHandler(NULL, TRUE);
17783 if (fs->nprocs == 0) {
17784 close(0);
17785 if (open(bb_dev_null, O_RDONLY) != 0)
17786 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
17787 }
17788 }
17789 else {
17790 SetConsoleCtrlHandler(ctrl_handler, TRUE);
17791 }
17792
17793 if (fs->n && fs->n->type == NCMD /* is it single cmd? */
17794 /* && n->ncmd.args->type == NARG - always true? */
17795 && fs->n->ncmd.args && strcmp(fs->n->ncmd.args->narg.text, "trap") == 0
17796 && fs->n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
17797 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
17798 ) {
17799 TRACE(("Trap hack\n"));
17800 /* Save trap handler strings for trap builtin to print */
17801 fs->gmp->trap_ptr = xmemdup(fs->gmp->trap, sizeof(fs->gmp->trap));
17802 /* Fall through into clearing traps */
17803 }
17804 clear_traps();
17805#if JOBS_WIN32
17806 /* do job control only in root shell */
17807 jobctl = 0;
17808
17809 if (fs->n && fs->n->type == NCMD && fs->n->ncmd.args &&
17810 strcmp(fs->n->ncmd.args->narg.text, "jobs") == 0) {
17811 TRACE(("Job hack\n"));
17812 if (!fs->jpnull)
17813 freejob(curjob);
17814 goto end;
17815 }
17816 for (struct job *jp = curjob; jp; jp = jp->prev_job)
17817 freejob(jp);
17818#endif
17819 end:
17820 forkshell_child(fs);
17821}
17822
17823#undef free
17824static void
17825sticky_free(void *base)
17826{
17827 if (base >= sticky_mem_start && base < sticky_mem_end)
17828 return;
17829 free(base);
17830}
17831#endif
17832
15077/*- 17833/*-
15078 * Copyright (c) 1989, 1991, 1993, 1994 17834 * Copyright (c) 1989, 1991, 1993, 1994
15079 * The Regents of the University of California. All rights reserved. 17835 * The Regents of the University of California. All rights reserved.