aboutsummaryrefslogtreecommitdiff
path: root/shell/ash.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/ash.c')
-rw-r--r--shell/ash.c2740
1 files changed, 2703 insertions, 37 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 094a87390..719f722a2 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -15,6 +15,21 @@
15 * 15 *
16 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 16 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
17 */ 17 */
18
19/*
20 * MinGW notes
21 *
22 * - Environment variables from Windows will all be turned to uppercase.
23 * - PATH accepts both ; and : as separator, but can't be mixed
24 * - command without ".exe" extension is still understood as executable
25 * - shell scripts on the path are detected by the presence of '#!'
26 * - both / and \ are supported in PATH. Usually you must use /
27 * - job control doesn't work, though the jobs builtin is available
28 * - trap doesn't work for signals, only EXIT
29 * - /dev/null is supported for redirection
30 * - fake $PPID
31 */
32
18//config:config SHELL_ASH 33//config:config SHELL_ASH
19//config: bool #hidden option 34//config: bool #hidden option
20//config: depends on !NOMMU 35//config: depends on !NOMMU
@@ -170,11 +185,36 @@
170//config: you to run the specified command or builtin, 185//config: you to run the specified command or builtin,
171//config: even when there is a function with the same name. 186//config: even when there is a function with the same name.
172//config: 187//config:
188//config:
189//config:config ASH_NOCONSOLE
190//config: bool "'noconsole' option"
191//config: default y
192//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
193//config: help
194//config: Enable support for the 'noconsole' option, which attempts to
195//config: conceal the console normally associated with a command line
196//config: application. This may be useful when running a shell script
197//config: from a GUI application. Also the 'noiconify' option, which
198//config: controls whether the console is iconified or hidden.
199//config:
200//config:config ASH_GLOB_OPTIONS
201//config: bool "Globbing options"
202//config: default y
203//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
204//config: help
205//config: Enable support for options to control globbing:
206//config: - 'nocaseglob' allows case-insensitive filename globbing
207//config: - 'nohiddenglob' allows hidden files to be omitted from globbing
208//config: - 'nohidsysglob' allows hidden system files to be omitted
209//config:
173//config:endif # ash options 210//config:endif # ash options
174 211
175//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 212//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
176// APPLET_ODDNAME:name main location suid_type help 213// 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)) 214//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
215//applet:IF_PLATFORM_MINGW32(
216//applet:IF_SH_IS_ASH( APPLET_ODDNAME(lash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
217//applet:)
178//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash)) 218//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
179 219
180//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o 220//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o
@@ -195,7 +235,17 @@
195 235
196#define PROFILE 0 236#define PROFILE 0
197 237
238/*
239 * Only one of JOBS or JOBS_WIN32 is enabled at a time (or neither).
240 * JOBS_WIN32 doesn't enable job control, just some job-related features.
241 */
242#if ENABLE_PLATFORM_MINGW32
243#define JOBS_WIN32 ENABLE_ASH_JOB_CONTROL
244#define JOBS 0
245#else
246#define JOBS_WIN32 0
198#define JOBS ENABLE_ASH_JOB_CONTROL 247#define JOBS ENABLE_ASH_JOB_CONTROL
248#endif
199 249
200#include <fnmatch.h> 250#include <fnmatch.h>
201#include <sys/times.h> 251#include <sys/times.h>
@@ -206,6 +256,10 @@
206#else 256#else
207# define NUM_SCRIPTS 0 257# define NUM_SCRIPTS 0
208#endif 258#endif
259#if ENABLE_PLATFORM_MINGW32
260# include <conio.h>
261# include "lazyload.h"
262#endif
209 263
210/* So far, all bash compat is controlled by one config option */ 264/* So far, all bash compat is controlled by one config option */
211/* Separate defines document which part of code implements what */ 265/* Separate defines document which part of code implements what */
@@ -316,10 +370,89 @@ typedef long arith_t;
316# define unlikely(cond) (cond) 370# define unlikely(cond) (cond)
317#endif 371#endif
318 372
373#if !ENABLE_PLATFORM_MINGW32
374# define is_relative_path(path) ((path)[0] != '/')
375#endif
376
319#if !BB_MMU 377#if !BB_MMU
320# error "Do not even bother, ash will not run on NOMMU machine" 378# error "Do not even bother, ash will not run on NOMMU machine"
321#endif 379#endif
322 380
381#if ENABLE_PLATFORM_MINGW32
382# define FORKSHELL_DEBUG 0
383
384union node;
385struct strlist;
386struct job;
387
388struct forkshell {
389 /* filled by forkshell_copy() */
390 struct globals_var *gvp;
391 struct globals_misc *gmp;
392 struct tblentry **cmdtable;
393#if ENABLE_ASH_ALIAS
394 struct alias **atab;
395#endif
396#if MAX_HISTORY
397 char **history;
398 int cnt_history;
399#endif
400#if JOBS_WIN32
401 struct job *jobtab;
402 unsigned njobs;
403 struct job *curjob;
404#endif
405 /* struct parsefile *g_parsefile; */
406 HANDLE hMapFile;
407 char *old_base;
408 int size;
409# if FORKSHELL_DEBUG
410 int funcblocksize;
411 int funcstringsize;
412# endif
413 int relocatesize;
414
415 /* type of forkshell */
416 int fpid;
417
418 /* generic data, used by forkshell_child */
419 int mode;
420 int nprocs;
421#if JOBS_WIN32
422 int jpnull;
423#endif
424
425 /* optional data, used by forkshell_child */
426 int flags;
427 int fd[3];
428 union node *n;
429 char **argv;
430 char *path;
431};
432
433enum {
434 FS_OPENHERE,
435 FS_EVALBACKCMD,
436 FS_EVALSUBSHELL,
437 FS_EVALPIPE,
438 FS_SHELLEXEC
439};
440
441static struct forkshell* forkshell_prepare(struct forkshell *fs);
442static void forkshell_init(const char *idstr);
443static void *sticky_mem_start, *sticky_mem_end;
444static void sticky_free(void *p);
445# define free(p) sticky_free(p)
446#if !JOBS && !JOBS_WIN32
447#define spawn_forkshell(fs, jp, n, mode) spawn_forkshell(fs, jp, mode)
448#endif
449static void spawn_forkshell(struct forkshell *fs, struct job *jp,
450 union node *n, int mode);
451# if FORKSHELL_DEBUG
452static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes);
453# endif
454#endif
455
323/* ============ Hash table sizes. Configurable. */ 456/* ============ Hash table sizes. Configurable. */
324 457
325#define VTABSIZE 39 458#define VTABSIZE 39
@@ -347,7 +480,11 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
347 "m" "monitor", 480 "m" "monitor",
348 "n" "noexec", 481 "n" "noexec",
349/* Ditto: bash has no "set -s", "set -c" */ 482/* Ditto: bash has no "set -s", "set -c" */
483#if !ENABLE_PLATFORM_MINGW32
350 "s" "", 484 "s" "",
485#else
486 "s" "stdin",
487#endif
351 "c" "", 488 "c" "",
352 "x" "xtrace", 489 "x" "xtrace",
353 "v" "verbose", 490 "v" "verbose",
@@ -364,6 +501,18 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
364 ,"\0" "nolog" 501 ,"\0" "nolog"
365 ,"\0" "debug" 502 ,"\0" "debug"
366#endif 503#endif
504#if ENABLE_PLATFORM_MINGW32
505 ,"X" "winxp"
506#endif
507#if ENABLE_ASH_NOCONSOLE
508 ,"\0" "noconsole"
509 ,"\0" "noiconify"
510#endif
511#if ENABLE_ASH_GLOB_OPTIONS
512 ,"\0" "nocaseglob"
513 ,"\0" "nohiddenglob"
514 ,"\0" "nohidsysglob"
515#endif
367}; 516};
368//bash 4.4.23 also has these opts (with these defaults): 517//bash 4.4.23 also has these opts (with these defaults):
369//braceexpand on 518//braceexpand on
@@ -406,21 +555,36 @@ struct jmploc {
406struct globals_misc { 555struct globals_misc {
407 uint8_t exitstatus; /* exit status of last command */ 556 uint8_t exitstatus; /* exit status of last command */
408 uint8_t back_exitstatus;/* exit status of backquoted command */ 557 uint8_t back_exitstatus;/* exit status of backquoted command */
558#if !ENABLE_PLATFORM_MINGW32
409 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ 559 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
560#endif
410 smallint inps4; /* Prevent PS4 nesting. */ 561 smallint inps4; /* Prevent PS4 nesting. */
411 int savestatus; /* exit status of last command outside traps */ 562 int savestatus; /* exit status of last command outside traps */
412 int rootpid; /* pid of main shell */ 563 int rootpid; /* pid of main shell */
413 /* shell level: 0 for the main shell, 1 for its children, and so on */ 564 /* shell level: 0 for the main shell, 1 for its children, and so on */
414 int shlvl; 565 int shlvl;
566#if ENABLE_PLATFORM_MINGW32
567 int loopnest; /* current loop nesting level */
568#endif
415#define rootshell (!shlvl) 569#define rootshell (!shlvl)
416 int errlinno; 570 int errlinno;
417 571
418 char *minusc; /* argument to -c option */ 572 char *minusc; /* argument to -c option */
573#if ENABLE_PLATFORM_MINGW32
574 char *dirarg; /* argument to -d option */
575 char *title; /* argument to -t option */
576#if ENABLE_SUW32
577 int delayexit; /* set by -N option */
578# endif
579#endif
419 580
420 char *curdir; // = nullstr; /* current working directory */ 581 char *curdir; // = nullstr; /* current working directory */
421 char *physdir; // = nullstr; /* physical working directory */ 582 char *physdir; // = nullstr; /* physical working directory */
422 583
423 char *arg0; /* value of $0 */ 584 char *arg0; /* value of $0 */
585#if ENABLE_PLATFORM_MINGW32
586 char *commandname;
587#endif
424 588
425 struct jmploc *exception_handler; 589 struct jmploc *exception_handler;
426 590
@@ -431,8 +595,10 @@ struct globals_misc {
431 * but we do read it async. 595 * but we do read it async.
432 */ 596 */
433 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ 597 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
598#if !ENABLE_PLATFORM_MINGW32
434 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ 599 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
435 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ 600 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
601#endif
436 smallint exception_type; /* kind of exception: */ 602 smallint exception_type; /* kind of exception: */
437#define EXINT 0 /* SIGINT received */ 603#define EXINT 0 /* SIGINT received */
438#define EXERROR 1 /* a generic error */ 604#define EXERROR 1 /* a generic error */
@@ -467,8 +633,21 @@ struct globals_misc {
467# define nolog optlist[16 + BASH_PIPEFAIL] 633# define nolog optlist[16 + BASH_PIPEFAIL]
468# define debug optlist[17 + BASH_PIPEFAIL] 634# define debug optlist[17 + BASH_PIPEFAIL]
469#endif 635#endif
636#if ENABLE_PLATFORM_MINGW32
637# define winxp optlist[16 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
638# if ENABLE_ASH_NOCONSOLE
639# define noconsole optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
640# define noiconify optlist[18 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
641# endif
642# if ENABLE_ASH_GLOB_OPTIONS
643# define nocaseglob optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
644# define nohiddenglob optlist[18 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
645# define nohidsysglob optlist[19 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
646# endif
647#endif
470 648
471 /* trap handler commands */ 649 /* trap handler commands */
650#if !ENABLE_PLATFORM_MINGW32
472 /* 651 /*
473 * Sigmode records the current value of the signal handlers for the various 652 * Sigmode records the current value of the signal handlers for the various
474 * modes. A value of zero means that the current handler is not known. 653 * modes. A value of zero means that the current handler is not known.
@@ -482,6 +661,7 @@ struct globals_misc {
482 661
483 /* indicates specified signal received */ 662 /* indicates specified signal received */
484 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ 663 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
664#endif
485 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ 665 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
486 char *trap[NSIG + 1]; 666 char *trap[NSIG + 1];
487/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */ 667/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
@@ -506,10 +686,21 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
506#define rootpid (G_misc.rootpid ) 686#define rootpid (G_misc.rootpid )
507#define shlvl (G_misc.shlvl ) 687#define shlvl (G_misc.shlvl )
508#define errlinno (G_misc.errlinno ) 688#define errlinno (G_misc.errlinno )
689#if ENABLE_PLATFORM_MINGW32
690#define loopnest (G_misc.loopnest )
691#endif
509#define minusc (G_misc.minusc ) 692#define minusc (G_misc.minusc )
693#if ENABLE_PLATFORM_MINGW32
694#define dirarg (G_misc.dirarg )
695#define title (G_misc.title )
696#define delayexit (G_misc.delayexit )
697#endif
510#define curdir (G_misc.curdir ) 698#define curdir (G_misc.curdir )
511#define physdir (G_misc.physdir ) 699#define physdir (G_misc.physdir )
512#define arg0 (G_misc.arg0 ) 700#define arg0 (G_misc.arg0 )
701#if ENABLE_PLATFORM_MINGW32
702#define commandname (G_misc.commandname)
703#endif
513#define exception_handler (G_misc.exception_handler) 704#define exception_handler (G_misc.exception_handler)
514#define exception_type (G_misc.exception_type ) 705#define exception_type (G_misc.exception_type )
515#define suppress_int (G_misc.suppress_int ) 706#define suppress_int (G_misc.suppress_int )
@@ -525,6 +716,13 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
525#define trap_ptr (G_misc.trap_ptr ) 716#define trap_ptr (G_misc.trap_ptr )
526#define random_gen (G_misc.random_gen ) 717#define random_gen (G_misc.random_gen )
527#define backgndpid (G_misc.backgndpid ) 718#define backgndpid (G_misc.backgndpid )
719
720#if ENABLE_PLATFORM_MINGW32
721#undef got_sigchld
722#undef pending_sig
723#define pending_sig (0)
724#endif
725
528#define INIT_G_misc() do { \ 726#define INIT_G_misc() do { \
529 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \ 727 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \
530 savestatus = -1; \ 728 savestatus = -1; \
@@ -540,6 +738,9 @@ static void trace_printf(const char *fmt, ...);
540static void trace_vprintf(const char *fmt, va_list va); 738static void trace_vprintf(const char *fmt, va_list va);
541# define TRACE(param) trace_printf param 739# define TRACE(param) trace_printf param
542# define TRACEV(param) trace_vprintf param 740# define TRACEV(param) trace_vprintf param
741# if ENABLE_PLATFORM_MINGW32 && defined(close)
742# undef close
743# endif
543# define close(fd) do { \ 744# define close(fd) do { \
544 int dfd = (fd); \ 745 int dfd = (fd); \
545 if (close(dfd) < 0) \ 746 if (close(dfd) < 0) \
@@ -629,11 +830,18 @@ struct parsefile {
629 830
630 /* Number of outstanding calls to pungetc. */ 831 /* Number of outstanding calls to pungetc. */
631 int unget; 832 int unget;
833
834#if ENABLE_PLATFORM_MINGW32
835 /* True if a trailing CR from a previous read was left unprocessed. */
836 int cr;
837#endif
632}; 838};
633 839
634static struct parsefile basepf; /* top level input file */ 840static struct parsefile basepf; /* top level input file */
635static struct parsefile *g_parsefile = &basepf; /* current input file */ 841static struct parsefile *g_parsefile = &basepf; /* current input file */
842#if ENABLE_PLATFORM_POSIX
636static char *commandname; /* currently executing command */ 843static char *commandname; /* currently executing command */
844#endif
637 845
638 846
639/* ============ Interrupts / exceptions */ 847/* ============ Interrupts / exceptions */
@@ -690,21 +898,39 @@ raise_exception(int e)
690 * are held using the INT_OFF macro. (The test for iflag is just 898 * are held using the INT_OFF macro. (The test for iflag is just
691 * defensive programming.) 899 * defensive programming.)
692 */ 900 */
693static void raise_interrupt(void) NORETURN; 901static void raise_interrupt(void) IF_NOT_PLATFORM_MINGW32(NORETURN);
694static void 902static void
695raise_interrupt(void) 903raise_interrupt(void)
696{ 904{
905#if ENABLE_PLATFORM_MINGW32
906 /* Contrary to the comment above on Windows raise_interrupt() is
907 * called when SIGINT is trapped or ignored. We detect this here
908 * and return without doing anything. */
909 if (trap[SIGINT])
910 return;
911#endif
697 pending_int = 0; 912 pending_int = 0;
913#if !ENABLE_PLATFORM_MINGW32
698 /* Signal is not automatically unmasked after it is raised, 914 /* Signal is not automatically unmasked after it is raised,
699 * do it ourself - unmask all signals */ 915 * do it ourself - unmask all signals */
700 sigprocmask_allsigs(SIG_UNBLOCK); 916 sigprocmask_allsigs(SIG_UNBLOCK);
917#endif
701 /* pending_sig = 0; - now done in signal_handler() */ 918 /* pending_sig = 0; - now done in signal_handler() */
702 919
703 if (!(rootshell && iflag)) { 920 if (!(rootshell && iflag)) {
921#if !ENABLE_PLATFORM_MINGW32
704 /* Kill ourself with SIGINT */ 922 /* Kill ourself with SIGINT */
705 signal(SIGINT, SIG_DFL); 923 signal(SIGINT, SIG_DFL);
706 raise(SIGINT); 924 raise(SIGINT);
925#else
926 fflush_all();
927 _exit(SIGINT << 24);
928#endif
707 } 929 }
930#if ENABLE_PLATFORM_MINGW32
931 if (iflag)
932 write(STDOUT_FILENO, "^C", 2);
933#endif
708 /* bash: ^C even on empty command line sets $? */ 934 /* bash: ^C even on empty command line sets $? */
709 exitstatus = SIGINT + 128; 935 exitstatus = SIGINT + 128;
710 raise_exception(EXINT); 936 raise_exception(EXINT);
@@ -1992,6 +2218,18 @@ maybe_single_quote(const char *s)
1992 return single_quote(s); 2218 return single_quote(s);
1993} 2219}
1994 2220
2221#if ENABLE_PLATFORM_MINGW32
2222/* Copy path to a string on the stack long enough to allow a file extension
2223 * to be added. */
2224static char *
2225stack_add_ext_space(const char *path)
2226{
2227 char *p = growstackto(strlen(path) + 5);
2228 strcpy(p, path);
2229 return p;
2230}
2231#endif
2232
1995 2233
1996/* ============ nextopt */ 2234/* ============ nextopt */
1997 2235
@@ -2114,6 +2352,9 @@ struct localvar {
2114#else 2352#else
2115# define VDYNAMIC 0 2353# define VDYNAMIC 0
2116#endif 2354#endif
2355#if ENABLE_PLATFORM_MINGW32
2356# define VIMPORT 0x400 /* variable was imported from environment */
2357#endif
2117 2358
2118 2359
2119/* Need to be before varinit_data[] */ 2360/* Need to be before varinit_data[] */
@@ -2143,6 +2384,24 @@ static void change_seconds(const char *) FAST_FUNC;
2143static void change_realtime(const char *) FAST_FUNC; 2384static void change_realtime(const char *) FAST_FUNC;
2144#endif 2385#endif
2145 2386
2387#if ENABLE_PLATFORM_MINGW32
2388static void FAST_FUNC
2389change_terminal_mode(const char *newval UNUSED_PARAM)
2390{
2391 terminal_mode(TRUE);
2392}
2393
2394static void clearcmdentry(void);
2395static void FAST_FUNC
2396change_override_applets(const char *newval UNUSED_PARAM)
2397{
2398 clearcmdentry();
2399}
2400
2401# define LINENO_INDEX (5 + 2 * ENABLE_ASH_MAIL + ENABLE_ASH_GETOPTS)
2402# define FUNCNAME_INDEX (LINENO_INDEX + 1)
2403#endif
2404
2146static const struct { 2405static const struct {
2147 int flags; 2406 int flags;
2148 const char *var_text; 2407 const char *var_text;
@@ -2180,6 +2439,12 @@ static const struct {
2180#if ENABLE_FEATURE_EDITING_SAVEHISTORY 2439#if ENABLE_FEATURE_EDITING_SAVEHISTORY
2181 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL }, 2440 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
2182#endif 2441#endif
2442#if ENABLE_PLATFORM_MINGW32
2443 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_SKIP_ANSI_EMULATION, change_terminal_mode },
2444 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_TERMINAL_MODE, change_terminal_mode },
2445 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_OVERRIDE_APPLETS, change_override_applets },
2446 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_CRITICAL_ERROR_DIALOGS, change_critical_error_dialogs },
2447#endif
2183}; 2448};
2184 2449
2185struct redirtab; 2450struct redirtab;
@@ -2401,6 +2666,65 @@ bltinlookup(const char *name)
2401 return lookupvar(name); 2666 return lookupvar(name);
2402} 2667}
2403 2668
2669#if ENABLE_PLATFORM_MINGW32
2670static char *
2671fix_pathvar(const char *path, int len)
2672{
2673 char *newpath = xstrdup(path);
2674 char *p;
2675 int modified = FALSE;
2676
2677 p = newpath + len;
2678 while (*p) {
2679 if (*p != ':' && *p != ';') {
2680 /* skip drive */
2681 if (isalpha(*p) && p[1] == ':')
2682 p += 2;
2683 /* skip through path component */
2684 for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2685 continue;
2686 }
2687 /* *p is ':', ';' or '\0' here */
2688 if (*p == ':') {
2689 *p++ = ';';
2690 modified = TRUE;
2691 }
2692 else if (*p == ';') {
2693 ++p;
2694 }
2695 }
2696
2697 if (!modified) {
2698 free(newpath);
2699 newpath = NULL;
2700 }
2701 return newpath;
2702}
2703
2704#define BB_VAR_EXACT 1 /* exact match for name */
2705#define BB_VAR_ASSIGN -1 /* matches name followed by '=' */
2706
2707/* Match variables that should be placed in the environment immediately
2708 * they're exported and removed immediately they're no longer exported */
2709static int
2710is_bb_var(const char *s)
2711{
2712 const char *p;
2713 int len;
2714
2715 for (p = bbvar; *p; p += len + 1) {
2716 len = strlen(p);
2717 if (strncmp(s, p, len) == 0) {
2718 if (s[len] == '\0')
2719 return BB_VAR_EXACT;
2720 else if (s[len] == '=')
2721 return BB_VAR_ASSIGN;
2722 }
2723 }
2724 return FALSE;
2725}
2726#endif
2727
2404/* 2728/*
2405 * Same as setvar except that the variable and value are passed in 2729 * Same as setvar except that the variable and value are passed in
2406 * the first argument as name=value. Since the first argument will 2730 * the first argument as name=value. Since the first argument will
@@ -2412,6 +2736,26 @@ static struct var *
2412setvareq(char *s, int flags) 2736setvareq(char *s, int flags)
2413{ 2737{
2414 struct var *vp, **vpp; 2738 struct var *vp, **vpp;
2739#if ENABLE_PLATFORM_MINGW32
2740 const char *paths = "PATH=\0""CDPATH=\0""MANPATH=\0";
2741 const char *p;
2742 int len;
2743
2744 for (p = paths; *p; p += len + 1) {
2745 len = strlen(p);
2746 if (strncmp(s, p, len) == 0) {
2747 char *newpath = fix_pathvar(s, len);
2748 if (newpath) {
2749 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2750 free(s);
2751 flags |= VNOSAVE;
2752 flags &= ~(VTEXTFIXED|VSTACK);
2753 s = newpath;
2754 }
2755 break;
2756 }
2757 }
2758#endif
2415 2759
2416 vpp = hashvar(s); 2760 vpp = hashvar(s);
2417 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2761 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
@@ -2437,6 +2781,11 @@ setvareq(char *s, int flags)
2437 if (!(vp->flags & (VTEXTFIXED|VSTACK))) 2781 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2438 free((char*)vp->var_text); 2782 free((char*)vp->var_text);
2439 2783
2784#if ENABLE_PLATFORM_MINGW32
2785 if ((flags & VUNSET) && (vp->flags & VEXPORT) &&
2786 is_bb_var(s) == BB_VAR_EXACT)
2787 unsetenv(s);
2788#endif
2440 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) { 2789 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2441 *vpp = vp->next; 2790 *vpp = vp->next;
2442 free(vp); 2791 free(vp);
@@ -2466,6 +2815,10 @@ setvareq(char *s, int flags)
2466 s = ckstrdup(s); 2815 s = ckstrdup(s);
2467 vp->var_text = s; 2816 vp->var_text = s;
2468 vp->flags = flags; 2817 vp->flags = flags;
2818#if ENABLE_PLATFORM_MINGW32
2819 if ((flags & VEXPORT) && is_bb_var(s) == BB_VAR_ASSIGN)
2820 putenv(s);
2821#endif
2469 2822
2470 out: 2823 out:
2471 return vp; 2824 return vp;
@@ -2587,6 +2940,72 @@ listvars(int on, int off, struct strlist *lp, char ***end)
2587 return grabstackstr(ep); 2940 return grabstackstr(ep);
2588} 2941}
2589 2942
2943#if ENABLE_PLATFORM_MINGW32
2944/* Adjust directory separator in variables imported from the environment */
2945static void
2946setwinxp(int on)
2947{
2948 static smallint is_winxp = 1;
2949 struct var **vpp;
2950 struct var *vp;
2951
2952 if (on == is_winxp)
2953 return;
2954 is_winxp = on;
2955
2956 for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
2957 for (vp = *vpp; vp; vp = vp->next) {
2958 if ((vp->flags & VIMPORT)) {
2959 char *end = strchr(vp->var_text, '=');
2960 if (!end || is_prefixed_with(vp->var_text, "COMSPEC=") ||
2961 is_prefixed_with(vp->var_text, "SYSTEMROOT="))
2962 continue;
2963 if (!on)
2964 bs_to_slash(end + 1);
2965 else
2966 slash_to_bs(end + 1);
2967 }
2968 }
2969 }
2970}
2971
2972# if ENABLE_ASH_NOCONSOLE
2973/*
2974 * Console state is either:
2975 * 0 normal
2976 * 1 iconified/hidden
2977 * 2 unknown
2978 */
2979static int console_state(void)
2980{
2981 DECLARE_PROC_ADDR(BOOL, ShowWindow, HWND, int);
2982
2983 if (INIT_PROC_ADDR(user32.dll, ShowWindow)) {
2984 BOOL state;
2985
2986 if (noiconify) {
2987 state = IsWindowVisible(GetConsoleWindow());
2988 if (state >= 0)
2989 return state == 0;
2990 } else {
2991 state = IsIconic(GetConsoleWindow());
2992 if (state >= 0)
2993 return state != 0;
2994 }
2995 }
2996 return 2;
2997}
2998
2999static void hide_console(int hide)
3000{
3001 // Switch console state if it's known and isn't the required state
3002 if (console_state() == !hide)
3003 ShowWindow(GetConsoleWindow(), hide ?
3004 (noiconify ? SW_HIDE : SW_MINIMIZE) : SW_NORMAL);
3005}
3006# endif
3007#endif
3008
2590 3009
2591/* ============ Path search helper */ 3010/* ============ Path search helper */
2592static const char * 3011static const char *
@@ -2630,7 +3049,7 @@ static const char *pathopt; /* set by padvance */
2630static int 3049static int
2631padvance_magic(const char **path, const char *name, int magic) 3050padvance_magic(const char **path, const char *name, int magic)
2632{ 3051{
2633 const char *term = "%:"; 3052 const char *term = "%"PATH_SEP_STR;
2634 const char *lpathopt; 3053 const char *lpathopt;
2635 const char *p; 3054 const char *p;
2636 char *q; 3055 char *q;
@@ -2647,14 +3066,14 @@ padvance_magic(const char **path, const char *name, int magic)
2647 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) { 3066 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2648 lpathopt = start + 1; 3067 lpathopt = start + 1;
2649 start = p; 3068 start = p;
2650 term = ":"; 3069 term = PATH_SEP_STR;
2651 } 3070 }
2652 3071
2653 len = strcspn(start, term); 3072 len = strcspn(start, term);
2654 p = start + len; 3073 p = start + len;
2655 3074
2656 if (*p == '%') { 3075 if (*p == '%') {
2657 size_t extra = strchrnul(p, ':') - p; 3076 size_t extra = strchrnul(p, PATH_SEP) - p;
2658 3077
2659 if (legal_pathopt(p + 1, term, magic)) 3078 if (legal_pathopt(p + 1, term, magic))
2660 lpathopt = p + 1; 3079 lpathopt = p + 1;
@@ -2665,14 +3084,18 @@ padvance_magic(const char **path, const char *name, int magic)
2665 } 3084 }
2666 3085
2667 pathopt = lpathopt; 3086 pathopt = lpathopt;
2668 *path = *p == ':' ? p + 1 : NULL; 3087 *path = *p == PATH_SEP ? p + 1 : NULL;
2669 3088
2670 /* "2" is for '/' and '\0' */ 3089 /* "2" is for '/' and '\0' */
2671 qlen = len + strlen(name) + 2; 3090 /* reserve space for suffix on WIN32 */
3091 qlen = len + strlen(name) + 2 IF_PLATFORM_MINGW32(+ 4);
2672 q = growstackto(qlen); 3092 q = growstackto(qlen);
2673 3093
2674 if (len) { 3094 if (len) {
2675 q = mempcpy(q, start, len); 3095 q = mempcpy(q, start, len);
3096#if ENABLE_PLATFORM_MINGW32
3097 if (q[-1] != '/' && q[-1] != '\\')
3098#endif
2676 *q++ = '/'; 3099 *q++ = '/';
2677 } 3100 }
2678 strcpy(q, name); 3101 strcpy(q, name);
@@ -2763,6 +3186,7 @@ setprompt_if(smallint do_set, int whichprompt)
2763 3186
2764#define CD_PHYSICAL 1 3187#define CD_PHYSICAL 1
2765#define CD_PRINT 2 3188#define CD_PRINT 2
3189#define CD_PRINT_ALL 4
2766 3190
2767static int 3191static int
2768cdopt(void) 3192cdopt(void)
@@ -2771,7 +3195,14 @@ cdopt(void)
2771 int i, j; 3195 int i, j;
2772 3196
2773 j = 'L'; 3197 j = 'L';
3198#if ENABLE_PLATFORM_MINGW32
3199 while ((i = nextopt("LPa")) != '\0') {
3200 if (i == 'a')
3201 flags |= CD_PRINT_ALL;
3202 else
3203#else
2774 while ((i = nextopt("LP")) != '\0') { 3204 while ((i = nextopt("LP")) != '\0') {
3205#endif
2775 if (i != j) { 3206 if (i != j) {
2776 flags ^= CD_PHYSICAL; 3207 flags ^= CD_PHYSICAL;
2777 j = i; 3208 j = i;
@@ -2788,6 +3219,129 @@ cdopt(void)
2788static const char * 3219static const char *
2789updatepwd(const char *dir) 3220updatepwd(const char *dir)
2790{ 3221{
3222#if ENABLE_PLATFORM_MINGW32
3223 /*
3224 * Due to Windows drive notion, getting pwd is a completely
3225 * different thing. Handle it in a separate routine
3226 */
3227
3228 char *new;
3229 char *p;
3230 char *cdcomppath;
3231 const char *lim;
3232 int len;
3233 char buffer[PATH_MAX];
3234 /*
3235 * There are five cases that make some kind of sense
3236 *
3237 * Absolute paths:
3238 * c:/path
3239 * //host/share
3240 *
3241 * Relative to current working directory of other drive:
3242 * c:path
3243 *
3244 * Relative to current root (drive/share):
3245 * /path
3246 *
3247 * Relative to current working directory of current root (drive/share):
3248 * path
3249 */
3250 enum {ABS_DRIVE, ABS_SHARE, REL_OTHER, REL_ROOT, REL_CWD} target;
3251
3252 /* skip multiple leading separators unless dir is a UNC path */
3253 if (is_dir_sep(*dir) && unc_root_len(dir) == 0) {
3254 while (is_dir_sep(dir[1]))
3255 ++dir;
3256 }
3257
3258 len = strlen(dir);
3259 if (len >= 2 && has_dos_drive_prefix(dir))
3260 target = len >= 3 && is_dir_sep(dir[2]) ? ABS_DRIVE : REL_OTHER;
3261 else if (unc_root_len(dir) != 0)
3262 target = ABS_SHARE;
3263 else if (is_dir_sep(*dir))
3264 target = REL_ROOT;
3265 else
3266 target = REL_CWD;
3267
3268 cdcomppath = sstrdup(dir);
3269 STARTSTACKSTR(new);
3270
3271 switch (target) {
3272 case REL_OTHER:
3273 /* c:path */
3274 if (get_drive_cwd(dir, buffer, PATH_MAX) == NULL)
3275 return 0;
3276 new = stack_putstr(buffer, new);
3277 len = 2;
3278 cdcomppath += len;
3279 dir += len;
3280 break;
3281 case REL_CWD:
3282 case REL_ROOT:
3283 /* path or /path */
3284 len = root_len(curdir);
3285 if (len == 0)
3286 return 0;
3287 new = target == REL_CWD ? stack_putstr(curdir, new) :
3288 stnputs(curdir, len, new);
3289 break;
3290 default:
3291 /* //host/share or c:/path */
3292 len = root_len(dir);
3293 if (len == 0)
3294 return 0;
3295 new = stnputs(dir, len, new);
3296 cdcomppath += len;
3297 dir += len;
3298 break;
3299 }
3300
3301 new = makestrspace(strlen(dir) + 2, new);
3302 lim = (char *)stackblock() + len + 1;
3303
3304 if (!is_dir_sep(*dir)) {
3305 if (!is_dir_sep(new[-1]))
3306 USTPUTC('/', new);
3307 if (new > lim && is_dir_sep(*lim))
3308 lim++;
3309 } else {
3310 USTPUTC('/', new);
3311 cdcomppath++;
3312 if (is_dir_sep(dir[1]) && !is_dir_sep(dir[2])) {
3313 USTPUTC('/', new);
3314 cdcomppath++;
3315 lim++;
3316 }
3317 }
3318 p = strtok(cdcomppath, "/\\");
3319 while (p) {
3320 switch (*p) {
3321 case '.':
3322 if (p[1] == '.' && p[2] == '\0') {
3323 while (new > lim) {
3324 STUNPUTC(new);
3325 if (is_dir_sep(new[-1]))
3326 break;
3327 }
3328 break;
3329 }
3330 if (p[1] == '\0')
3331 break;
3332 /* fall through */
3333 default:
3334 new = stack_putstr(p, new);
3335 USTPUTC('/', new);
3336 }
3337 p = strtok(NULL, "/\\");
3338 }
3339 if (new > lim)
3340 STUNPUTC(new);
3341 *new = 0;
3342 fix_path_case((char *)stackblock());
3343 return bs_to_slash((char *)stackblock());
3344#else
2791 char *new; 3345 char *new;
2792 char *p; 3346 char *p;
2793 char *cdcomppath; 3347 char *cdcomppath;
@@ -2841,6 +3395,7 @@ updatepwd(const char *dir)
2841 STUNPUTC(new); 3395 STUNPUTC(new);
2842 *new = 0; 3396 *new = 0;
2843 return stackblock(); 3397 return stackblock();
3398#endif
2844} 3399}
2845 3400
2846/* 3401/*
@@ -2936,7 +3491,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2936 } 3491 }
2937 if (!dest) 3492 if (!dest)
2938 dest = nullstr; 3493 dest = nullstr;
2939 if (*dest == '/') 3494 if (!is_relative_path(dest))
2940 goto step6; 3495 goto step6;
2941 if (*dest == '.') { 3496 if (*dest == '.') {
2942 c = dest[1]; 3497 c = dest[1];
@@ -2959,7 +3514,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2959 p = stalloc(len); 3514 p = stalloc(len);
2960 3515
2961 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 3516 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2962 if (c && c != ':') 3517 if (c && c != PATH_SEP)
2963 flags |= CD_PRINT; 3518 flags |= CD_PRINT;
2964 docd: 3519 docd:
2965 if (!docd(p, flags)) 3520 if (!docd(p, flags))
@@ -2981,6 +3536,26 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2981 return 0; 3536 return 0;
2982} 3537}
2983 3538
3539#if ENABLE_PLATFORM_MINGW32
3540static void
3541print_all_cwd(void)
3542{
3543 FILE *mnt;
3544 struct mntent *entry;
3545 char buffer[PATH_MAX];
3546
3547 mnt = setmntent(bb_path_mtab_file, "r");
3548 if (mnt) {
3549 while ((entry=getmntent(mnt)) != NULL) {
3550 entry->mnt_dir[2] = '\0';
3551 if (get_drive_cwd(entry->mnt_dir, buffer, PATH_MAX) != NULL)
3552 out1fmt("%s\n", buffer);
3553 }
3554 endmntent(mnt);
3555 }
3556}
3557#endif
3558
2984static int FAST_FUNC 3559static int FAST_FUNC
2985pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 3560pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2986{ 3561{
@@ -2988,6 +3563,12 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2988 const char *dir = curdir; 3563 const char *dir = curdir;
2989 3564
2990 flags = cdopt(); 3565 flags = cdopt();
3566#if ENABLE_PLATFORM_MINGW32
3567 if (flags & CD_PRINT_ALL) {
3568 print_all_cwd();
3569 return 0;
3570 }
3571#endif
2991 if (flags) { 3572 if (flags) {
2992 if (physdir == nullstr) 3573 if (physdir == nullstr)
2993 setpwd(dir, 0); 3574 setpwd(dir, 0);
@@ -3616,7 +4197,12 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3616struct procstat { 4197struct procstat {
3617 pid_t ps_pid; /* process id */ 4198 pid_t ps_pid; /* process id */
3618 int ps_status; /* last process status from wait() */ 4199 int ps_status; /* last process status from wait() */
4200#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3619 char *ps_cmd; /* text of command being run */ 4201 char *ps_cmd; /* text of command being run */
4202#endif
4203#if ENABLE_PLATFORM_MINGW32
4204 HANDLE ps_proc;
4205#endif
3620}; 4206};
3621 4207
3622struct job { 4208struct job {
@@ -3632,8 +4218,10 @@ struct job {
3632#define JOBDONE 2 /* all procs are completed */ 4218#define JOBDONE 2 /* all procs are completed */
3633 unsigned 4219 unsigned
3634 state: 8, 4220 state: 8,
3635#if JOBS 4221#if JOBS || ENABLE_PLATFORM_MINGW32
3636 sigint: 1, /* job was killed by SIGINT */ 4222 sigint: 1, /* job was killed by SIGINT */
4223#endif
4224#if JOBS
3637 jobctl: 1, /* job running under job control */ 4225 jobctl: 1, /* job running under job control */
3638#endif 4226#endif
3639 waited: 1, /* true if this entry has been waited for */ 4227 waited: 1, /* true if this entry has been waited for */
@@ -3643,17 +4231,23 @@ struct job {
3643}; 4231};
3644 4232
3645static struct job *makejob(/*union node *,*/ int); 4233static struct job *makejob(/*union node *,*/ int);
4234#if !ENABLE_PLATFORM_MINGW32
3646static int forkshell(struct job *, union node *, int); 4235static int forkshell(struct job *, union node *, int);
4236#endif
3647static int waitforjob(struct job *); 4237static int waitforjob(struct job *);
3648 4238
3649#if !JOBS 4239#if !JOBS && !JOBS_WIN32
3650enum { doing_jobctl = 0 }; 4240enum { doing_jobctl = 0 };
3651#define setjobctl(on) do {} while (0) 4241#define setjobctl(on) do {} while (0)
3652#else 4242#elif JOBS_WIN32
4243static smallint doing_jobctl; //references:8
4244#define setjobctl(on) do { if (rootshell) doing_jobctl = on; } while (0)
4245#else /* JOBS */
3653static smallint doing_jobctl; //references:8 4246static smallint doing_jobctl; //references:8
3654static void setjobctl(int); 4247static void setjobctl(int);
3655#endif 4248#endif
3656 4249
4250#if !ENABLE_PLATFORM_MINGW32
3657/* 4251/*
3658 * Ignore a signal. 4252 * Ignore a signal.
3659 */ 4253 */
@@ -3802,6 +4396,10 @@ setsignal(int signo)
3802 4396
3803 sigaction_set(signo, &act); 4397 sigaction_set(signo, &act);
3804} 4398}
4399#else
4400#define setsignal(s)
4401#define ignoresig(s)
4402#endif
3805 4403
3806/* mode flags for set_curjob */ 4404/* mode flags for set_curjob */
3807#define CUR_DELETE 2 4405#define CUR_DELETE 2
@@ -3935,7 +4533,7 @@ set_curjob(struct job *jp, unsigned mode)
3935 } 4533 }
3936} 4534}
3937 4535
3938#if JOBS || DEBUG 4536#if JOBS || ENABLE_PLATFORM_MINGW32 || DEBUG
3939static int 4537static int
3940jobno(const struct job *jp) 4538jobno(const struct job *jp)
3941{ 4539{
@@ -3953,7 +4551,9 @@ static struct job *
3953getjob(const char *name, int getctl) 4551getjob(const char *name, int getctl)
3954{ 4552{
3955 struct job *jp; 4553 struct job *jp;
4554#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3956 struct job *found; 4555 struct job *found;
4556#endif
3957 const char *err_msg = "%s: no such job"; 4557 const char *err_msg = "%s: no such job";
3958 unsigned num; 4558 unsigned num;
3959 int c; 4559 int c;
@@ -3998,6 +4598,7 @@ getjob(const char *name, int getctl)
3998 } 4598 }
3999 } 4599 }
4000 4600
4601#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4001 found = NULL; 4602 found = NULL;
4002 while (jp) { 4603 while (jp) {
4003 if (*p == '?' 4604 if (*p == '?'
@@ -4014,6 +4615,9 @@ getjob(const char *name, int getctl)
4014 if (!found) 4615 if (!found)
4015 goto err; 4616 goto err;
4016 jp = found; 4617 jp = found;
4618#else
4619 goto err;
4620#endif
4017 4621
4018 gotit: 4622 gotit:
4019#if JOBS 4623#if JOBS
@@ -4032,14 +4636,18 @@ getjob(const char *name, int getctl)
4032static void 4636static void
4033freejob(struct job *jp) 4637freejob(struct job *jp)
4034{ 4638{
4639#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4035 struct procstat *ps; 4640 struct procstat *ps;
4036 int i; 4641 int i;
4642#endif
4037 4643
4038 INT_OFF; 4644 INT_OFF;
4645#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4039 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { 4646 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
4040 if (ps->ps_cmd != nullstr) 4647 if (ps->ps_cmd != nullstr)
4041 free(ps->ps_cmd); 4648 free(ps->ps_cmd);
4042 } 4649 }
4650#endif
4043 if (jp->ps != &jp->ps0) 4651 if (jp->ps != &jp->ps0)
4044 free(jp->ps); 4652 free(jp->ps);
4045 jp->used = 0; 4653 jp->used = 0;
@@ -4132,7 +4740,9 @@ setjobctl(int on)
4132 ttyfd = fd; 4740 ttyfd = fd;
4133 doing_jobctl = on; 4741 doing_jobctl = on;
4134} 4742}
4743#endif
4135 4744
4745#if JOBS || JOBS_WIN32
4136static int FAST_FUNC 4746static int FAST_FUNC
4137killcmd(int argc, char **argv) 4747killcmd(int argc, char **argv)
4138{ 4748{
@@ -4162,8 +4772,10 @@ killcmd(int argc, char **argv)
4162 * sh -c 'true|sleep 1 & sleep 2; kill %1' 4772 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4163 */ 4773 */
4164 n = jp->nprocs; /* can't be 0 (I hope) */ 4774 n = jp->nprocs; /* can't be 0 (I hope) */
4775#if !ENABLE_PLATFORM_MINGW32
4165 if (jp->jobctl) 4776 if (jp->jobctl)
4166 n = 1; 4777 n = 1;
4778#endif
4167 dst = alloca(n * sizeof(int)*4); 4779 dst = alloca(n * sizeof(int)*4);
4168 argv[i] = dst; 4780 argv[i] = dst;
4169 for (j = 0; j < n; j++) { 4781 for (j = 0; j < n; j++) {
@@ -4178,7 +4790,11 @@ killcmd(int argc, char **argv)
4178 * leading space. Needed to not confuse 4790 * leading space. Needed to not confuse
4179 * negative pids with "kill -SIGNAL_NO" syntax 4791 * negative pids with "kill -SIGNAL_NO" syntax
4180 */ 4792 */
4793#if !ENABLE_PLATFORM_MINGW32
4181 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); 4794 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4795#else
4796 dst += sprintf(dst, " -%u", (int)ps->ps_pid);
4797#endif
4182 } 4798 }
4183 *dst = '\0'; 4799 *dst = '\0';
4184 } 4800 }
@@ -4186,7 +4802,9 @@ killcmd(int argc, char **argv)
4186 } 4802 }
4187 return kill_main(argc, argv); 4803 return kill_main(argc, argv);
4188} 4804}
4805#endif
4189 4806
4807#if JOBS
4190static void 4808static void
4191showpipe(struct job *jp /*, FILE *out*/) 4809showpipe(struct job *jp /*, FILE *out*/)
4192{ 4810{
@@ -4291,6 +4909,78 @@ sprint_status48(char *os, int status, int sigonly)
4291 return s - os; 4909 return s - os;
4292} 4910}
4293 4911
4912#if ENABLE_PLATFORM_MINGW32
4913static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
4914{
4915 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
4916# if ENABLE_FEATURE_EDITING
4917 bb_got_signal = SIGINT; /* for read_line_input: "we got a signal" */
4918# endif
4919 if (!suppress_int && !(rootshell && iflag))
4920 raise_interrupt();
4921 pending_int = 1;
4922 return TRUE;
4923 }
4924 return FALSE;
4925}
4926
4927/*
4928 * Windows does not know about parent-child relationship
4929 * They don't support waitpid(-1)
4930 */
4931static pid_t
4932waitpid_child(int *status, int wait_flags)
4933{
4934 struct job *jb;
4935 struct procstat *ps;
4936 int pid_nr = 0;
4937 pid_t *pidlist;
4938 HANDLE *proclist;
4939 pid_t pid = -1;
4940 DWORD win_status, idx;
4941 int i;
4942
4943 for (jb = curjob; jb; jb = jb->prev_job) {
4944 if (jb->state != JOBDONE)
4945 pid_nr += jb->nprocs;
4946 }
4947 if (pid_nr == 0)
4948 return -1;
4949
4950 pidlist = ckmalloc(sizeof(*pidlist)*pid_nr);
4951 proclist = ckmalloc(sizeof(*proclist)*pid_nr);
4952
4953 pid_nr = 0;
4954 for (jb = curjob; jb; jb = jb->prev_job) {
4955 if (jb->state == JOBDONE)
4956 continue;
4957 ps = jb->ps;
4958 for (i = 0; i < jb->nprocs; ++i) {
4959 if (ps[i].ps_proc) {
4960 pidlist[pid_nr] = ps[i].ps_pid;
4961 proclist[pid_nr++] = ps[i].ps_proc;
4962 }
4963 }
4964 }
4965
4966 if (pid_nr == 0)
4967 goto done;
4968
4969 idx = WaitForMultipleObjects(pid_nr, proclist, FALSE,
4970 wait_flags&WNOHANG ? 1 : INFINITE);
4971 if (idx < pid_nr) {
4972 GetExitCodeProcess(proclist[idx], &win_status);
4973 *status = exit_code_to_wait_status(win_status);
4974 pid = pidlist[idx];
4975 }
4976 done:
4977 free(pidlist);
4978 free(proclist);
4979 return pid;
4980}
4981#define waitpid(p, s, f) waitpid_child(s, f)
4982#endif
4983
4294#define DOWAIT_NONBLOCK 0 4984#define DOWAIT_NONBLOCK 0
4295#define DOWAIT_BLOCK 1 4985#define DOWAIT_BLOCK 1
4296#define DOWAIT_BLOCK_OR_SIG 2 4986#define DOWAIT_BLOCK_OR_SIG 2
@@ -4301,6 +4991,7 @@ sprint_status48(char *os, int status, int sigonly)
4301static int 4991static int
4302waitproc(int block, int *status) 4992waitproc(int block, int *status)
4303{ 4993{
4994#if !ENABLE_PLATFORM_MINGW32
4304 sigset_t oldmask; 4995 sigset_t oldmask;
4305 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; 4996 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4306 int err; 4997 int err;
@@ -4331,6 +5022,11 @@ waitproc(int block, int *status)
4331 } while (got_sigchld); 5022 } while (got_sigchld);
4332 5023
4333 return err; 5024 return err;
5025#else
5026 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
5027 *status = 0;
5028 return waitpid(-1, status, flags);
5029#endif
4334} 5030}
4335 5031
4336static int 5032static int
@@ -4387,6 +5083,11 @@ waitone(int block, struct job *job)
4387 jobno(jp), pid, ps->ps_status, status)); 5083 jobno(jp), pid, ps->ps_status, status));
4388 ps->ps_status = status; 5084 ps->ps_status = status;
4389 thisjob = jp; 5085 thisjob = jp;
5086#if ENABLE_PLATFORM_MINGW32
5087 ps->ps_pid = -1;
5088 CloseHandle(ps->ps_proc);
5089 ps->ps_proc = NULL;
5090#endif
4390 } 5091 }
4391 if (ps->ps_status == -1) 5092 if (ps->ps_status == -1)
4392 jobstate = JOBRUNNING; 5093 jobstate = JOBRUNNING;
@@ -4449,6 +5150,7 @@ waitone(int block, struct job *job)
4449static int 5150static int
4450dowait(int block, struct job *jp) 5151dowait(int block, struct job *jp)
4451{ 5152{
5153#if !ENABLE_PLATFORM_MINGW32
4452 smallint gotchld = *(volatile smallint *)&got_sigchld; 5154 smallint gotchld = *(volatile smallint *)&got_sigchld;
4453 int rpid; 5155 int rpid;
4454 int pid; 5156 int pid;
@@ -4470,9 +5172,17 @@ dowait(int block, struct job *jp)
4470 } while (pid >= 0); 5172 } while (pid >= 0);
4471 5173
4472 return rpid; 5174 return rpid;
5175#else
5176 int pid = 1;
5177
5178 while (jp ? jp->state == JOBRUNNING : pid > 0)
5179 pid = waitone(block, jp);
5180
5181 return pid;
5182#endif
4473} 5183}
4474 5184
4475#if JOBS 5185#if JOBS || JOBS_WIN32
4476static void 5186static void
4477showjob(struct job *jp, int mode) 5187showjob(struct job *jp, int mode)
4478{ 5188{
@@ -4487,7 +5197,7 @@ showjob(struct job *jp, int mode)
4487 5197
4488 if (mode & SHOW_ONLY_PGID) { /* jobs -p */ 5198 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
4489 /* just output process (group) id of pipeline */ 5199 /* just output process (group) id of pipeline */
4490 fprintf(out, "%d\n", ps->ps_pid); 5200 fprintf(out, "%"PID_FMT"d\n", ps->ps_pid);
4491 return; 5201 return;
4492 } 5202 }
4493 5203
@@ -4500,7 +5210,7 @@ showjob(struct job *jp, int mode)
4500 s[col - 3] = '-'; 5210 s[col - 3] = '-';
4501 5211
4502 if (mode & SHOW_PIDS) 5212 if (mode & SHOW_PIDS)
4503 col += fmtstr(s + col, 16, "%d ", ps->ps_pid); 5213 col += fmtstr(s + col, 16, "%"PID_FMT"d ", ps->ps_pid);
4504 5214
4505 psend = ps + jp->nprocs; 5215 psend = ps + jp->nprocs;
4506 5216
@@ -4509,8 +5219,10 @@ showjob(struct job *jp, int mode)
4509 col += sizeof("Running") - 1; 5219 col += sizeof("Running") - 1;
4510 } else { 5220 } else {
4511 int status = psend[-1].ps_status; 5221 int status = psend[-1].ps_status;
5222#if !ENABLE_PLATFORM_MINGW32
4512 if (jp->state == JOBSTOPPED) 5223 if (jp->state == JOBSTOPPED)
4513 status = jp->stopstatus; 5224 status = jp->stopstatus;
5225#endif
4514 col += sprint_status48(s + col, status, 0); 5226 col += sprint_status48(s + col, status, 0);
4515 } 5227 }
4516 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ 5228 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
@@ -4529,14 +5241,18 @@ showjob(struct job *jp, int mode)
4529 s[0] = '\0'; 5241 s[0] = '\0';
4530 col = 33; 5242 col = 33;
4531 if (mode & SHOW_PIDS) 5243 if (mode & SHOW_PIDS)
4532 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; 5244 col = fmtstr(s, 48, "\n%*c%"PID_FMT"d ", indent_col, ' ', ps->ps_pid) - 1;
4533 start: 5245 start:
5246#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4534 fprintf(out, "%s%*c%s%s", 5247 fprintf(out, "%s%*c%s%s",
4535 s, 5248 s,
4536 33 - col >= 0 ? 33 - col : 0, ' ', 5249 33 - col >= 0 ? 33 - col : 0, ' ',
4537 ps == jp->ps ? "" : "| ", 5250 ps == jp->ps ? "" : "| ",
4538 ps->ps_cmd 5251 ps->ps_cmd
4539 ); 5252 );
5253#else
5254 fprintf(out, "%s", s);
5255#endif
4540 } while (++ps != psend); 5256 } while (++ps != psend);
4541 newline_and_flush(out); 5257 newline_and_flush(out);
4542 5258
@@ -4621,7 +5337,7 @@ getstatus(struct job *job)
4621 { 5337 {
4622 /* XXX: limits number of signals */ 5338 /* XXX: limits number of signals */
4623 retval = WTERMSIG(status); 5339 retval = WTERMSIG(status);
4624#if JOBS 5340#if JOBS || ENABLE_PLATFORM_MINGW32
4625 if (retval == SIGINT) 5341 if (retval == SIGINT)
4626 job->sigint = 1; 5342 job->sigint = 1;
4627#endif 5343#endif
@@ -4793,7 +5509,7 @@ makejob(/*union node *node,*/ int nprocs)
4793 break; 5509 break;
4794 if (jp->state != JOBDONE || !jp->waited) 5510 if (jp->state != JOBDONE || !jp->waited)
4795 continue; 5511 continue;
4796#if JOBS 5512#if JOBS || JOBS_WIN32
4797 if (doing_jobctl) 5513 if (doing_jobctl)
4798 continue; 5514 continue;
4799#endif 5515#endif
@@ -4819,7 +5535,7 @@ makejob(/*union node *node,*/ int nprocs)
4819 return jp; 5535 return jp;
4820} 5536}
4821 5537
4822#if JOBS 5538#if JOBS || JOBS_WIN32
4823/* 5539/*
4824 * Return a string identifying a command (to be printed by the 5540 * Return a string identifying a command (to be printed by the
4825 * jobs command). 5541 * jobs command).
@@ -5161,6 +5877,7 @@ clear_traps(void)
5161 INT_ON; 5877 INT_ON;
5162} 5878}
5163 5879
5880#if !ENABLE_PLATFORM_MINGW32
5164/* Lives far away from here, needed for forkchild */ 5881/* Lives far away from here, needed for forkchild */
5165static void closescript(void); 5882static void closescript(void);
5166 5883
@@ -5287,14 +6004,22 @@ forkchild(struct job *jp, union node *n, int mode)
5287 for (jp = curjob; jp; jp = jp->prev_job) 6004 for (jp = curjob; jp; jp = jp->prev_job)
5288 freejob(jp); 6005 freejob(jp);
5289} 6006}
6007#endif
5290 6008
5291/* Called after fork(), in parent */ 6009/* Called after fork(), in parent */
5292#if !JOBS 6010#if !JOBS && !JOBS_WIN32
5293#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) 6011#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5294#endif 6012#endif
5295static void 6013static void
6014#if !ENABLE_PLATFORM_MINGW32
5296forkparent(struct job *jp, union node *n, int mode, pid_t pid) 6015forkparent(struct job *jp, union node *n, int mode, pid_t pid)
6016#else
6017forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
6018#endif
5297{ 6019{
6020#if ENABLE_PLATFORM_MINGW32
6021 pid_t pid = GetProcessId(proc);
6022#endif
5298 TRACE(("In parent shell: child = %d\n", pid)); 6023 TRACE(("In parent shell: child = %d\n", pid));
5299 if (!jp) /* jp is NULL when called by openhere() for heredoc support */ 6024 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
5300 return; 6025 return;
@@ -5313,19 +6038,29 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5313 if (mode == FORK_BG) { 6038 if (mode == FORK_BG) {
5314 backgndpid = pid; /* set $! */ 6039 backgndpid = pid; /* set $! */
5315 set_curjob(jp, CUR_RUNNING); 6040 set_curjob(jp, CUR_RUNNING);
6041#if ENABLE_PLATFORM_MINGW32
6042 if (iflag && jp && jp->nprocs == 0)
6043 fprintf(stderr, "[%d] %"PID_FMT"d\n", jobno(jp), pid);
6044#endif
5316 } 6045 }
5317 if (jp) { 6046 if (jp) {
5318 struct procstat *ps = &jp->ps[jp->nprocs++]; 6047 struct procstat *ps = &jp->ps[jp->nprocs++];
5319 ps->ps_pid = pid; 6048 ps->ps_pid = pid;
5320 ps->ps_status = -1; 6049 ps->ps_status = -1;
6050#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
5321 ps->ps_cmd = nullstr; 6051 ps->ps_cmd = nullstr;
5322#if JOBS 6052#endif
6053#if ENABLE_PLATFORM_MINGW32
6054 ps->ps_proc = proc;
6055#endif
6056#if JOBS || JOBS_WIN32
5323 if (doing_jobctl && n) 6057 if (doing_jobctl && n)
5324 ps->ps_cmd = commandtext(n); 6058 ps->ps_cmd = commandtext(n);
5325#endif 6059#endif
5326 } 6060 }
5327} 6061}
5328 6062
6063#if !ENABLE_PLATFORM_MINGW32
5329/* jp and n are NULL when called by openhere() for heredoc support */ 6064/* jp and n are NULL when called by openhere() for heredoc support */
5330static int 6065static int
5331forkshell(struct job *jp, union node *n, int mode) 6066forkshell(struct job *jp, union node *n, int mode)
@@ -5348,6 +6083,7 @@ forkshell(struct job *jp, union node *n, int mode)
5348 } 6083 }
5349 return pid; 6084 return pid;
5350} 6085}
6086#endif
5351 6087
5352/* 6088/*
5353 * Wait for job to finish. 6089 * Wait for job to finish.
@@ -5411,6 +6147,10 @@ waitforjob(struct job *jp)
5411 return exitstatus; 6147 return exitstatus;
5412 6148
5413 st = getstatus(jp); 6149 st = getstatus(jp);
6150#if ENABLE_PLATFORM_MINGW32
6151 if (!jp->sigint && iflag && rootshell)
6152 pending_int = 0;
6153#endif
5414#if JOBS 6154#if JOBS
5415 if (jp->jobctl) { 6155 if (jp->jobctl) {
5416 xtcsetpgrp(ttyfd, rootpid); 6156 xtcsetpgrp(ttyfd, rootpid);
@@ -5436,6 +6176,7 @@ waitforjob(struct job *jp)
5436/* 6176/*
5437 * return 1 if there are stopped jobs, otherwise 0 6177 * return 1 if there are stopped jobs, otherwise 0
5438 */ 6178 */
6179#if !ENABLE_PLATFORM_MINGW32
5439static int 6180static int
5440stoppedjobs(void) 6181stoppedjobs(void)
5441{ 6182{
@@ -5454,6 +6195,17 @@ stoppedjobs(void)
5454 out: 6195 out:
5455 return retval; 6196 return retval;
5456} 6197}
6198#else
6199static int
6200stoppedjobs(void)
6201{
6202 if (iflag && curjob) {
6203 out2str("You have background jobs.\n");
6204 return 1;
6205 }
6206 return 0;
6207}
6208#endif
5457 6209
5458 6210
5459/* 6211/*
@@ -5478,6 +6230,7 @@ openhere(union node *redir)
5478 char *p; 6230 char *p;
5479 int pip[2]; 6231 int pip[2];
5480 size_t len = 0; 6232 size_t len = 0;
6233 IF_PLATFORM_MINGW32(struct forkshell fs);
5481 6234
5482 if (pipe(pip) < 0) 6235 if (pipe(pip) < 0)
5483 ash_msg_and_raise_perror("can't create pipe"); 6236 ash_msg_and_raise_perror("can't create pipe");
@@ -5494,6 +6247,14 @@ openhere(union node *redir)
5494 goto out; 6247 goto out;
5495 } 6248 }
5496 6249
6250#if ENABLE_PLATFORM_MINGW32
6251 memset(&fs, 0, sizeof(fs));
6252 fs.fpid = FS_OPENHERE;
6253 fs.fd[0] = pip[0];
6254 fs.fd[1] = pip[1];
6255 fs.path = p;
6256 spawn_forkshell(&fs, NULL, NULL, FORK_NOJOB);
6257#else
5497 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { 6258 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
5498 /* child */ 6259 /* child */
5499 close(pip[0]); 6260 close(pip[0]);
@@ -5505,6 +6266,7 @@ openhere(union node *redir)
5505 xwrite(pip[1], p, len); 6266 xwrite(pip[1], p, len);
5506 _exit_SUCCESS(); 6267 _exit_SUCCESS();
5507 } 6268 }
6269#endif
5508 out: 6270 out:
5509 close(pip[1]); 6271 close(pip[1]);
5510 return pip[0]; 6272 return pip[0];
@@ -5582,6 +6344,9 @@ openredirect(union node *redir)
5582 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); 6344 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5583 if (f < 0) 6345 if (f < 0)
5584 goto ecreate; 6346 goto ecreate;
6347#if ENABLE_PLATFORM_MINGW32
6348 lseek(f, 0, SEEK_END);
6349#endif
5585 break; 6350 break;
5586 } 6351 }
5587 6352
@@ -6133,6 +6898,9 @@ ifsbreakup(char *string, struct arglist *arglist)
6133 const char *ifs, *realifs; 6898 const char *ifs, *realifs;
6134 int ifsspc; 6899 int ifsspc;
6135 int nulonly; 6900 int nulonly;
6901#if ENABLE_PLATFORM_MINGW32
6902 int lshift = 0;
6903#endif
6136 6904
6137 start = string; 6905 start = string;
6138 if (ifslastp != NULL) { 6906 if (ifslastp != NULL) {
@@ -6143,7 +6911,33 @@ ifsbreakup(char *string, struct arglist *arglist)
6143 do { 6911 do {
6144 int afternul; 6912 int afternul;
6145 6913
6914#if ENABLE_PLATFORM_MINGW32
6915 /* Adjust region offsets for left-shifted string. */
6916 ifsp->begoff -= lshift;
6917 ifsp->endoff -= lshift;
6918#endif
6146 p = string + ifsp->begoff; 6919 p = string + ifsp->begoff;
6920#if ENABLE_PLATFORM_MINGW32
6921 if (ifsp->endoff > ifsp->begoff + 1) {
6922 /* Transform CRLF to LF. Skip regions having zero or
6923 * one characters: they can't contain CRLF. If the
6924 * region shrinks shift the rest of the string left. */
6925 int oldlen = ifsp->endoff - ifsp->begoff;
6926 int newlen = remove_cr(p, oldlen);
6927 int delta = oldlen - newlen;
6928
6929 if (delta > 0) {
6930 char *t = string + ifsp->endoff;
6931 char *s = string + ifsp->endoff - delta;
6932
6933 while (*t)
6934 *s++ = *t++;
6935 *s = '\0';
6936 lshift += delta;
6937 ifsp->endoff -= delta;
6938 }
6939 }
6940#endif
6147 afternul = nulonly; 6941 afternul = nulonly;
6148 nulonly = ifsp->nulonly; 6942 nulonly = ifsp->nulonly;
6149 ifs = nulonly ? nullstr : realifs; 6943 ifs = nulonly ? nullstr : realifs;
@@ -6588,6 +7382,7 @@ evalbackcmd(union node *n, struct backcmd *result
6588 const int ip = 0; 7382 const int ip = 0;
6589 const int ic = 1; 7383 const int ic = 1;
6590#endif 7384#endif
7385 IF_PLATFORM_MINGW32(struct forkshell fs);
6591 7386
6592 result->fd = -1; 7387 result->fd = -1;
6593 result->buf = NULL; 7388 result->buf = NULL;
@@ -6601,6 +7396,15 @@ evalbackcmd(union node *n, struct backcmd *result
6601 ash_msg_and_raise_perror("can't create pipe"); 7396 ash_msg_and_raise_perror("can't create pipe");
6602 /* process substitution uses NULL job, like openhere() */ 7397 /* process substitution uses NULL job, like openhere() */
6603 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; 7398 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;
7399#if ENABLE_PLATFORM_MINGW32
7400 memset(&fs, 0, sizeof(fs));
7401 fs.fpid = FS_EVALBACKCMD;
7402 fs.n = n;
7403 fs.fd[0] = pip[0];
7404 fs.fd[1] = pip[1];
7405 fs.fd[2] = ctl;
7406 spawn_forkshell(&fs, jp, n, FORK_NOJOB);
7407#else
6604 if (forkshell(jp, n, FORK_NOJOB) == 0) { 7408 if (forkshell(jp, n, FORK_NOJOB) == 0) {
6605 /* child */ 7409 /* child */
6606 FORCE_INT_ON; 7410 FORCE_INT_ON;
@@ -6624,6 +7428,7 @@ evalbackcmd(union node *n, struct backcmd *result
6624 evaltreenr(n, EV_EXIT); 7428 evaltreenr(n, EV_EXIT);
6625 /* NOTREACHED */ 7429 /* NOTREACHED */
6626 } 7430 }
7431#endif
6627 /* parent */ 7432 /* parent */
6628#if BASH_PROCESS_SUBST 7433#if BASH_PROCESS_SUBST
6629 if (ctl != CTLBACKQ) { 7434 if (ctl != CTLBACKQ) {
@@ -6702,8 +7507,14 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
6702 7507
6703 /* Eat all trailing newlines */ 7508 /* Eat all trailing newlines */
6704 dest = expdest; 7509 dest = expdest;
6705 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) 7510 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) {
6706 STUNPUTC(dest); 7511 STUNPUTC(dest);
7512#if ENABLE_PLATFORM_MINGW32
7513 if (dest > ((char *)stackblock() + startloc) && dest[-1] == '\r') {
7514 STUNPUTC(dest);
7515 }
7516#endif
7517 }
6707 expdest = dest; 7518 expdest = dest;
6708 7519
6709 if (!(flag & EXP_QUOTED)) 7520 if (!(flag & EXP_QUOTED))
@@ -7850,6 +8661,26 @@ expandmeta(struct strlist *str /*, int flag*/)
7850#else 8661#else
7851/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */ 8662/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
7852 8663
8664#if ENABLE_ASH_GLOB_OPTIONS
8665static int FAST_FUNC
8666ash_accept_glob(const char *name)
8667{
8668 struct stat st;
8669
8670 if (nohiddenglob || nohidsysglob) {
8671 if (!lstat(name, &st)) {
8672 if ((st.st_attr & FILE_ATTRIBUTE_HIDDEN)) {
8673 if (nohiddenglob ||
8674 (st.st_attr & FILE_ATTRIBUTE_SYSTEM)) {
8675 return FALSE;
8676 }
8677 }
8678 }
8679 }
8680 return TRUE;
8681}
8682#endif
8683
7853/* 8684/*
7854 * Do metacharacter (i.e. *, ?, [...]) expansion. 8685 * Do metacharacter (i.e. *, ?, [...]) expansion.
7855 */ 8686 */
@@ -7877,6 +8708,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7877 8708
7878 metaflag = 0; 8709 metaflag = 0;
7879 start = name; 8710 start = name;
8711#if ENABLE_PLATFORM_MINGW32
8712 if (expdir_len == 0 && has_dos_drive_prefix(start) && start[2] != '/')
8713 start += 2;
8714#endif
7880 for (p = name; esc = 0, *p; p += esc + 1) { 8715 for (p = name; esc = 0, *p; p += esc + 1) {
7881 if (*p == '*' || *p == '?') 8716 if (*p == '*' || *p == '?')
7882 metaflag = 1; 8717 metaflag = 1;
@@ -7951,8 +8786,16 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7951 while (!pending_int && (dp = readdir(dirp)) != NULL) { 8786 while (!pending_int && (dp = readdir(dirp)) != NULL) {
7952 if (dp->d_name[0] == '.' && !matchdot) 8787 if (dp->d_name[0] == '.' && !matchdot)
7953 continue; 8788 continue;
8789#if ENABLE_ASH_GLOB_OPTIONS
8790# undef pmatch
8791# define pmatch(a, b) !fnmatch((a), (b), nocaseglob ? FNM_CASEFOLD : 0)
8792#endif
7954 if (pmatch(start, dp->d_name)) { 8793 if (pmatch(start, dp->d_name)) {
7955 if (atend) { 8794 if (atend) {
8795#if ENABLE_ASH_GLOB_OPTIONS
8796 if (!ash_accept_glob(dp->d_name))
8797 continue;
8798#endif
7956 strcpy(enddir, dp->d_name); 8799 strcpy(enddir, dp->d_name);
7957 addfname(expdir); 8800 addfname(expdir);
7958 } else { 8801 } else {
@@ -7980,6 +8823,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7980 endname[-esc - 1] = esc ? '\\' : '/'; 8823 endname[-esc - 1] = esc ? '\\' : '/';
7981#undef expdir 8824#undef expdir
7982#undef expdir_max 8825#undef expdir_max
8826#if ENABLE_ASH_GLOB_OPTIONS
8827# undef pmatch
8828# define pmatch(a, b) !fnmatch((a), (b), 0)
8829#endif
7983} 8830}
7984 8831
7985static struct strlist * 8832static struct strlist *
@@ -8256,10 +9103,34 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8256{ 9103{
8257#if ENABLE_FEATURE_SH_STANDALONE 9104#if ENABLE_FEATURE_SH_STANDALONE
8258 if (applet_no >= 0) { 9105 if (applet_no >= 0) {
9106# if ENABLE_PLATFORM_MINGW32
9107 /* Treat all applets as NOEXEC, including the shell itself if
9108 * this is a FS_SHELLEXEC shell. */
9109 struct forkshell *fs = (struct forkshell *)sticky_mem_start;
9110 if (applet_main[applet_no] != ash_main ||
9111 (fs && fs->fpid == FS_SHELLEXEC)) {
9112 /* mingw-w64's getopt() uses __argv[0] as the program name */
9113 __argv[0] = (char *)cmd;
9114 /* 'which' wants to know if it was invoked from a standalone
9115 * shell. Use the spare element of argv to add a flag, but
9116 * not if the first argument is '--help', that's a special
9117 * case. */
9118 if (strcmp(argv[0], "which") == 0 &&
9119 (argv[1] == NULL || strcmp(argv[1], "--help") != 0)) {
9120 --argv;
9121 argv[0] = argv[1];
9122 argv[1] = (char *)"-s";
9123 }
9124# else
8259 if (APPLET_IS_NOEXEC(applet_no)) { 9125 if (APPLET_IS_NOEXEC(applet_no)) {
9126# endif
9127#if !ENABLE_PLATFORM_MINGW32 || !defined(_UCRT)
9128 /* If building for UCRT move this up into shellexec() to
9129 * work around a bug. */
8260 clearenv(); 9130 clearenv();
8261 while (*envp) 9131 while (*envp)
8262 putenv(*envp++); 9132 putenv(*envp++);
9133#endif
8263 popredir(/*drop:*/ 1); 9134 popredir(/*drop:*/ 1);
8264 run_noexec_applet_and_exit(applet_no, cmd, argv); 9135 run_noexec_applet_and_exit(applet_no, cmd, argv);
8265 } 9136 }
@@ -8270,6 +9141,22 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8270 } 9141 }
8271#endif 9142#endif
8272 9143
9144#if ENABLE_PLATFORM_MINGW32
9145 /* Workaround for libtool, which assumes the host is an MSYS2
9146 * environment and requires special-case escaping for cmd.exe.
9147 * https://github.com/skeeto/w64devkit/issues/50 */
9148 if (string_array_len(argv) >= 3 &&
9149 strcmp(argv[0], "cmd") == 0 &&
9150 strcmp(argv[1], "//c") == 0 &&
9151 strcmp(argv[2], "echo") == 0) {
9152 argv[1]++; /* drop extra slash */
9153 }
9154
9155 /* cmd was allocated on the stack with room for an extension */
9156 add_win32_extension((char *)cmd);
9157 execve(cmd, argv, envp);
9158 /* skip POSIX-mandated retry on ENOEXEC */
9159#else /* !ENABLE_PLATFORM_MINGW32 */
8273 repeat: 9160 repeat:
8274#ifdef SYSV 9161#ifdef SYSV
8275 do { 9162 do {
@@ -8305,6 +9192,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8305 argv[0] = (char*) "ash"; 9192 argv[0] = (char*) "ash";
8306 goto repeat; 9193 goto repeat;
8307 } 9194 }
9195#endif /* !ENABLE_PLATFORM_MINGW32 */
8308} 9196}
8309 9197
8310/* 9198/*
@@ -8312,6 +9200,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8312 * have to change the find_command routine as well. 9200 * have to change the find_command routine as well.
8313 * argv[-1] must exist and be writable! See tryexec() for why. 9201 * argv[-1] must exist and be writable! See tryexec() for why.
8314 */ 9202 */
9203static struct builtincmd *find_builtin(const char *name);
8315static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; 9204static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
8316static void shellexec(char *prog, char **argv, const char *path, int idx) 9205static void shellexec(char *prog, char **argv, const char *path, int idx)
8317{ 9206{
@@ -8322,12 +9211,29 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8322 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ 9211 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
8323 9212
8324 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 9213 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
9214#if ENABLE_FEATURE_SH_STANDALONE && ENABLE_PLATFORM_MINGW32 && defined(_UCRT)
9215 /* Avoid UCRT bug by updating parent's environment and passing a
9216 * NULL environment pointer to execve(). */
9217 clearenv();
9218 while (*envp)
9219 putenv(*envp++);
9220 envp = NULL;
9221#endif
9222#if !ENABLE_PLATFORM_MINGW32
8325 if (strchr(prog, '/') != NULL 9223 if (strchr(prog, '/') != NULL
9224#else
9225 if (has_path(prog)
9226#endif
8326#if ENABLE_FEATURE_SH_STANDALONE 9227#if ENABLE_FEATURE_SH_STANDALONE
8327 || (applet_no = find_applet_by_name(prog)) >= 0 9228 || (applet_no = find_applet_by_name_for_sh(prog, path)) >= 0
8328#endif 9229#endif
8329 ) { 9230 ) {
9231#if ENABLE_PLATFORM_MINGW32
9232 char *progext = stack_add_ext_space(prog);
9233 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) progext, argv, envp);
9234#else
8330 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp); 9235 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
9236#endif
8331 if (applet_no >= 0) { 9237 if (applet_no >= 0) {
8332 /* We tried execing ourself, but it didn't work. 9238 /* We tried execing ourself, but it didn't work.
8333 * Maybe /proc/self/exe doesn't exist? 9239 * Maybe /proc/self/exe doesn't exist?
@@ -8336,6 +9242,21 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8336 goto try_PATH; 9242 goto try_PATH;
8337 } 9243 }
8338 e = errno; 9244 e = errno;
9245#if ENABLE_PLATFORM_MINGW32
9246 if (unix_path(prog)) {
9247 const char *name = bb_basename(prog);
9248# if ENABLE_FEATURE_SH_STANDALONE
9249 if ((applet_no = find_applet_by_name_for_sh(name, path)) >= 0) {
9250 tryexec(applet_no, name, argv, envp);
9251 e = errno;
9252 }
9253# endif
9254 if (!find_builtin(name)) {
9255 argv[0] = (char *)name;
9256 goto try_PATH;
9257 }
9258 }
9259#endif
8339 } else { 9260 } else {
8340 try_PATH: 9261 try_PATH:
8341 e = ENOENT; 9262 e = ENOENT;
@@ -8381,6 +9302,9 @@ printentry(struct tblentry *cmdp)
8381 padvance(&path, cmdp->cmdname); 9302 padvance(&path, cmdp->cmdname);
8382 } while (--idx >= 0); 9303 } while (--idx >= 0);
8383 name = stackblock(); 9304 name = stackblock();
9305#if ENABLE_PLATFORM_MINGW32
9306 add_win32_extension(name);
9307#endif
8384 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); 9308 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8385} 9309}
8386 9310
@@ -8581,7 +9505,7 @@ changepath(const char *newval)
8581 bltin = idx; 9505 bltin = idx;
8582 break; 9506 break;
8583 } 9507 }
8584 new = strchr(new, ':'); 9508 new = strchr(new, PATH_SEP);
8585 if (!new) 9509 if (!new)
8586 break; 9510 break;
8587 idx++; 9511 idx++;
@@ -8758,14 +9682,37 @@ describe_command(char *command, const char *path, int describe_command_verbose)
8758 case CMDNORMAL: { 9682 case CMDNORMAL: {
8759 int j = entry.u.index; 9683 int j = entry.u.index;
8760 char *p; 9684 char *p;
9685#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
9686 if (j < -1) {
9687 p = (char *)bb_basename(command);
9688 if (describe_command_verbose) {
9689 out1fmt(" is a builtin applet");
9690 } else {
9691 out1str(applet_to_exe(p));
9692 }
9693 break;
9694 }
9695#endif
8761 if (j < 0) { 9696 if (j < 0) {
9697#if ENABLE_PLATFORM_MINGW32
9698 p = stack_add_ext_space(command);
9699#else
8762 p = command; 9700 p = command;
9701#endif
8763 } else { 9702 } else {
9703#if ENABLE_PLATFORM_MINGW32
9704 if (unix_path(command))
9705 command = (char *)bb_basename(command);
9706#endif
8764 do { 9707 do {
8765 padvance(&path, command); 9708 padvance(&path, command);
8766 } while (--j >= 0); 9709 } while (--j >= 0);
8767 p = stackblock(); 9710 p = stackblock();
8768 } 9711 }
9712#if ENABLE_PLATFORM_MINGW32
9713 add_win32_extension(p);
9714 bs_to_slash(p);
9715#endif
8769 if (describe_command_verbose) { 9716 if (describe_command_verbose) {
8770 out1fmt(" is %s", p); 9717 out1fmt(" is %s", p);
8771 } else { 9718 } else {
@@ -8921,6 +9868,15 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8921/*static int funcstringsize; // size of strings in node */ 9868/*static int funcstringsize; // size of strings in node */
8922static void *funcblock; /* block to allocate function from */ 9869static void *funcblock; /* block to allocate function from */
8923static char *funcstring_end; /* end of block to allocate strings from */ 9870static char *funcstring_end; /* end of block to allocate strings from */
9871#if ENABLE_PLATFORM_MINGW32
9872static int fs_size;
9873static void *fs_start;
9874# if FORKSHELL_DEBUG
9875static void *fs_funcstring;
9876static const char **annot;
9877static char *annot_free;
9878# endif
9879#endif
8924 9880
8925static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 9881static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8926 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 9882 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -9053,14 +10009,79 @@ calcsize(int funcblocksize, union node *n)
9053} 10009}
9054 10010
9055static char * 10011static char *
9056nodeckstrdup(char *s) 10012nodeckstrdup(const char *s)
9057{ 10013{
10014#if ENABLE_PLATFORM_MINGW32
10015 if(!s)
10016 return NULL;
10017#endif
9058 funcstring_end -= SHELL_ALIGN(strlen(s) + 1); 10018 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
9059 return strcpy(funcstring_end, s); 10019 return strcpy(funcstring_end, s);
9060} 10020}
9061 10021
9062static union node *copynode(union node *); 10022static union node *copynode(union node *);
9063 10023
10024#if ENABLE_PLATFORM_MINGW32
10025# if FORKSHELL_DEBUG
10026# define MARK_PTR(dst,note,flag) forkshell_mark_ptr((void *)&dst, note, flag)
10027# else
10028# define MARK_PTR(dst,note,flag) forkshell_mark_ptr((void *)&dst)
10029# endif
10030
10031#define NO_FREE 0
10032#define FREE 1
10033
10034#if FORKSHELL_DEBUG
10035static void forkshell_mark_ptr(void *dst, const char *note, int flag)
10036#else
10037static void forkshell_mark_ptr(void *dst)
10038#endif
10039{
10040 char *lrelocate = (char *)fs_start + fs_size;
10041 int index = ((char *)dst - (char *)fs_start)/sizeof(char *);
10042
10043 lrelocate[index/8] |= 1 << (index % 8);
10044
10045#if FORKSHELL_DEBUG
10046 if (dst < fs_start || dst >= fs_funcstring) {
10047 fprintf(stderr, "dst (%p) out of range (%p %p)\n",
10048 dst, fs_start, fs_funcstring);
10049 }
10050 if (annot) {
10051 if (annot[index]) {
10052 fprintf(stderr, "duplicate annotation: %s %s\n",
10053 annot[index], note);
10054 }
10055 annot[index] = note;
10056 annot_free[index] = flag;
10057 }
10058#endif
10059}
10060
10061# define SAVE_PTR(dst,note,flag) { \
10062 if (fs_size) { \
10063 MARK_PTR(dst,note,flag); \
10064 } \
10065}
10066# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2) { \
10067 if (fs_size) { \
10068 MARK_PTR(dst1,note1,flag1); \
10069 MARK_PTR(dst2,note2,flag2); \
10070 } \
10071}
10072# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3) { \
10073 if (fs_size) { \
10074 MARK_PTR(dst1,note1,flag1); \
10075 MARK_PTR(dst2,note2,flag2); \
10076 MARK_PTR(dst3,note3,flag3); \
10077 } \
10078}
10079#else
10080# define SAVE_PTR(dst,note,flag)
10081# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2)
10082# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3)
10083#endif
10084
9064static struct nodelist * 10085static struct nodelist *
9065copynodelist(struct nodelist *lp) 10086copynodelist(struct nodelist *lp)
9066{ 10087{
@@ -9072,6 +10093,8 @@ copynodelist(struct nodelist *lp)
9072 *lpp = funcblock; 10093 *lpp = funcblock;
9073 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); 10094 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
9074 (*lpp)->n = copynode(lp->n); 10095 (*lpp)->n = copynode(lp->n);
10096 SAVE_PTR2((*lpp)->n, "(*lpp)->next", NO_FREE,
10097 (*lpp)->next, "(*lpp)->next", NO_FREE);
9075 lp = lp->next; 10098 lp = lp->next;
9076 lpp = &(*lpp)->next; 10099 lpp = &(*lpp)->next;
9077 } 10100 }
@@ -9095,10 +10118,14 @@ copynode(union node *n)
9095 new->ncmd.args = copynode(n->ncmd.args); 10118 new->ncmd.args = copynode(n->ncmd.args);
9096 new->ncmd.assign = copynode(n->ncmd.assign); 10119 new->ncmd.assign = copynode(n->ncmd.assign);
9097 new->ncmd.linno = n->ncmd.linno; 10120 new->ncmd.linno = n->ncmd.linno;
10121 SAVE_PTR3(new->ncmd.redirect, "ncmd.redirect", NO_FREE,
10122 new->ncmd.args, "ncmd.args", NO_FREE,
10123 new->ncmd.assign, "ncmd.assign", NO_FREE);
9098 break; 10124 break;
9099 case NPIPE: 10125 case NPIPE:
9100 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); 10126 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
9101 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; 10127 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
10128 SAVE_PTR(new->npipe.cmdlist, "npipe.cmdlist", NO_FREE);
9102 break; 10129 break;
9103 case NREDIR: 10130 case NREDIR:
9104 case NBACKGND: 10131 case NBACKGND:
@@ -9106,6 +10133,8 @@ copynode(union node *n)
9106 new->nredir.redirect = copynode(n->nredir.redirect); 10133 new->nredir.redirect = copynode(n->nredir.redirect);
9107 new->nredir.n = copynode(n->nredir.n); 10134 new->nredir.n = copynode(n->nredir.n);
9108 new->nredir.linno = n->nredir.linno; 10135 new->nredir.linno = n->nredir.linno;
10136 SAVE_PTR2(new->nredir.redirect, "nredir.redirect", NO_FREE,
10137 new->nredir.n, "nredir.n", NO_FREE);
9109 break; 10138 break;
9110 case NAND: 10139 case NAND:
9111 case NOR: 10140 case NOR:
@@ -9114,37 +10143,58 @@ copynode(union node *n)
9114 case NUNTIL: 10143 case NUNTIL:
9115 new->nbinary.ch2 = copynode(n->nbinary.ch2); 10144 new->nbinary.ch2 = copynode(n->nbinary.ch2);
9116 new->nbinary.ch1 = copynode(n->nbinary.ch1); 10145 new->nbinary.ch1 = copynode(n->nbinary.ch1);
10146 SAVE_PTR2(new->nbinary.ch1, "nbinary.ch1", NO_FREE,
10147 new->nbinary.ch2, "nbinary.ch2", NO_FREE);
9117 break; 10148 break;
9118 case NIF: 10149 case NIF:
9119 new->nif.elsepart = copynode(n->nif.elsepart); 10150 new->nif.elsepart = copynode(n->nif.elsepart);
9120 new->nif.ifpart = copynode(n->nif.ifpart); 10151 new->nif.ifpart = copynode(n->nif.ifpart);
9121 new->nif.test = copynode(n->nif.test); 10152 new->nif.test = copynode(n->nif.test);
10153 SAVE_PTR3(new->nif.elsepart, "nif.elsepart", NO_FREE,
10154 new->nif.ifpart, "nif.ifpart", NO_FREE,
10155 new->nif.test, "nif.test", NO_FREE);
9122 break; 10156 break;
9123 case NFOR: 10157 case NFOR:
9124 new->nfor.var = nodeckstrdup(n->nfor.var); 10158 new->nfor.var = nodeckstrdup(n->nfor.var);
9125 new->nfor.body = copynode(n->nfor.body); 10159 new->nfor.body = copynode(n->nfor.body);
9126 new->nfor.args = copynode(n->nfor.args); 10160 new->nfor.args = copynode(n->nfor.args);
9127 new->nfor.linno = n->nfor.linno; 10161 new->nfor.linno = n->nfor.linno;
10162 SAVE_PTR3(new->nfor.var,
10163 xasprintf("nfor.var '%s'", n->nfor.var ?: "NULL"), FREE,
10164 new->nfor.body, "nfor.body", NO_FREE,
10165 new->nfor.args, "nfor.args", NO_FREE);
9128 break; 10166 break;
9129 case NCASE: 10167 case NCASE:
9130 new->ncase.cases = copynode(n->ncase.cases); 10168 new->ncase.cases = copynode(n->ncase.cases);
9131 new->ncase.expr = copynode(n->ncase.expr); 10169 new->ncase.expr = copynode(n->ncase.expr);
9132 new->ncase.linno = n->ncase.linno; 10170 new->ncase.linno = n->ncase.linno;
10171 SAVE_PTR2(new->ncase.cases, "ncase.cases", NO_FREE,
10172 new->ncase.expr, "ncase.expr", NO_FREE);
9133 break; 10173 break;
9134 case NCLIST: 10174 case NCLIST:
9135 new->nclist.body = copynode(n->nclist.body); 10175 new->nclist.body = copynode(n->nclist.body);
9136 new->nclist.pattern = copynode(n->nclist.pattern); 10176 new->nclist.pattern = copynode(n->nclist.pattern);
9137 new->nclist.next = copynode(n->nclist.next); 10177 new->nclist.next = copynode(n->nclist.next);
10178 SAVE_PTR3(new->nclist.body, "nclist.body", NO_FREE,
10179 new->nclist.pattern, "nclist.pattern", NO_FREE,
10180 new->nclist.next, "nclist.next", NO_FREE);
9138 break; 10181 break;
9139 case NDEFUN: 10182 case NDEFUN:
9140 new->ndefun.body = copynode(n->ndefun.body); 10183 new->ndefun.body = copynode(n->ndefun.body);
9141 new->ndefun.text = nodeckstrdup(n->ndefun.text); 10184 new->ndefun.text = nodeckstrdup(n->ndefun.text);
9142 new->ndefun.linno = n->ndefun.linno; 10185 new->ndefun.linno = n->ndefun.linno;
10186 SAVE_PTR2(new->ndefun.body, "ndefun.body", NO_FREE,
10187 new->ndefun.text,
10188 xasprintf("ndefun.text '%s'", n->ndefun.text ?: "NULL"), FREE);
9143 break; 10189 break;
9144 case NARG: 10190 case NARG:
9145 new->narg.backquote = copynodelist(n->narg.backquote); 10191 new->narg.backquote = copynodelist(n->narg.backquote);
9146 new->narg.text = nodeckstrdup(n->narg.text); 10192 new->narg.text = nodeckstrdup(n->narg.text);
9147 new->narg.next = copynode(n->narg.next); 10193 new->narg.next = copynode(n->narg.next);
10194 SAVE_PTR3(new->narg.backquote, "narg.backquote", NO_FREE,
10195 new->narg.text,
10196 xasprintf("narg.text '%s'", n->narg.text ?: "NULL"), FREE,
10197 new->narg.next, "narg.next", NO_FREE);
9148 break; 10198 break;
9149 case NTO: 10199 case NTO:
9150#if BASH_REDIR_OUTPUT 10200#if BASH_REDIR_OUTPUT
@@ -9157,6 +10207,8 @@ copynode(union node *n)
9157 new->nfile.fname = copynode(n->nfile.fname); 10207 new->nfile.fname = copynode(n->nfile.fname);
9158 new->nfile.fd = n->nfile.fd; 10208 new->nfile.fd = n->nfile.fd;
9159 new->nfile.next = copynode(n->nfile.next); 10209 new->nfile.next = copynode(n->nfile.next);
10210 SAVE_PTR2(new->nfile.fname, "nfile.fname", NO_FREE,
10211 new->nfile.next, "nfile.next", NO_FREE);
9160 break; 10212 break;
9161 case NTOFD: 10213 case NTOFD:
9162 case NFROMFD: 10214 case NFROMFD:
@@ -9164,15 +10216,20 @@ copynode(union node *n)
9164 new->ndup.dupfd = n->ndup.dupfd; 10216 new->ndup.dupfd = n->ndup.dupfd;
9165 new->ndup.fd = n->ndup.fd; 10217 new->ndup.fd = n->ndup.fd;
9166 new->ndup.next = copynode(n->ndup.next); 10218 new->ndup.next = copynode(n->ndup.next);
10219 SAVE_PTR2(new->ndup.vname, "ndup.vname", NO_FREE,
10220 new->ndup.next, "ndup.next", NO_FREE);
9167 break; 10221 break;
9168 case NHERE: 10222 case NHERE:
9169 case NXHERE: 10223 case NXHERE:
9170 new->nhere.doc = copynode(n->nhere.doc); 10224 new->nhere.doc = copynode(n->nhere.doc);
9171 new->nhere.fd = n->nhere.fd; 10225 new->nhere.fd = n->nhere.fd;
9172 new->nhere.next = copynode(n->nhere.next); 10226 new->nhere.next = copynode(n->nhere.next);
10227 SAVE_PTR2(new->nhere.doc, "nhere.doc", NO_FREE,
10228 new->nhere.next, "nhere.next", NO_FREE);
9173 break; 10229 break;
9174 case NNOT: 10230 case NNOT:
9175 new->nnot.com = copynode(n->nnot.com); 10231 new->nnot.com = copynode(n->nnot.com);
10232 SAVE_PTR(new->nnot.com, "nnot.com", NO_FREE);
9176 break; 10233 break;
9177 }; 10234 };
9178 new->type = n->type; 10235 new->type = n->type;
@@ -9193,6 +10250,7 @@ copyfunc(union node *n)
9193 f = ckzalloc(blocksize /* + funcstringsize */); 10250 f = ckzalloc(blocksize /* + funcstringsize */);
9194 funcblock = (char *) f + offsetof(struct funcnode, n); 10251 funcblock = (char *) f + offsetof(struct funcnode, n);
9195 funcstring_end = (char *) f + blocksize; 10252 funcstring_end = (char *) f + blocksize;
10253 IF_PLATFORM_MINGW32(fs_size = 0);
9196 copynode(n); 10254 copynode(n);
9197 /* f->count = 0; - ckzalloc did it */ 10255 /* f->count = 0; - ckzalloc did it */
9198 return f; 10256 return f;
@@ -9220,12 +10278,15 @@ defun(union node *func)
9220#define SKIPFUNCDEF (1 << 3) 10278#define SKIPFUNCDEF (1 << 3)
9221static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 10279static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
9222static int skipcount; /* number of levels to skip */ 10280static int skipcount; /* number of levels to skip */
10281#if ENABLE_PLATFORM_POSIX
9223static int loopnest; /* current loop nesting level */ 10282static int loopnest; /* current loop nesting level */
10283#endif
9224static int funcline; /* starting line number of current function, or 0 if not in a function */ 10284static int funcline; /* starting line number of current function, or 0 if not in a function */
9225 10285
9226/* Forward decl way out to parsing code - dotrap needs it */ 10286/* Forward decl way out to parsing code - dotrap needs it */
9227static int evalstring(char *s, int flags); 10287static int evalstring(char *s, int flags);
9228 10288
10289#if !ENABLE_PLATFORM_MINGW32
9229/* Called to execute a trap. 10290/* Called to execute a trap.
9230 * Single callsite - at the end of evaltree(). 10291 * Single callsite - at the end of evaltree().
9231 * If we return non-zero, evaltree raises EXEXIT exception. 10292 * If we return non-zero, evaltree raises EXEXIT exception.
@@ -9284,6 +10345,45 @@ dotrap(void)
9284 savestatus = last_status; 10345 savestatus = last_status;
9285 TRACE(("dotrap returns\n")); 10346 TRACE(("dotrap returns\n"));
9286} 10347}
10348#else
10349static void
10350dotrap(void)
10351{
10352 int status, last_status;
10353 char *p;
10354
10355 if (!pending_int)
10356 return;
10357
10358 status = savestatus;
10359 last_status = status;
10360 if (status < 0) {
10361 status = exitstatus;
10362 savestatus = status;
10363 }
10364 pending_int = 0;
10365 barrier();
10366
10367 TRACE(("dotrap entered\n"));
10368 if (evalskip) {
10369 pending_int = 1;
10370 return;
10371 }
10372
10373 p = trap[SIGINT];
10374 if (p) {
10375 TRACE(("sig %d is active, will run handler '%s'\n", SIGINT, p));
10376 trap_depth++;
10377 evalstring(p, 0);
10378 trap_depth--;
10379 if (evalskip != SKIPFUNC)
10380 exitstatus = status;
10381 }
10382
10383 savestatus = last_status;
10384 TRACE(("dotrap returns\n"));
10385}
10386#endif
9287 10387
9288/* forward declarations - evaluation is fairly recursive business... */ 10388/* forward declarations - evaluation is fairly recursive business... */
9289static int evalloop(union node *, int); 10389static int evalloop(union node *, int);
@@ -9569,19 +10669,36 @@ evalcase(union node *n, int flags)
9569static int 10669static int
9570evalsubshell(union node *n, int flags) 10670evalsubshell(union node *n, int flags)
9571{ 10671{
10672 IF_PLATFORM_MINGW32(struct forkshell fs;)
9572 struct job *jp; 10673 struct job *jp;
9573 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ 10674 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9574 int status; 10675 int status;
9575 10676
9576 errlinno = lineno = n->nredir.linno; 10677 errlinno = lineno = n->nredir.linno;
9577 10678
10679#if ENABLE_PLATFORM_MINGW32
10680 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) {
10681 expredir(n->nredir.redirect);
10682 redirect(n->nredir.redirect, 0);
10683 evaltreenr(n->nredir.n, flags);
10684 /* never returns */
10685 }
10686#else
9578 expredir(n->nredir.redirect); 10687 expredir(n->nredir.redirect);
9579 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) 10688 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
9580 goto nofork; 10689 goto nofork;
10690#endif
9581 INT_OFF; 10691 INT_OFF;
9582 if (backgnd == FORK_FG) 10692 if (backgnd == FORK_FG)
9583 get_tty_state(); 10693 get_tty_state();
9584 jp = makejob(/*n,*/ 1); 10694 jp = makejob(/*n,*/ 1);
10695#if ENABLE_PLATFORM_MINGW32
10696 memset(&fs, 0, sizeof(fs));
10697 fs.fpid = FS_EVALSUBSHELL;
10698 fs.n = n;
10699 fs.flags = flags;
10700 spawn_forkshell(&fs, jp, n, backgnd);
10701#else
9585 if (forkshell(jp, n, backgnd) == 0) { 10702 if (forkshell(jp, n, backgnd) == 0) {
9586 /* child */ 10703 /* child */
9587 INT_ON; 10704 INT_ON;
@@ -9593,6 +10710,7 @@ evalsubshell(union node *n, int flags)
9593 evaltreenr(n->nredir.n, flags); 10710 evaltreenr(n->nredir.n, flags);
9594 /* never returns */ 10711 /* never returns */
9595 } 10712 }
10713#endif
9596 /* parent */ 10714 /* parent */
9597 status = 0; 10715 status = 0;
9598 if (backgnd == FORK_FG) 10716 if (backgnd == FORK_FG)
@@ -9673,6 +10791,7 @@ expredir(union node *n)
9673static int 10791static int
9674evalpipe(union node *n, int flags) 10792evalpipe(union node *n, int flags)
9675{ 10793{
10794 IF_PLATFORM_MINGW32(struct forkshell fs;)
9676 struct job *jp; 10795 struct job *jp;
9677 struct nodelist *lp; 10796 struct nodelist *lp;
9678 int pipelen; 10797 int pipelen;
@@ -9699,6 +10818,16 @@ evalpipe(union node *n, int flags)
9699 ash_msg_and_raise_perror("can't create pipe"); 10818 ash_msg_and_raise_perror("can't create pipe");
9700 } 10819 }
9701 } 10820 }
10821#if ENABLE_PLATFORM_MINGW32
10822 memset(&fs, 0, sizeof(fs));
10823 fs.fpid = FS_EVALPIPE;
10824 fs.flags = flags;
10825 fs.n = lp->n;
10826 fs.fd[0] = pip[0];
10827 fs.fd[1] = pip[1];
10828 fs.fd[2] = prevfd;
10829 spawn_forkshell(&fs, jp, lp->n, n->npipe.pipe_backgnd);
10830#else
9702 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 10831 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
9703 /* child */ 10832 /* child */
9704 INT_ON; 10833 INT_ON;
@@ -9716,6 +10845,7 @@ evalpipe(union node *n, int flags)
9716 evaltreenr(lp->n, flags); 10845 evaltreenr(lp->n, flags);
9717 /* never returns */ 10846 /* never returns */
9718 } 10847 }
10848#endif
9719 /* parent */ 10849 /* parent */
9720 if (prevfd >= 0) 10850 if (prevfd >= 0)
9721 close(prevfd); 10851 close(prevfd);
@@ -9773,6 +10903,9 @@ setinteractive(int on)
9773 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP); 10903 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
9774# if ENABLE_FEATURE_TAB_COMPLETION 10904# if ENABLE_FEATURE_TAB_COMPLETION
9775 line_input_state->get_exe_name = ash_command_name; 10905 line_input_state->get_exe_name = ash_command_name;
10906# if ENABLE_ASH_GLOB_OPTIONS
10907 line_input_state->sh_accept_glob = ash_accept_glob;
10908# endif
9776# endif 10909# endif
9777# if EDITING_HAS_sh_get_var 10910# if EDITING_HAS_sh_get_var
9778 line_input_state->sh_get_var = lookupvar; 10911 line_input_state->sh_get_var = lookupvar;
@@ -9800,6 +10933,12 @@ optschanged(void)
9800#else 10933#else
9801 viflag = 0; /* forcibly keep the option off */ 10934 viflag = 0; /* forcibly keep the option off */
9802#endif 10935#endif
10936#if ENABLE_ASH_NOCONSOLE
10937 hide_console(noconsole);
10938#endif
10939#if ENABLE_PLATFORM_MINGW32
10940 setwinxp(winxp);
10941#endif
9803} 10942}
9804 10943
9805struct localvar_list { 10944struct localvar_list {
@@ -9819,6 +10958,9 @@ poplocalvars(int keep)
9819 struct localvar_list *ll; 10958 struct localvar_list *ll;
9820 struct localvar *lvp, *next; 10959 struct localvar *lvp, *next;
9821 struct var *vp; 10960 struct var *vp;
10961#if ENABLE_PLATFORM_MINGW32
10962 int var_type;
10963#endif
9822 10964
9823 INT_OFF; 10965 INT_OFF;
9824 ll = localvar_stack; 10966 ll = localvar_stack;
@@ -9861,6 +11003,17 @@ poplocalvars(int keep)
9861 free((char*)vp->var_text); 11003 free((char*)vp->var_text);
9862 vp->flags = lvp->flags; 11004 vp->flags = lvp->flags;
9863 vp->var_text = lvp->text; 11005 vp->var_text = lvp->text;
11006#if ENABLE_PLATFORM_MINGW32
11007 var_type = is_bb_var(lvp->text);
11008 if (var_type == BB_VAR_ASSIGN && (lvp->flags & VEXPORT))
11009 putenv(lvp->text);
11010 else if (var_type) {
11011 char *var = xstrdup(lvp->text);
11012 *strchrnul(var, '=') = '\0';
11013 unsetenv(var);
11014 free(var);
11015 }
11016#endif
9864 } 11017 }
9865 free(lvp); 11018 free(lvp);
9866 } 11019 }
@@ -10138,6 +11291,9 @@ static int readcmd(int, char **) FAST_FUNC;
10138static int setcmd(int, char **) FAST_FUNC; 11291static int setcmd(int, char **) FAST_FUNC;
10139static int shiftcmd(int, char **) FAST_FUNC; 11292static int shiftcmd(int, char **) FAST_FUNC;
10140static int timescmd(int, char **) FAST_FUNC; 11293static int timescmd(int, char **) FAST_FUNC;
11294#if ENABLE_PLATFORM_MINGW32
11295static int titlecmd(int, char **) FAST_FUNC;
11296#endif
10141static int trapcmd(int, char **) FAST_FUNC; 11297static int trapcmd(int, char **) FAST_FUNC;
10142static int umaskcmd(int, char **) FAST_FUNC; 11298static int umaskcmd(int, char **) FAST_FUNC;
10143static int unsetcmd(int, char **) FAST_FUNC; 11299static int unsetcmd(int, char **) FAST_FUNC;
@@ -10210,7 +11366,7 @@ static const struct builtincmd builtintab[] = {
10210#if MAX_HISTORY 11366#if MAX_HISTORY
10211 { BUILTIN_NOSPEC "history" , historycmd }, 11367 { BUILTIN_NOSPEC "history" , historycmd },
10212#endif 11368#endif
10213#if JOBS 11369#if JOBS || JOBS_WIN32
10214 { BUILTIN_REGULAR "jobs" , jobscmd }, 11370 { BUILTIN_REGULAR "jobs" , jobscmd },
10215 { BUILTIN_REGULAR "kill" , killcmd }, 11371 { BUILTIN_REGULAR "kill" , killcmd },
10216#endif 11372#endif
@@ -10237,6 +11393,9 @@ static const struct builtincmd builtintab[] = {
10237 { BUILTIN_REGULAR "test" , testcmd }, 11393 { BUILTIN_REGULAR "test" , testcmd },
10238#endif 11394#endif
10239 { BUILTIN_SPEC_REG "times" , timescmd }, 11395 { BUILTIN_SPEC_REG "times" , timescmd },
11396#if ENABLE_PLATFORM_MINGW32
11397 { BUILTIN_REGULAR "title" , titlecmd },
11398#endif
10240 { BUILTIN_SPEC_REG "trap" , trapcmd }, 11399 { BUILTIN_SPEC_REG "trap" , trapcmd },
10241 { BUILTIN_REGULAR "true" , truecmd }, 11400 { BUILTIN_REGULAR "true" , truecmd },
10242 { BUILTIN_REGULAR "type" , typecmd }, 11401 { BUILTIN_REGULAR "type" , typecmd },
@@ -10255,7 +11414,7 @@ static const struct builtincmd builtintab[] = {
10255 /* [ */ 1 * ENABLE_ASH_TEST + \ 11414 /* [ */ 1 * ENABLE_ASH_TEST + \
10256 /* [[ */ 1 * BASH_TEST2 + \ 11415 /* [[ */ 1 * BASH_TEST2 + \
10257 /* alias */ 1 * ENABLE_ASH_ALIAS + \ 11416 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10258 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ 11417 /* bg */ 1 * JOBS + \
10259 /* break cd cddir */ 3) 11418 /* break cd cddir */ 3)
10260#define EVALCMD (COMMANDCMD + \ 11419#define EVALCMD (COMMANDCMD + \
10261 /* command */ 1 * ENABLE_ASH_CMDCMD + \ 11420 /* command */ 1 * ENABLE_ASH_CMDCMD + \
@@ -10336,6 +11495,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10336 * as POSIX mandates */ 11495 * as POSIX mandates */
10337 return back_exitstatus; 11496 return back_exitstatus;
10338} 11497}
11498
10339static int 11499static int
10340evalcommand(union node *cmd, int flags) 11500evalcommand(union node *cmd, int flags)
10341{ 11501{
@@ -10546,9 +11706,11 @@ evalcommand(union node *cmd, int flags)
10546 11706
10547 default: { 11707 default: {
10548 11708
11709//TODO: find a better solution for Windows on ARM than ignoring NOFORK
10549#if ENABLE_FEATURE_SH_STANDALONE \ 11710#if ENABLE_FEATURE_SH_STANDALONE \
10550 && ENABLE_FEATURE_SH_NOFORK \ 11711 && ENABLE_FEATURE_SH_NOFORK \
10551 && NUM_APPLETS > 1 11712 && NUM_APPLETS > 1 \
11713 && !(defined(_ARM64_) && !defined(_UCRT) && ENABLE_PLATFORM_MINGW32)
10552/* (1) BUG: if variables are set, we need to fork, or save/restore them 11714/* (1) BUG: if variables are set, we need to fork, or save/restore them
10553 * around run_nofork_applet() call. 11715 * around run_nofork_applet() call.
10554 * (2) Should this check also be done in forkshell()? 11716 * (2) Should this check also be done in forkshell()?
@@ -10570,6 +11732,9 @@ evalcommand(union node *cmd, int flags)
10570 * and/or wait for user input ineligible for NOFORK: 11732 * and/or wait for user input ineligible for NOFORK:
10571 * for example, "yes" or "rm" (rm -i waits for input). 11733 * for example, "yes" or "rm" (rm -i waits for input).
10572 */ 11734 */
11735#if ENABLE_PLATFORM_MINGW32
11736 argv[0] = (char *)bb_basename(argv[0]);
11737#endif
10573 exitstatus = run_nofork_applet(applet_no, argv); 11738 exitstatus = run_nofork_applet(applet_no, argv);
10574 environ = sv_environ; 11739 environ = sv_environ;
10575 /* 11740 /*
@@ -10589,6 +11754,22 @@ evalcommand(union node *cmd, int flags)
10589 * in a script or a subshell does not need forking, 11754 * in a script or a subshell does not need forking,
10590 * we can just exec it. 11755 * we can just exec it.
10591 */ 11756 */
11757#if ENABLE_PLATFORM_MINGW32
11758 if (!(flags & EV_EXIT) || may_have_traps IF_SUW32(|| delayexit)) {
11759 /* No, forking off a child is necessary */
11760 struct forkshell fs;
11761
11762 INT_OFF;
11763 memset(&fs, 0, sizeof(fs));
11764 fs.fpid = FS_SHELLEXEC;
11765 fs.argv = argv;
11766 fs.path = (char*)path;
11767 fs.fd[0] = cmdentry.u.index;
11768 jp = makejob(/*cmd,*/ 1);
11769 spawn_forkshell(&fs, jp, cmd, FORK_FG);
11770 break;
11771 }
11772#else
10592 if (!(flags & EV_EXIT) || may_have_traps) { 11773 if (!(flags & EV_EXIT) || may_have_traps) {
10593 /* No, forking off a child is necessary */ 11774 /* No, forking off a child is necessary */
10594 INT_OFF; 11775 INT_OFF;
@@ -10602,6 +11783,7 @@ evalcommand(union node *cmd, int flags)
10602 FORCE_INT_ON; 11783 FORCE_INT_ON;
10603 /* fall through to exec'ing external program */ 11784 /* fall through to exec'ing external program */
10604 } 11785 }
11786#endif
10605 shellexec(argv[0], argv, path, cmdentry.u.index); 11787 shellexec(argv[0], argv, path, cmdentry.u.index);
10606 /* NOTREACHED */ 11788 /* NOTREACHED */
10607 } /* default */ 11789 } /* default */
@@ -10814,6 +11996,54 @@ static void popstring(void)
10814 INT_ON; 11996 INT_ON;
10815} 11997}
10816 11998
11999#if ENABLE_PLATFORM_MINGW32
12000/*
12001 * Wrapper around nonblock_immune_read() to remove CRs, but only from
12002 * CRLF pairs. The tricky part is handling a CR at the end of the buffer.
12003 */
12004static inline ssize_t
12005nonblock_immune_wrapper(struct parsefile *pf, char *buffer, size_t count)
12006{
12007 int nr, injected_cr;
12008
12009 // Inject unprocessed CR from previous read into the buffer.
12010 if (pf->cr)
12011 *buffer = '\r';
12012 retry:
12013 nr = nonblock_immune_read(pf->pf_fd, buffer + pf->cr, count - pf->cr);
12014 if (nr < 0)
12015 return nr;
12016
12017 injected_cr = pf->cr;
12018 nr += pf->cr;
12019 pf->cr = 0;
12020
12021 if (nr > 0) {
12022 nr = remove_cr(buffer, nr);
12023 // remove_cr() won't reduce size to zero, so [nr - 1] is OK.
12024 if (buffer[nr - 1] == '\r') {
12025 if (nr > 1) {
12026 // Ignore trailing CR for now: we'll deal with it later.
12027 pf->cr = 1;
12028 --nr;
12029 } else if (injected_cr) { // nr == 1
12030 // Buffer only contains an injected CR. This means the
12031 // read returned EOF. Return the buffer as-is. The
12032 // next call will detect EOF.
12033 } else {
12034 // Buffer only contains a CR from the most recent read.
12035 // Try another read, treating the CR as injected. We'll
12036 // either get more characters or EOF. Either way we
12037 // won't end up here again.
12038 pf->cr = 1;
12039 goto retry;
12040 }
12041 }
12042 }
12043 return nr;
12044}
12045#endif
12046
10817static int 12047static int
10818preadfd(void) 12048preadfd(void)
10819{ 12049{
@@ -10824,7 +12054,11 @@ preadfd(void)
10824#if ENABLE_FEATURE_EDITING 12054#if ENABLE_FEATURE_EDITING
10825 /* retry: */ 12055 /* retry: */
10826 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 12056 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
12057#if ENABLE_PLATFORM_MINGW32
12058 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
12059#else
10827 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 12060 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
12061#endif
10828 else { 12062 else {
10829# if ENABLE_ASH_IDLE_TIMEOUT 12063# if ENABLE_ASH_IDLE_TIMEOUT
10830 int timeout = -1; 12064 int timeout = -1;
@@ -10863,12 +12097,21 @@ preadfd(void)
10863 INT_ON; /* here non-blocked SIGINT will longjmp */ 12097 INT_ON; /* here non-blocked SIGINT will longjmp */
10864 if (nr == 0) { 12098 if (nr == 0) {
10865 /* ^C pressed, "convert" to SIGINT */ 12099 /* ^C pressed, "convert" to SIGINT */
12100# if !ENABLE_PLATFORM_MINGW32
10866 write(STDOUT_FILENO, "^C\n", 3); 12101 write(STDOUT_FILENO, "^C\n", 3);
10867 raise(SIGINT); /* here non-blocked SIGINT will longjmp */ 12102 raise(SIGINT); /* here non-blocked SIGINT will longjmp */
10868 /* raise(SIGINT) did not work! (e.g. if SIGINT 12103 /* raise(SIGINT) did not work! (e.g. if SIGINT
10869 * is SIG_IGNed on startup, it stays SIG_IGNed) 12104 * is SIG_IGNed on startup, it stays SIG_IGNed)
10870 */ 12105 */
12106# else
12107 raise_interrupt();
12108 write(STDOUT_FILENO, "^C\n", 3);
12109# endif
10871 if (trap[SIGINT]) { 12110 if (trap[SIGINT]) {
12111# if ENABLE_PLATFORM_MINGW32
12112 pending_int = 1;
12113 dotrap();
12114# endif
10872 empty_line_input: 12115 empty_line_input:
10873 buf[0] = '\n'; 12116 buf[0] = '\n';
10874 buf[1] = '\0'; 12117 buf[1] = '\0';
@@ -10897,7 +12140,11 @@ preadfd(void)
10897 } 12140 }
10898 } 12141 }
10899#else 12142#else
12143# if ENABLE_PLATFORM_MINGW32
12144 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
12145# else
10900 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 12146 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
12147# endif
10901#endif 12148#endif
10902 12149
10903#if 0 /* disabled: nonblock_immune_read() handles this problem */ 12150#if 0 /* disabled: nonblock_immune_read() handles this problem */
@@ -11196,6 +12443,7 @@ popallfiles(void)
11196 unwindfiles(&basepf); 12443 unwindfiles(&basepf);
11197} 12444}
11198 12445
12446#if !ENABLE_PLATFORM_MINGW32
11199/* 12447/*
11200 * Close the file(s) that the shell is reading commands from. Called 12448 * Close the file(s) that the shell is reading commands from. Called
11201 * after a fork is done. 12449 * after a fork is done.
@@ -11209,6 +12457,7 @@ closescript(void)
11209 g_parsefile->pf_fd = 0; 12457 g_parsefile->pf_fd = 0;
11210 } 12458 }
11211} 12459}
12460#endif
11212 12461
11213/* 12462/*
11214 * Like setinputfile, but takes an open file descriptor. Call this with 12463 * Like setinputfile, but takes an open file descriptor. Call this with
@@ -11445,8 +12694,19 @@ options(int *login_sh)
11445 int val; 12694 int val;
11446 int c; 12695 int c;
11447 12696
11448 if (login_sh != NULL) /* if we came from startup code */ 12697#if ENABLE_ASH_NOCONSOLE
12698 noconsole = console_state();
12699#endif
12700 if (login_sh != NULL) { /* if we came from startup code */
11449 minusc = NULL; 12701 minusc = NULL;
12702#if ENABLE_PLATFORM_MINGW32
12703 dirarg = NULL;
12704 title = NULL;
12705# if ENABLE_SUW32
12706 delayexit = 0;
12707# endif
12708#endif
12709 }
11450 while ((p = *argptr) != NULL) { 12710 while ((p = *argptr) != NULL) {
11451 c = *p++; 12711 c = *p++;
11452 if (c != '-' && c != '+') 12712 if (c != '-' && c != '+')
@@ -11476,6 +12736,31 @@ options(int *login_sh)
11476 cflag = 1; 12736 cflag = 1;
11477 continue; 12737 continue;
11478 } 12738 }
12739#if ENABLE_PLATFORM_MINGW32
12740 /* Undocumented flags;
12741 * -d force current directory
12742 * -t title to display in console window
12743 * -N prompt user before exit
12744 * Must appear before -s or -c. */
12745 if (c == 'd' && val == 1) {
12746 if (*argptr == NULL)
12747 ash_msg_and_raise_error(bb_msg_requires_arg, "-d");
12748 dirarg = *argptr++;
12749 continue;
12750 }
12751 if (c == 't' && val == 1) {
12752 if (*argptr == NULL)
12753 ash_msg_and_raise_error(bb_msg_requires_arg, "-t");
12754 title = *argptr++;
12755 continue;
12756 }
12757# if ENABLE_SUW32
12758 if (c == 'N' && val == 1) {
12759 delayexit = 1;
12760 continue;
12761 }
12762# endif
12763#endif
11479 if (c == 's') { /* -s, +s */ 12764 if (c == 's') { /* -s, +s */
11480 sflag = 1; 12765 sflag = 1;
11481 continue; 12766 continue;
@@ -13503,6 +14788,9 @@ evalstring(char *s, int flags)
13503 int status; 14788 int status;
13504 14789
13505 s = sstrdup(s); 14790 s = sstrdup(s);
14791#if ENABLE_PLATFORM_MINGW32
14792 remove_cr(s, strlen(s)+1);
14793#endif
13506 setinputstring(s); 14794 setinputstring(s);
13507 setstackmark(&smark); 14795 setstackmark(&smark);
13508 14796
@@ -13590,7 +14878,7 @@ cmdloop(int top)
13590 int skip; 14878 int skip;
13591 14879
13592 setstackmark(&smark); 14880 setstackmark(&smark);
13593#if JOBS 14881#if JOBS || JOBS_WIN32
13594 if (doing_jobctl) 14882 if (doing_jobctl)
13595 showjobs(SHOW_CHANGED|SHOW_STDERR); 14883 showjobs(SHOW_CHANGED|SHOW_STDERR);
13596#endif 14884#endif
@@ -13598,6 +14886,9 @@ cmdloop(int top)
13598 if (iflag && top) { 14886 if (iflag && top) {
13599 inter++; 14887 inter++;
13600 chkmail(); 14888 chkmail();
14889#if ENABLE_PLATFORM_MINGW32
14890 terminal_mode(TRUE);
14891#endif
13601 } 14892 }
13602 n = parsecmd(inter); 14893 n = parsecmd(inter);
13603#if DEBUG 14894#if DEBUG
@@ -13621,8 +14912,10 @@ cmdloop(int top)
13621 } else { 14912 } else {
13622 int i; 14913 int i;
13623 14914
14915#if !ENABLE_PLATFORM_MINGW32
13624 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ 14916 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13625 job_warning >>= 1; 14917 job_warning >>= 1;
14918#endif
13626 numeof = 0; 14919 numeof = 0;
13627 i = evaltree(n, 0); 14920 i = evaltree(n, 0);
13628 if (n) 14921 if (n)
@@ -13652,7 +14945,7 @@ find_dot_file(char *basename)
13652 int len; 14945 int len;
13653 14946
13654 /* don't try this for absolute or relative paths */ 14947 /* don't try this for absolute or relative paths */
13655 if (strchr(basename, '/')) 14948 if (strchr(basename, '/') IF_PLATFORM_MINGW32(|| strchr(basename, '\\')))
13656 return basename; 14949 return basename;
13657 14950
13658 path = pathval(); 14951 path = pathval();
@@ -13781,6 +15074,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13781 struct builtincmd *bcmd; 15074 struct builtincmd *bcmd;
13782 int len; 15075 int len;
13783 15076
15077#if !ENABLE_PLATFORM_MINGW32
13784 /* If name contains a slash, don't use PATH or hash table */ 15078 /* If name contains a slash, don't use PATH or hash table */
13785 if (strchr(name, '/') != NULL) { 15079 if (strchr(name, '/') != NULL) {
13786 entry->u.index = -1; 15080 entry->u.index = -1;
@@ -13797,6 +15091,35 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13797 entry->cmdtype = CMDNORMAL; 15091 entry->cmdtype = CMDNORMAL;
13798 return; 15092 return;
13799 } 15093 }
15094#else /* ENABLE_PLATFORM_MINGW32 */
15095 /* If name contains a slash or drive prefix, don't use PATH or hash table */
15096 if (has_path(name)) {
15097 entry->u.index = -1;
15098 entry->cmdtype = CMDNORMAL;
15099 fullname = stack_add_ext_space(name);
15100 if (add_win32_extension(fullname)) {
15101 return;
15102 } else if (unix_path(name)) {
15103 name = (char *)bb_basename(name);
15104 if (
15105# if ENABLE_FEATURE_SH_STANDALONE
15106 find_applet_by_name_for_sh(name, path) >= 0 ||
15107# endif
15108 !find_builtin(bb_basename(name))
15109 ) {
15110 act |= DO_NOFUNC;
15111 } else if (act & DO_ABS) {
15112 entry->cmdtype = CMDUNKNOWN;
15113 return;
15114 }
15115 } else if (act & DO_ABS) {
15116 entry->cmdtype = CMDUNKNOWN;
15117 return;
15118 } else {
15119 return;
15120 }
15121 }
15122#endif /* ENABLE_PLATFORM_MINGW32 */
13800 15123
13801/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ 15124/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
13802 15125
@@ -13851,7 +15174,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13851 15174
13852#if ENABLE_FEATURE_SH_STANDALONE 15175#if ENABLE_FEATURE_SH_STANDALONE
13853 { 15176 {
13854 int applet_no = find_applet_by_name(name); 15177 int applet_no = find_applet_by_name_for_sh(name, path);
13855 if (applet_no >= 0) { 15178 if (applet_no >= 0) {
13856 entry->cmdtype = CMDNORMAL; 15179 entry->cmdtype = CMDNORMAL;
13857 entry->u.index = -2 - applet_no; 15180 entry->u.index = -2 - applet_no;
@@ -13890,12 +15213,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13890 } 15213 }
13891 } 15214 }
13892 /* if rehash, don't redo absolute path names */ 15215 /* if rehash, don't redo absolute path names */
13893 if (fullname[0] == '/' && idx <= prev) { 15216 if (!is_relative_path(fullname) && idx <= prev) {
13894 if (idx < prev) 15217 if (idx < prev)
13895 continue; 15218 continue;
13896 TRACE(("searchexec \"%s\": no change\n", name)); 15219 TRACE(("searchexec \"%s\": no change\n", name));
13897 goto success; 15220 goto success;
13898 } 15221 }
15222#if ENABLE_PLATFORM_MINGW32
15223 add_win32_extension(fullname);
15224#endif
13899 while (stat(fullname, &statb) < 0) { 15225 while (stat(fullname, &statb) < 0) {
13900#ifdef SYSV 15226#ifdef SYSV
13901 if (errno == EINTR) 15227 if (errno == EINTR)
@@ -14034,19 +15360,45 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14034 if (LONE_DASH(action)) 15360 if (LONE_DASH(action))
14035 action = NULL; 15361 action = NULL;
14036 else { 15362 else {
15363#if !ENABLE_PLATFORM_MINGW32
14037 if (action[0]) /* not NULL and not "" and not "-" */ 15364 if (action[0]) /* not NULL and not "" and not "-" */
14038 may_have_traps = 1; 15365 may_have_traps = 1;
15366#endif
14039 action = ckstrdup(action); 15367 action = ckstrdup(action);
14040 } 15368 }
14041 } 15369 }
14042 free(trap[signo]); 15370 free(trap[signo]);
14043 trap[signo] = action; 15371 trap[signo] = action;
15372#if ENABLE_PLATFORM_MINGW32
15373 if (signo == SIGINT) {
15374 // trap '' INT disables Ctrl-C, anything else enables it
15375 if (action && action[0] == '\0') {
15376 SetConsoleCtrlHandler(NULL, TRUE);
15377# if ENABLE_FEATURE_EDITING
15378 if (line_input_state) {
15379 line_input_state->flags |= IGNORE_CTRL_C;
15380 }
15381# endif
15382 } else {
15383 SetConsoleCtrlHandler(NULL, FALSE);
15384# if ENABLE_FEATURE_EDITING
15385 if (line_input_state) {
15386 line_input_state->flags &= ~IGNORE_CTRL_C;
15387 }
15388# endif
15389 }
15390 }
15391#else
14044 if (signo != 0 && signo < NSIG) 15392 if (signo != 0 && signo < NSIG)
14045 setsignal(signo); 15393 setsignal(signo);
15394#endif
14046 INT_ON; 15395 INT_ON;
14047 next: 15396 next:
14048 ap++; 15397 ap++;
14049 } 15398 }
15399#if ENABLE_PLATFORM_MINGW32
15400 may_have_traps = trap[SIGINT] && trap[SIGINT][0] != '\0';
15401#endif
14050 return exitcode; 15402 return exitcode;
14051} 15403}
14052 15404
@@ -14075,10 +15427,12 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14075 { 15427 {
14076 const char *a = applet_names; 15428 const char *a = applet_names;
14077 while (*a) { 15429 while (*a) {
14078 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a); 15430 if (prefer_applet(a, pathval())) {
14079 if (col > 60) { 15431 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
14080 out1fmt("\n"); 15432 if (col > 60) {
14081 col = 0; 15433 out1fmt("\n");
15434 col = 0;
15435 }
14082 } 15436 }
14083 while (*a++ != '\0') 15437 while (*a++ != '\0')
14084 continue; 15438 continue;
@@ -14140,7 +15494,24 @@ exportcmd(int argc UNUSED_PARAM, char **argv)
14140 } else { 15494 } else {
14141 vp = *findvar(hashvar(name), name); 15495 vp = *findvar(hashvar(name), name);
14142 if (vp) { 15496 if (vp) {
15497#if ENABLE_PLATFORM_MINGW32
15498 if (is_bb_var(name) == BB_VAR_EXACT) {
15499 if (flag_off == ~VEXPORT)
15500 unsetenv(name);
15501 else if (flag == VEXPORT && !(vp->flags & VUNSET))
15502 putenv(vp->var_text);
15503 }
15504#endif
14143 vp->flags = ((vp->flags | flag) & flag_off); 15505 vp->flags = ((vp->flags | flag) & flag_off);
15506#if ENABLE_PLATFORM_MINGW32
15507 /* Unexporting a variable imported from the
15508 * environment restores its original value and
15509 * removes the VIMPORT flag. */
15510 if ((vp->flags & VIMPORT) && (flag_off == ~VEXPORT)) {
15511 vp->flags &= ~VIMPORT;
15512 p = getenv(name);
15513 } else
15514#endif
14144 continue; 15515 continue;
14145 } 15516 }
14146 } 15517 }
@@ -14231,6 +15602,21 @@ timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14231 return 0; 15602 return 0;
14232} 15603}
14233 15604
15605#if ENABLE_PLATFORM_MINGW32
15606static int FAST_FUNC
15607titlecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15608{
15609 if (*argptr == NULL) {
15610 char buffer[256];
15611 if (get_title(buffer, sizeof(buffer)))
15612 puts(buffer);
15613 } else {
15614 set_title(*argptr);
15615 }
15616 return 0;
15617}
15618#endif
15619
14234#if ENABLE_FEATURE_SH_MATH 15620#if ENABLE_FEATURE_SH_MATH
14235/* 15621/*
14236 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell. 15622 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
@@ -14331,6 +15717,27 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14331 goto again; 15717 goto again;
14332 } 15718 }
14333 15719
15720#if ENABLE_PLATFORM_MINGW32
15721 if ((uintptr_t)r == 2) {
15722 /* ^C pressed, propagate event */
15723 if (trap[SIGINT]) {
15724 write(STDOUT_FILENO, "^C", 2);
15725 pending_int = 1;
15726 dotrap();
15727 if (!(rootshell && iflag))
15728 return (uintptr_t)0;
15729 else
15730 goto again;
15731 } else if (iflag) {
15732 raise_interrupt();
15733 } else {
15734 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
15735 exitshell();
15736 }
15737 return (uintptr_t)r;
15738 }
15739#endif
15740
14334 if ((uintptr_t)r > 1) 15741 if ((uintptr_t)r > 1)
14335 ash_msg_and_raise_error(r); 15742 ash_msg_and_raise_error(r);
14336 15743
@@ -14391,6 +15798,9 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14391 if (!isdigit(modestr[0])) 15798 if (!isdigit(modestr[0]))
14392 mask ^= 0777; 15799 mask ^= 0777;
14393 umask(mask); 15800 umask(mask);
15801#if ENABLE_PLATFORM_MINGW32
15802 setvareq(xasprintf("BB_UMASK=0%o", mask), VEXPORT|VNOSAVE);
15803#endif
14394 } 15804 }
14395 return 0; 15805 return 0;
14396} 15806}
@@ -14474,6 +15884,13 @@ exitshell(void)
14474 /*free(p); - we'll exit soon */ 15884 /*free(p); - we'll exit soon */
14475 } 15885 }
14476 out: 15886 out:
15887#if ENABLE_SUW32
15888 if (delayexit) {
15889#define EXIT_MSG "Press any key to exit..."
15890 console_write(EXIT_MSG, sizeof(EXIT_MSG) - 1);
15891 _getch();
15892 }
15893#endif
14477 exitreset(); 15894 exitreset();
14478 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}". 15895 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14479 * our setjobctl(0) does not panic if tcsetpgrp fails inside it. 15896 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
@@ -14484,22 +15901,97 @@ exitshell(void)
14484 /* NOTREACHED */ 15901 /* NOTREACHED */
14485} 15902}
14486 15903
15904#if ENABLE_PLATFORM_MINGW32
15905/* We need to see if HOME is *really* unset */
15906# undef getenv
15907static void setvar_if_unset(const char *key, const char *value)
15908{
15909 if (!getenv(key) || getuid() == 0)
15910 setvar(key, value, VEXPORT);
15911}
15912#endif
15913
14487/* Don't inline: conserve stack of caller from having our locals too */ 15914/* Don't inline: conserve stack of caller from having our locals too */
14488static NOINLINE void 15915static NOINLINE void
14489init(void) 15916init(void)
14490{ 15917{
15918#if ENABLE_PLATFORM_MINGW32
15919 int import = 0;
15920#else
14491 /* we will never free this */ 15921 /* we will never free this */
14492 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ); 15922 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ);
14493 basepf.linno = 1; 15923 basepf.linno = 1;
14494 15924
14495 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 15925 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
14496 setsignal(SIGCHLD); 15926 setsignal(SIGCHLD);
15927#endif
14497 15928
14498 { 15929 {
14499 char **envp; 15930 char **envp;
14500 const char *p; 15931 const char *p;
14501 15932
14502 initvar(); 15933 initvar();
15934
15935#if ENABLE_PLATFORM_MINGW32
15936 /*
15937 * case insensitive env names from Windows world
15938 *
15939 * Some standard env names such as PATH is named Path and so on
15940 * ash itself is case sensitive, so "Path" will confuse it, as
15941 * MSVC getenv() is case insensitive.
15942 *
15943 * We may end up having both Path and PATH. Then Path will be chosen
15944 * because it appears first.
15945 */
15946 if (windows_env()) {
15947 /*
15948 * If we get here it's because the environment suggests we
15949 * haven't been invoked from an earlier instance of BusyBox.
15950 */
15951 char *start, *end;
15952 struct passwd *pw;
15953
15954 /* mintty sets HOME: unset it */
15955 const char *tty = getenv("TERM_PROGRAM");
15956 if (tty && strcmp(tty, "mintty") == 0) {
15957 unsetenv("HOME");
15958 }
15959
15960 import = VIMPORT;
15961 for (envp = environ; envp && *envp; envp++) {
15962 if (!(end=strchr(*envp, '=')))
15963 continue;
15964
15965 /* check for invalid characters in name */
15966 start = (char *)endofname(*envp);
15967 if (*start != '=') {
15968 /* Make a copy of the original variable */
15969 setvareq(xstrdup(*envp), VEXPORT|VNOSAVE);
15970
15971 /* Replace invalid characters with underscores */
15972 for (; start < end; start++) {
15973 if (!isalnum(*start)) {
15974 *start = '_';
15975 }
15976 }
15977 }
15978
15979 /* make all variable names uppercase */
15980 for (start = *envp;start < end;start++)
15981 *start = toupper(*start);
15982 }
15983
15984 /* Initialise some variables normally set at login, but
15985 * only if someone hasn't already set them or we're root. */
15986 pw = getpwuid(getuid());
15987 if (pw) {
15988 setvar_if_unset("USER", pw->pw_name);
15989 setvar_if_unset("LOGNAME", pw->pw_name);
15990 setvar_if_unset("HOME", pw->pw_dir);
15991 }
15992 setvar_if_unset("SHELL", DEFAULT_SHELL);
15993 }
15994#endif
14503 for (envp = environ; envp && *envp; envp++) { 15995 for (envp = environ; envp && *envp; envp++) {
14504/* Used to have 15996/* Used to have
14505 * p = endofname(*envp); 15997 * p = endofname(*envp);
@@ -14513,7 +16005,11 @@ init(void)
14513 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this 16005 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14514 */ 16006 */
14515 if (strchr(*envp, '=')) { 16007 if (strchr(*envp, '=')) {
16008#if !ENABLE_PLATFORM_MINGW32
14516 setvareq(*envp, VEXPORT|VTEXTFIXED); 16009 setvareq(*envp, VEXPORT|VTEXTFIXED);
16010#else
16011 setvareq(*envp, VEXPORT|import);
16012#endif
14517 } 16013 }
14518 } 16014 }
14519 16015
@@ -14564,7 +16060,11 @@ procargs(char **argv)
14564 int login_sh; 16060 int login_sh;
14565 16061
14566 xargv = argv; 16062 xargv = argv;
16063#if ENABLE_PLATFORM_MINGW32
16064 login_sh = applet_name[0] == 'l';
16065#else
14567 login_sh = xargv[0] && xargv[0][0] == '-'; 16066 login_sh = xargv[0] && xargv[0][0] == '-';
16067#endif
14568#if NUM_SCRIPTS > 0 16068#if NUM_SCRIPTS > 0
14569 if (minusc) 16069 if (minusc)
14570 goto setarg0; 16070 goto setarg0;
@@ -14588,7 +16088,9 @@ procargs(char **argv)
14588 } 16088 }
14589 if (iflag == 2 /* no explicit -i given */ 16089 if (iflag == 2 /* no explicit -i given */
14590 && sflag == 1 /* -s given (or implied) */ 16090 && sflag == 1 /* -s given (or implied) */
16091#if !ENABLE_PLATFORM_MINGW32
14591 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */ 16092 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
16093#endif
14592 && isatty(0) && isatty(1) /* we are on tty */ 16094 && isatty(0) && isatty(1) /* we are on tty */
14593 ) { 16095 ) {
14594 iflag = 1; 16096 iflag = 1;
@@ -14608,6 +16110,9 @@ procargs(char **argv)
14608 goto setarg0; 16110 goto setarg0;
14609 } else if (!sflag) { 16111 } else if (!sflag) {
14610 setinputfile(*xargv, 0); 16112 setinputfile(*xargv, 0);
16113#if ENABLE_PLATFORM_MINGW32
16114 bs_to_slash(*xargv);
16115#endif
14611 setarg0: 16116 setarg0:
14612 arg0 = *xargv++; 16117 arg0 = *xargv++;
14613 commandname = arg0; 16118 commandname = arg0;
@@ -14631,8 +16136,10 @@ procargs(char **argv)
14631 * NB: must do it before setting up signals (in optschanged()) 16136 * NB: must do it before setting up signals (in optschanged())
14632 * and reading .profile etc (after we return from here): 16137 * and reading .profile etc (after we return from here):
14633 */ 16138 */
16139#if !ENABLE_PLATFORM_MINGW32
14634 if (iflag) 16140 if (iflag)
14635 signal(SIGHUP, SIG_DFL); 16141 signal(SIGHUP, SIG_DFL);
16142#endif
14636 16143
14637 optschanged(); 16144 optschanged();
14638 16145
@@ -14677,9 +16184,25 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14677 struct stackmark smark; 16184 struct stackmark smark;
14678 int login_sh; 16185 int login_sh;
14679 16186
16187#if ENABLE_PLATFORM_MINGW32
16188 INIT_G_memstack();
16189
16190 /* from init() */
16191 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ);
16192 basepf.linno = 1;
16193
16194 if (argc == 3 && !strcmp(argv[1], "--fs")) {
16195 forkshell_init(argv[2]);
16196 /* only reached in case of error */
16197 bb_error_msg_and_die("forkshell failed");
16198 }
16199#endif
16200
14680 /* Initialize global data */ 16201 /* Initialize global data */
14681 INIT_G_misc(); 16202 INIT_G_misc();
16203#if !ENABLE_PLATFORM_MINGW32
14682 INIT_G_memstack(); 16204 INIT_G_memstack();
16205#endif
14683 INIT_G_var(); 16206 INIT_G_var();
14684#if ENABLE_ASH_ALIAS 16207#if ENABLE_ASH_ALIAS
14685 INIT_G_alias(); 16208 INIT_G_alias();
@@ -14725,6 +16248,10 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14725 init(); 16248 init();
14726 setstackmark(&smark); 16249 setstackmark(&smark);
14727 16250
16251#if ENABLE_PLATFORM_MINGW32
16252 SetConsoleCtrlHandler(ctrl_handler, TRUE);
16253#endif
16254
14728#if NUM_SCRIPTS > 0 16255#if NUM_SCRIPTS > 0
14729 if (argc < 0) 16256 if (argc < 0)
14730 /* Non-NULL minusc tells procargs that an embedded script is being run */ 16257 /* Non-NULL minusc tells procargs that an embedded script is being run */
@@ -14736,11 +16263,45 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14736 trace_puts_args(argv); 16263 trace_puts_args(argv);
14737#endif 16264#endif
14738 16265
16266#if ENABLE_PLATFORM_MINGW32
16267 if (!dirarg && !login_sh && iflag) {
16268 char *cwd = getcwd(NULL, 0);
16269 if (cwd) {
16270 chdir(cwd);
16271 setpwd(NULL, 0);
16272 free(cwd);
16273 }
16274 }
16275
16276 if (title)
16277 set_title(title);
16278#endif
16279
14739 if (login_sh) { 16280 if (login_sh) {
14740 const char *hp; 16281 const char *hp;
14741 16282
16283#if ENABLE_PLATFORM_MINGW32
16284 if (!dirarg) {
16285 hp = lookupvar("HOME");
16286 if (hp == NULL || *hp == '\0')
16287 hp = xgetpwuid(getuid())->pw_dir;
16288 chdir(hp);
16289 setpwd(NULL, 0);
16290 }
16291#endif
16292
14742 state = 1; 16293 state = 1;
16294#if ENABLE_PLATFORM_MINGW32
16295 hp = concat_path_file(get_system_drive(), "/etc/profile");
16296 read_profile(hp);
16297 free((void *)hp);
16298
16299 hp = exe_relative_path("/etc/profile");
16300 read_profile(hp);
16301 free((void *)hp);
16302#else
14743 read_profile("/etc/profile"); 16303 read_profile("/etc/profile");
16304#endif
14744 state1: 16305 state1:
14745 state = 2; 16306 state = 2;
14746 hp = lookupvar("HOME"); 16307 hp = lookupvar("HOME");
@@ -14749,10 +16310,18 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14749 } 16310 }
14750 state2: 16311 state2:
14751 state = 3; 16312 state = 3;
16313#if ENABLE_PLATFORM_MINGW32
16314 if (dirarg) {
16315 chdir(dirarg);
16316 setpwd(NULL, 0);
16317 }
16318#endif
14752 if (iflag 16319 if (iflag
16320#if ENABLE_PLATFORM_POSIX
14753#ifndef linux 16321#ifndef linux
14754 && getuid() == geteuid() && getgid() == getegid() 16322 && getuid() == geteuid() && getgid() == getegid()
14755#endif 16323#endif
16324#endif
14756 ) { 16325 ) {
14757 const char *shinit = lookupvar("ENV"); 16326 const char *shinit = lookupvar("ENV");
14758 if (shinit != NULL && *shinit != '\0') 16327 if (shinit != NULL && *shinit != '\0')
@@ -14777,7 +16346,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14777 // ash -sc 'echo $-' 16346 // ash -sc 'echo $-'
14778 // continue reading input from stdin after running 'echo'. 16347 // continue reading input from stdin after running 'echo'.
14779 // bash does not do this: it prints "hBcs" and exits. 16348 // bash does not do this: it prints "hBcs" and exits.
16349#if !ENABLE_PLATFORM_MINGW32
14780 evalstring(minusc, EV_EXIT); 16350 evalstring(minusc, EV_EXIT);
16351#else
16352 evalstring(minusc, sflag ? 0 : EV_EXIT);
16353#endif
14781 } 16354 }
14782 16355
14783 if (sflag || minusc == NULL) { 16356 if (sflag || minusc == NULL) {
@@ -14820,6 +16393,1099 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14820 /* NOTREACHED */ 16393 /* NOTREACHED */
14821} 16394}
14822 16395
16396#if ENABLE_PLATFORM_MINGW32
16397static void
16398forkshell_openhere(struct forkshell *fs)
16399{
16400 const char *p = fs->path;
16401 size_t len = strlen(p);
16402 int pip[2];
16403
16404 pip[0] = fs->fd[0];
16405 pip[1] = fs->fd[1];
16406
16407 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16408
16409 close(pip[0]);
16410 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
16411 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
16412 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
16413 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
16414 signal(SIGPIPE, SIG_DFL);
16415 xwrite(pip[1], p, len);
16416 _exit_SUCCESS();
16417}
16418
16419static void
16420forkshell_evalbackcmd(struct forkshell *fs)
16421{
16422#if BASH_PROCESS_SUBST
16423 /* determine end of pipe used by parent (ip) and child (ic) */
16424 const int ctl = fs->fd[2];
16425 const int ip = (ctl == CTLTOPROC);
16426 const int ic = !(ctl == CTLTOPROC);
16427#else
16428 const int ip = 0;
16429 const int ic = 1;
16430#endif
16431 union node *n = fs->n;
16432 int pip[2];
16433
16434 pip[ip] = fs->fd[ip];
16435 pip[ic] = fs->fd[ic];
16436
16437 FORCE_INT_ON;
16438 close(pip[ip]);
16439 if (pip[ic] != ic) {
16440 /*close(ic);*/
16441 dup2_or_raise(pip[ic], ic);
16442 close(pip[ic]);
16443 }
16444 eflag = 0;
16445 ifsfree();
16446 evaltreenr(n, EV_EXIT);
16447 /* NOTREACHED */
16448}
16449
16450static void
16451forkshell_evalsubshell(struct forkshell *fs)
16452{
16453 union node *n = fs->n;
16454 int flags = fs->flags;
16455
16456 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16457 INT_ON;
16458 flags |= EV_EXIT;
16459 if (fs->mode)
16460 flags &= ~EV_TESTED;
16461 expredir(n->nredir.redirect);
16462 redirect(n->nredir.redirect, 0);
16463 evaltreenr(n->nredir.n, flags);
16464 /* never returns */
16465}
16466
16467static void
16468forkshell_evalpipe(struct forkshell *fs)
16469{
16470 union node *n = fs->n;
16471 int flags = fs->flags;
16472 int prevfd = fs->fd[2];
16473 int pip[2] = {fs->fd[0], fs->fd[1]};
16474
16475 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16476 INT_ON;
16477 if (pip[1] >= 0) {
16478 close(pip[0]);
16479 }
16480 if (prevfd > 0) {
16481 dup2(prevfd, 0);
16482 close(prevfd);
16483 }
16484 if (pip[1] > 1) {
16485 dup2(pip[1], 1);
16486 close(pip[1]);
16487 }
16488 evaltreenr(n, flags);
16489}
16490
16491static void
16492forkshell_shellexec(struct forkshell *fs)
16493{
16494 int idx = fs->fd[0];
16495 char **argv = fs->argv;
16496 char *path = fs->path;
16497
16498 FORCE_INT_ON;
16499 shellexec(argv[0], argv, path, idx);
16500}
16501
16502static void
16503forkshell_child(struct forkshell *fs)
16504{
16505 switch ( fs->fpid ) {
16506 case FS_OPENHERE:
16507 forkshell_openhere(fs);
16508 break;
16509 case FS_EVALBACKCMD:
16510 forkshell_evalbackcmd(fs);
16511 break;
16512 case FS_EVALSUBSHELL:
16513 forkshell_evalsubshell(fs);
16514 break;
16515 case FS_EVALPIPE:
16516 forkshell_evalpipe(fs);
16517 break;
16518 case FS_SHELLEXEC:
16519 forkshell_shellexec(fs);
16520 break;
16521 }
16522}
16523
16524/*
16525 * Reinitialise the builtin environment variables in varinit. Their
16526 * current settings have been copied from the parent in vartab. Look
16527 * these up using the names from varinit_data, copy the details from
16528 * vartab to varinit and replace the old copy in vartab with the new
16529 * one in varinit.
16530 *
16531 * Also reinitialise the function pointers and line number variable.
16532 */
16533static void
16534reinitvar(void)
16535{
16536 int i;
16537 const char *name;
16538 struct var **vpp, **old;
16539
16540 for (i=0; i<ARRAY_SIZE(varinit); ++i) {
16541 if (i == LINENO_INDEX)
16542 name = "LINENO=";
16543 else if (i == FUNCNAME_INDEX)
16544 name = "FUNCNAME=";
16545 else
16546 name = varinit_data[i].var_text;
16547 vpp = hashvar(name);
16548 if ( (old=findvar(vpp, name)) != NULL ) {
16549 varinit[i] = **old;
16550 *old = varinit+i;
16551 }
16552 varinit[i].var_func = varinit_data[i].var_func;
16553 }
16554 vlineno.var_text = linenovar;
16555 vfuncname.var_text = funcnamevar;
16556}
16557
16558static void
16559spawn_forkshell(struct forkshell *fs, struct job *jp, union node *n, int mode)
16560{
16561 struct forkshell *new;
16562 char buf[32];
16563 const char *argv[] = { "sh", "--fs", NULL, NULL };
16564 intptr_t ret;
16565
16566 new = forkshell_prepare(fs);
16567 if (new == NULL)
16568 goto fail;
16569
16570 new->mode = mode;
16571 new->nprocs = jp == NULL ? 0 : jp->nprocs;
16572#if JOBS_WIN32
16573 new->jpnull = jp == NULL;
16574#endif
16575 sprintf(buf, "%p", new->hMapFile);
16576 argv[2] = buf;
16577 ret = spawnve(P_NOWAIT, bb_busybox_exec_path, (char *const *)argv, NULL);
16578 CloseHandle(new->hMapFile);
16579 UnmapViewOfFile(new);
16580 if (ret == -1) {
16581 fail:
16582 if (jp)
16583 freejob(jp);
16584 ash_msg_and_raise_error("unable to spawn shell");
16585 }
16586 forkparent(jp, n, mode, (HANDLE)ret);
16587}
16588
16589/*
16590 * forkshell_prepare() and friends
16591 *
16592 * The sequence is as follows:
16593 * - funcblocksize is initialized
16594 * - forkshell_size(fs) is called to calculate the exact memory needed
16595 * - a new struct is allocated
16596 * - funcblock, funcstring, relocate are initialized from the new block
16597 * - forkshell_copy(fs) is called to copy recursively everything over
16598 * it will record all relocations along the way
16599 *
16600 * When this memory is mapped elsewhere, pointer fixup will be needed
16601 */
16602
16603/* redefine without test that fs_size is nonzero */
16604#undef SAVE_PTR
16605#undef SAVE_PTR2
16606#undef SAVE_PTR3
16607#define SAVE_PTR(dst,note,flag) {MARK_PTR(dst,note,flag);}
16608
16609static int align_len(const char *s)
16610{
16611 return s ? SHELL_ALIGN(strlen(s)+1) : 0;
16612}
16613
16614struct datasize {
16615 int funcblocksize;
16616 int funcstringsize;
16617};
16618
16619#define SLIST_SIZE_BEGIN(name,type) \
16620static struct datasize \
16621name(struct datasize ds, type *p) \
16622{ \
16623 while (p) { \
16624 ds.funcblocksize += sizeof(type);
16625 /* do something here with p */
16626#define SLIST_SIZE_END() \
16627 p = p->next; \
16628 } \
16629 return ds; \
16630}
16631
16632#define SLIST_COPY_BEGIN(name,type) \
16633static type * \
16634name(type *vp) \
16635{ \
16636 type *start; \
16637 type **vpp; \
16638 vpp = &start; \
16639 while (vp) { \
16640 *vpp = funcblock; \
16641 funcblock = (char *) funcblock + sizeof(type);
16642 /* do something here with vpp and vp */
16643#define SLIST_COPY_END() \
16644 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE); \
16645 vp = vp->next; \
16646 vpp = &(*vpp)->next; \
16647 } \
16648 *vpp = NULL; \
16649 return start; \
16650}
16651
16652/*
16653 * struct var
16654 */
16655SLIST_SIZE_BEGIN(var_size,struct var)
16656ds.funcstringsize += align_len(p->var_text);
16657SLIST_SIZE_END()
16658
16659SLIST_COPY_BEGIN(var_copy,struct var)
16660(*vpp)->var_text = nodeckstrdup(vp->var_text);
16661(*vpp)->flags = vp->flags;
16662(*vpp)->var_func = NULL;
16663SAVE_PTR((*vpp)->var_text, xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"), FREE);
16664SLIST_COPY_END()
16665
16666/*
16667 * struct tblentry
16668 */
16669static struct datasize
16670tblentry_size(struct datasize ds, struct tblentry *tep)
16671{
16672 while (tep) {
16673 ds.funcblocksize += sizeof(struct tblentry) + align_len(tep->cmdname);
16674 /* CMDBUILTIN, e->param.cmd needs no pointer relocation */
16675 if (tep->cmdtype == CMDFUNCTION) {
16676 ds.funcblocksize += offsetof(struct funcnode, n);
16677 ds.funcblocksize = calcsize(ds.funcblocksize, &tep->param.func->n);
16678 }
16679 tep = tep->next;
16680 }
16681 return ds;
16682}
16683
16684static struct tblentry *
16685tblentry_copy(struct tblentry *tep)
16686{
16687 struct tblentry *start;
16688 struct tblentry **newp;
16689 int size;
16690
16691 newp = &start;
16692 while (tep) {
16693 *newp = funcblock;
16694 size = sizeof(struct tblentry) + align_len(tep->cmdname);
16695
16696 funcblock = (char *) funcblock + size;
16697 memcpy(*newp, tep, sizeof(struct tblentry)+strlen(tep->cmdname));
16698 switch (tep->cmdtype) {
16699 case CMDBUILTIN:
16700 /* Save index of builtin, not pointer; fixed by forkshell_init() */
16701 (*newp)->param.index = tep->param.cmd - builtintab;
16702 break;
16703 case CMDFUNCTION:
16704 (*newp)->param.func = funcblock;
16705 funcblock = (char *) funcblock + offsetof(struct funcnode, n);
16706 copynode(&tep->param.func->n);
16707 SAVE_PTR((*newp)->param.func, "param.func", NO_FREE);
16708 break;
16709 default:
16710 break;
16711 }
16712 SAVE_PTR((*newp)->next, xasprintf("cmdname '%s'", tep->cmdname), FREE);
16713 tep = tep->next;
16714 newp = &(*newp)->next;
16715 }
16716 *newp = NULL;
16717 return start;
16718}
16719
16720static struct datasize
16721cmdtable_size(struct datasize ds)
16722{
16723 int i;
16724 ds.funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE;
16725 for (i = 0; i < CMDTABLESIZE; i++)
16726 ds = tblentry_size(ds, cmdtable[i]);
16727 return ds;
16728}
16729
16730static struct tblentry **
16731cmdtable_copy(void)
16732{
16733 struct tblentry **new = funcblock;
16734 int i;
16735
16736 funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE;
16737 for (i = 0; i < CMDTABLESIZE; i++) {
16738 new[i] = tblentry_copy(cmdtable[i]);
16739 SAVE_PTR(new[i], xasprintf("cmdtable[%d]", i), FREE);
16740 }
16741 return new;
16742}
16743
16744#if ENABLE_ASH_ALIAS
16745/*
16746 * struct alias
16747 */
16748SLIST_SIZE_BEGIN(alias_size,struct alias)
16749ds.funcstringsize += align_len(p->name);
16750ds.funcstringsize += align_len(p->val);
16751SLIST_SIZE_END()
16752
16753SLIST_COPY_BEGIN(alias_copy,struct alias)
16754(*vpp)->name = nodeckstrdup(vp->name);
16755(*vpp)->val = nodeckstrdup(vp->val);
16756(*vpp)->flag = vp->flag;
16757SAVE_PTR((*vpp)->name, xasprintf("(*vpp)->name '%s'", vp->name ?: "NULL"), FREE);
16758SAVE_PTR((*vpp)->val, xasprintf("(*vpp)->val '%s'", vp->val ?: "NULL"), FREE);
16759SLIST_COPY_END()
16760
16761static struct datasize
16762atab_size(struct datasize ds)
16763{
16764 int i;
16765 ds.funcblocksize += sizeof(struct alias *)*ATABSIZE;
16766 for (i = 0; i < ATABSIZE; i++)
16767 ds = alias_size(ds, atab[i]);
16768 return ds;
16769}
16770
16771static struct alias **
16772atab_copy(void)
16773{
16774 struct alias **new = funcblock;
16775 int i;
16776
16777 funcblock = (char *) funcblock + sizeof(struct alias *)*ATABSIZE;
16778 for (i = 0; i < ATABSIZE; i++) {
16779 new[i] = alias_copy(atab[i]);
16780 SAVE_PTR(new[i], xasprintf("atab[%d]", i), FREE);
16781 }
16782 return new;
16783}
16784#endif
16785
16786/*
16787 * char **
16788 */
16789static struct datasize
16790argv_size(struct datasize ds, char **p)
16791{
16792 if (p) {
16793 while (*p) {
16794 ds.funcblocksize += sizeof(char *);
16795 ds.funcstringsize += align_len(*p);
16796 p++;
16797 }
16798 ds.funcblocksize += sizeof(char *);
16799 }
16800 return ds;
16801}
16802
16803static char **
16804argv_copy(char **p)
16805{
16806 char **new, **start = funcblock;
16807#if FORKSHELL_DEBUG
16808 int i = 0;
16809#endif
16810
16811 if (p) {
16812 while (*p) {
16813 new = funcblock;
16814 funcblock = (char *) funcblock + sizeof(char *);
16815 *new = nodeckstrdup(*p);
16816 SAVE_PTR(*new, xasprintf("argv[%d] '%s'", i++, *p), FREE);
16817 p++;
16818 }
16819 new = funcblock;
16820 funcblock = (char *) funcblock + sizeof(char *);
16821 *new = NULL;
16822 return start;
16823 }
16824 return NULL;
16825}
16826
16827#if MAX_HISTORY
16828static struct datasize
16829history_size(struct datasize ds)
16830{
16831 int i;
16832 line_input_t *st = line_input_state;
16833
16834 ds.funcblocksize += sizeof(char *) * st->cnt_history;
16835 for (i = 0; i < st->cnt_history; i++) {
16836 ds.funcstringsize += align_len(st->history[i]);
16837 }
16838 return ds;
16839}
16840
16841static char **
16842history_copy(void)
16843{
16844 line_input_t *st = line_input_state;
16845 char **new = funcblock;
16846 int i;
16847
16848 funcblock = (char *)funcblock + sizeof(char *) * st->cnt_history;
16849 for (i = 0; i < st->cnt_history; i++) {
16850 new[i] = nodeckstrdup(st->history[i]);
16851 SAVE_PTR(new[i],
16852 xasprintf("history[%d] '%s'", i, st->history[i]), FREE);
16853 }
16854 return new;
16855}
16856#endif
16857
16858#if JOBS_WIN32
16859/*
16860 * struct procstat
16861 */
16862static struct datasize
16863procstat_size(struct datasize ds, int nj)
16864{
16865 struct job *jp = jobtab + nj;
16866
16867 if (jp->ps != &jp->ps0)
16868 ds.funcblocksize += sizeof(struct procstat) * jp->nprocs;
16869
16870 for (int i = 0; i < jp->nprocs; i++)
16871 ds.funcstringsize += align_len(jp->ps[i].ps_cmd);
16872
16873 return ds;
16874}
16875
16876static struct procstat *
16877procstat_copy(int nj)
16878{
16879 struct job *jp = jobtab + nj;
16880 struct procstat *new = funcblock;
16881
16882 funcblock = (char *)funcblock + sizeof(struct procstat) * jp->nprocs;
16883 memcpy(new, jp->ps, sizeof(struct procstat) * jp->nprocs);
16884
16885 for (int i = 0; i < jp->nprocs; i++) {
16886 new[i].ps_cmd = nodeckstrdup(jp->ps[i].ps_cmd);
16887 SAVE_PTR(new[i].ps_cmd,
16888 xasprintf("jobtab[%d].ps[%d].ps_cmd '%s'",
16889 nj, i, jp->ps[i].ps_cmd), FREE);
16890 }
16891 return new;
16892}
16893
16894/*
16895 * struct jobs
16896 */
16897static struct datasize
16898jobtab_size(struct datasize ds)
16899{
16900 ds.funcblocksize += sizeof(struct job) * njobs;
16901 for (int i = 0; i < njobs; i++) {
16902 if (jobtab[i].used)
16903 ds = procstat_size(ds, i);
16904 }
16905 return ds;
16906}
16907
16908static struct job *
16909jobtab_copy(void)
16910{
16911 struct job *new = funcblock;
16912 int i;
16913
16914 funcblock = (char *)funcblock + sizeof(struct job) * njobs;
16915 memcpy(new, jobtab, sizeof(struct job) * njobs);
16916
16917 for (i = 0; i < njobs; i++) {
16918 if (!jobtab[i].used)
16919 continue;
16920
16921 if (jobtab[i].ps == &jobtab[i].ps0) {
16922 new[i].ps0.ps_cmd = nodeckstrdup(jobtab[i].ps0.ps_cmd);
16923 SAVE_PTR(new[i].ps0.ps_cmd,
16924 xasprintf("jobtab[%d].ps0.ps_cmd '%s'",
16925 i, jobtab[i].ps0.ps_cmd), FREE);
16926 new[i].ps = &new[i].ps0;
16927 } else if (jobtab[i].nprocs) {
16928 new[i].ps = procstat_copy(i);
16929 } else {
16930 new[i].ps = NULL;
16931 }
16932 SAVE_PTR(new[i].ps, xasprintf("jobtab[%d].ps", i), FREE);
16933
16934 if (jobtab[i].prev_job) {
16935 new[i].prev_job = new + (jobtab[i].prev_job - jobtab);
16936 SAVE_PTR(new[i].prev_job,
16937 xasprintf("jobtab[%d].prev_job", i), FREE);
16938 }
16939 }
16940 return new;
16941}
16942#endif
16943
16944/*
16945 * struct redirtab
16946 */
16947static int
16948redirtab_size(int funcblocksize, struct redirtab *rdtp)
16949{
16950 while (rdtp) {
16951 funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
16952 rdtp = rdtp->next;
16953 }
16954 return funcblocksize;
16955}
16956
16957static struct redirtab *
16958redirtab_copy(struct redirtab *rdtp)
16959{
16960 struct redirtab *start;
16961 struct redirtab **vpp;
16962
16963 vpp = &start;
16964 while (rdtp) {
16965 int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
16966 *vpp = funcblock;
16967 funcblock = (char *) funcblock + size;
16968 memcpy(*vpp, rdtp, size);
16969 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE);
16970 rdtp = rdtp->next;
16971 vpp = &(*vpp)->next;
16972 }
16973 *vpp = NULL;
16974 return start;
16975}
16976
16977static struct datasize
16978globals_var_size(struct datasize ds)
16979{
16980 int i;
16981
16982 ds.funcblocksize += sizeof(struct globals_var);
16983 ds.funcstringsize += align_len(funcname);
16984 ds = argv_size(ds, shellparam.p);
16985 ds.funcblocksize = redirtab_size(ds.funcblocksize, redirlist);
16986 for (i = 0; i < VTABSIZE; i++)
16987 ds = var_size(ds, vartab[i]);
16988 return ds;
16989}
16990
16991#undef funcname
16992#undef shellparam
16993#undef redirlist
16994#undef vartab
16995static struct globals_var *
16996globals_var_copy(void)
16997{
16998 int i;
16999 struct globals_var *gvp, *new;
17000
17001 gvp = ash_ptr_to_globals_var;
17002 new = funcblock;
17003 funcblock = (char *) funcblock + sizeof(struct globals_var);
17004 memcpy(new, gvp, sizeof(struct globals_var));
17005
17006 new->funcname = nodeckstrdup(gvp->funcname);
17007 SAVE_PTR(new->funcname, xasprintf("funcname '%s'", gvp->funcname ?: "NULL"), FREE);
17008
17009 /* shparam */
17010 new->shellparam.malloced = 0;
17011 new->shellparam.p = argv_copy(gvp->shellparam.p);
17012 SAVE_PTR(new->shellparam.p, "shellparam.p", NO_FREE);
17013
17014 new->redirlist = redirtab_copy(gvp->redirlist);
17015 SAVE_PTR(new->redirlist, "redirlist", NO_FREE);
17016
17017 for (i = 0; i < VTABSIZE; i++) {
17018 new->vartab[i] = var_copy(gvp->vartab[i]);
17019 SAVE_PTR(new->vartab[i], xasprintf("vartab[%d]", i), FREE);
17020 }
17021
17022 return new;
17023}
17024
17025static struct datasize
17026globals_misc_size(struct datasize ds)
17027{
17028 ds.funcblocksize += sizeof(struct globals_misc);
17029 ds.funcstringsize += align_len(minusc);
17030 if (curdir != nullstr)
17031 ds.funcstringsize += align_len(curdir);
17032 if (physdir != nullstr)
17033 ds.funcstringsize += align_len(physdir);
17034 ds.funcstringsize += align_len(arg0);
17035 ds.funcstringsize += align_len(commandname);
17036 for (int i = 0; i < ARRAY_SIZE(trap); i++)
17037 ds.funcstringsize += align_len(trap[i]);
17038 return ds;
17039}
17040
17041#undef minusc
17042#undef curdir
17043#undef physdir
17044#undef arg0
17045#undef commandname
17046#undef nullstr
17047#undef trap
17048static struct globals_misc *
17049globals_misc_copy(void)
17050{
17051 struct globals_misc *p = ash_ptr_to_globals_misc;
17052 struct globals_misc *new = funcblock;
17053
17054 funcblock = (char *) funcblock + sizeof(struct globals_misc);
17055 memcpy(new, p, sizeof(struct globals_misc));
17056
17057 new->minusc = nodeckstrdup(p->minusc);
17058 new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr;
17059 new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr;
17060 new->arg0 = nodeckstrdup(p->arg0);
17061 new->commandname = nodeckstrdup(p->commandname);
17062 SAVE_PTR(new->minusc, xasprintf("minusc '%s'", p->minusc ?: "NULL"), FREE);
17063 SAVE_PTR(new->curdir,
17064 xasprintf("curdir '%s'", new->curdir ?: "NULL"), FREE);
17065 SAVE_PTR(new->physdir,
17066 xasprintf("physdir '%s'", new->physdir ?: "NULL"), FREE);
17067 SAVE_PTR(new->arg0, xasprintf("arg0 '%s'", p->arg0 ?: "NULL"), FREE);
17068 SAVE_PTR(new->commandname,
17069 xasprintf("commandname '%s'", p->commandname ?: "NULL"), FREE);
17070 for (int i = 0; i < ARRAY_SIZE(p->trap); i++) {
17071 new->trap[i] = nodeckstrdup(p->trap[i]);
17072 SAVE_PTR(new->trap[i], xasprintf("trap[%d]", i), FREE);
17073 }
17074 return new;
17075}
17076
17077static struct datasize
17078forkshell_size(struct forkshell *fs)
17079{
17080 struct datasize ds = {0, 0};
17081
17082 ds.funcstringsize += align_len(fs->path);
17083 if (fs->fpid == FS_OPENHERE)
17084 return ds;
17085
17086 ds = globals_var_size(ds);
17087 ds = globals_misc_size(ds);
17088 ds = cmdtable_size(ds);
17089
17090 ds.funcblocksize = calcsize(ds.funcblocksize, fs->n);
17091 ds = argv_size(ds, fs->argv);
17092
17093 if ((ENABLE_ASH_ALIAS || MAX_HISTORY || JOBS_WIN32) &&
17094 fs->fpid != FS_SHELLEXEC) {
17095#if ENABLE_ASH_ALIAS
17096 ds = atab_size(ds);
17097#endif
17098#if MAX_HISTORY
17099 if (line_input_state)
17100 ds = history_size(ds);
17101#endif
17102#if JOBS_WIN32
17103 ds = jobtab_size(ds);
17104#endif
17105 }
17106 return ds;
17107}
17108
17109static void
17110forkshell_copy(struct forkshell *fs, struct forkshell *new)
17111{
17112 memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */
17113
17114 new->path = nodeckstrdup(fs->path);
17115 SAVE_PTR(new->path, xasprintf("path '%s'", fs->path ?: "NULL"), FREE);
17116 if (fs->fpid == FS_OPENHERE)
17117 return;
17118
17119 new->gvp = globals_var_copy();
17120 new->gmp = globals_misc_copy();
17121 new->cmdtable = cmdtable_copy();
17122 SAVE_PTR(new->gvp, "gvp", NO_FREE);
17123 SAVE_PTR(new->gmp, "gmp", NO_FREE);
17124 SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE);
17125
17126 new->n = copynode(fs->n);
17127 new->argv = argv_copy(fs->argv);
17128 SAVE_PTR(new->n, "n", NO_FREE);
17129 SAVE_PTR(new->argv, "argv", NO_FREE);
17130
17131 if ((ENABLE_ASH_ALIAS || MAX_HISTORY || JOBS_WIN32) &&
17132 fs->fpid != FS_SHELLEXEC) {
17133#if ENABLE_ASH_ALIAS
17134 new->atab = atab_copy();
17135 SAVE_PTR(new->atab, "atab", NO_FREE);
17136#endif
17137#if MAX_HISTORY
17138 if (line_input_state) {
17139 new->history = history_copy();
17140 SAVE_PTR(new->history, "history", NO_FREE);
17141 new->cnt_history = line_input_state->cnt_history;
17142 }
17143#endif
17144#if JOBS_WIN32
17145 if (njobs) {
17146 new->jobtab = jobtab_copy();
17147 SAVE_PTR(new->jobtab, "jobtab", NO_FREE);
17148 new->njobs = njobs;
17149 if (curjob) {
17150 new->curjob = new->jobtab + (curjob - jobtab);
17151 SAVE_PTR(new->curjob, "curjob", NO_FREE);
17152 }
17153 }
17154#endif
17155 }
17156}
17157
17158#if FORKSHELL_DEBUG
17159#define NUM_BLOCKS FUNCSTRING
17160enum {GVP, GMP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, JOBTAB, FUNCSTRING};
17161
17162/* fp0 and notes can each be NULL */
17163static void
17164forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
17165{
17166 FILE *fp;
17167 void *lfuncblock;
17168 char *lfuncstring;
17169 char *lrelocate;
17170 char *s;
17171 int count, i, total, bitmapsize;
17172 int size[NUM_BLOCKS];
17173 char *lptr[NUM_BLOCKS+1];
17174 const char *fsname[] = {
17175 "FS_OPENHERE",
17176 "FS_EVALBACKCMD",
17177 "FS_EVALSUBSHELL",
17178 "FS_EVALPIPE",
17179 "FS_SHELLEXEC"
17180 };
17181
17182 if (fp0 != NULL) {
17183 fp = fp0;
17184 }
17185 else {
17186 char name[64];
17187 static int num = 0;
17188
17189 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
17190 if ((fp=fopen(name, "w")) == NULL)
17191 return;
17192 }
17193
17194 bitmapsize = (fs->relocatesize + 7)/8;
17195 total = sizeof(struct forkshell) + fs->funcblocksize +
17196 fs->funcstringsize + bitmapsize;
17197 fprintf(fp, "total size %6d = %d + %d + %d + %d = %d\n",
17198 fs->size + bitmapsize,
17199 (int)sizeof(struct forkshell), fs->funcblocksize,
17200 fs->funcstringsize, bitmapsize, total);
17201
17202 lfuncblock = (char *)(fs + 1);
17203 lfuncstring = (char *)lfuncblock + fs->funcblocksize;
17204 lrelocate = (char *)lfuncstring + fs->funcstringsize;
17205
17206 /* funcblocksize is zero for FS_OPENHERE */
17207 if (fs->funcblocksize != 0) {
17208 /* Depending on the configuration and the type of forkshell
17209 * some items may not be present. */
17210 lptr[FUNCSTRING] = lfuncstring;
17211#if JOBS_WIN32
17212 lptr[JOBTAB] = fs->jobtab ? (char *)fs->jobtab : lptr[FUNCSTRING];
17213#else
17214 lptr[JOBTAB] = lptr[FUNCSTRING];
17215#endif
17216#if MAX_HISTORY
17217 lptr[HISTORY] = fs->history ? (char *)fs->history : lptr[JOBTAB];
17218#else
17219 lptr[HISTORY] = lptr[JOBTAB];
17220#endif
17221 lptr[ATAB] = IF_ASH_ALIAS(fs->atab ? (char *)fs->atab :) lptr[HISTORY];
17222 lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB];
17223 lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV];
17224 lptr[CMDTABLE] = (char *)fs->cmdtable;
17225 lptr[GMP] = (char *)fs->gmp;
17226 lptr[GVP] = (char *)fs->gvp;
17227
17228 fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize);
17229 total = 0;
17230 for (i=0; i<NUM_BLOCKS; ++i) {
17231 size[i] = (int)(lptr[i+1] - lptr[i]);
17232 total += size[i];
17233 fprintf(fp, "%d %c ", size[i], i == NUM_BLOCKS - 1 ? '=' : '+');
17234 }
17235 fprintf(fp, "%d\n\n", total);
17236 }
17237 else {
17238 fprintf(fp, "\n");
17239 }
17240
17241 fprintf(fp, "%s\n\n", fsname[fs->fpid]);
17242 fprintf(fp, "--- relocate ---\n");
17243 count = 0;
17244 for (i = 0; i < fs->relocatesize; ++i) {
17245 if (lrelocate[i/8] & (1 << i % 8)) {
17246 char **ptr = (char **)((char *)fs + i * sizeof(char *));
17247 fprintf(fp, "%p %p %s\n", ptr, *ptr,
17248 notes && notes[i] ? notes[i] : "");
17249 ++count;
17250 }
17251 }
17252 fprintf(fp, "--- %d relocations ---\n\n", count);
17253
17254 fprintf(fp, "--- funcstring ---\n");
17255 count = 0;
17256 s = lfuncstring;
17257 while (s-lfuncstring < fs->funcstringsize) {
17258 if (!*s) {
17259 ++s;
17260 continue;
17261 }
17262 fprintf(fp, "%p '%s'\n", s, s);
17263 s += strlen(s)+1;
17264 ++count;
17265 }
17266 fprintf(fp, "--- %d strings ---\n", count);
17267
17268 if (fp0 == NULL)
17269 fclose(fp);
17270}
17271#endif
17272
17273static struct forkshell *
17274forkshell_prepare(struct forkshell *fs)
17275{
17276 struct forkshell *new;
17277 struct datasize ds;
17278 int size, relocatesize, bitmapsize;
17279 HANDLE h;
17280 SECURITY_ATTRIBUTES sa;
17281#if FORKSHELL_DEBUG
17282 char *relocate;
17283 char name[64];
17284 FILE *fp;
17285 static int num = 0;
17286#endif
17287
17288 /* calculate size of structure, funcblock and funcstring */
17289 ds = forkshell_size(fs);
17290 size = sizeof(struct forkshell) + ds.funcblocksize + ds.funcstringsize;
17291 relocatesize = (sizeof(struct forkshell) + ds.funcblocksize)/sizeof(char *);
17292 bitmapsize = (relocatesize + 7)/8;
17293
17294 /* Allocate shared memory region */
17295 memset(&sa, 0, sizeof(sa));
17296 sa.nLength = sizeof(sa);
17297 sa.lpSecurityDescriptor = NULL;
17298 sa.bInheritHandle = TRUE;
17299 h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0,
17300 size+bitmapsize, NULL);
17301
17302 /* Initialise pointers */
17303 new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
17304 if (new == NULL)
17305 return NULL;
17306 fs_size = size;
17307 fs_start = new;
17308 funcblock = (char *)(new + 1);
17309 funcstring_end = (char *)new + size;
17310#if FORKSHELL_DEBUG
17311 fs_funcstring = (char *)new + sizeof(struct forkshell) + ds.funcblocksize;
17312 relocate = (char *)new + size;
17313 annot = (const char **)xzalloc(sizeof(char *)*relocatesize);
17314 annot_free = xzalloc(relocatesize);
17315#endif
17316
17317 /* Now pack them all */
17318 forkshell_copy(fs, new);
17319
17320 /* Finish it up */
17321 new->size = size;
17322 new->relocatesize = relocatesize;
17323 new->old_base = (char *)new;
17324 new->hMapFile = h;
17325#if FORKSHELL_DEBUG
17326 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
17327 if ((fp=fopen(name, "w")) != NULL) {
17328 int i;
17329
17330 new->funcblocksize = (char *)funcblock - (char *)(new + 1);
17331 new->funcstringsize = (char *)new + size - funcstring_end;
17332
17333 /* perform some sanity checks on pointers */
17334 fprintf(fp, "forkshell %p %6d\n", new, (int)sizeof(*new));
17335 fprintf(fp, "funcblock %p %6d\n", new+1, new->funcblocksize);
17336 fprintf(fp, "funcstring %p %6d\n", funcstring_end,
17337 new->funcstringsize);
17338 if ((char *)funcblock != funcstring_end)
17339 fprintf(fp, " funcstring != end funcblock + 1 %p\n", funcblock);
17340 fprintf(fp, "relocate %p %6d\n\n", relocate, bitmapsize);
17341
17342 forkshell_print(fp, new, annot);
17343
17344 for (i = 0; i < relocatesize; ++i) {
17345 if (annot_free[i]) {
17346 free((void *)annot[i]);
17347 }
17348 }
17349 free(annot);
17350 free(annot_free);
17351 annot = NULL;
17352 fclose(fp);
17353 }
17354#endif
17355 return new;
17356}
17357
17358#undef trap_ptr
17359static void
17360forkshell_init(const char *idstr)
17361{
17362 struct forkshell *fs;
17363 void *map_handle;
17364 HANDLE h;
17365 int i;
17366 char **ptr;
17367 char *lrelocate;
17368 struct jmploc jmploc;
17369
17370 if (sscanf(idstr, "%p", &map_handle) != 1)
17371 return;
17372
17373 h = (HANDLE)map_handle;
17374 fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
17375 if (!fs)
17376 return;
17377
17378 /* this memory can't be freed */
17379 sticky_mem_start = fs;
17380 sticky_mem_end = (char *) fs + fs->size;
17381
17382 /* pointer fixup */
17383 lrelocate = (char *)fs + fs->size;
17384 for (i = 0; i < fs->relocatesize; i++) {
17385 if (lrelocate[i/8] & (1 << i % 8)) {
17386 ptr = (char **)((char *)fs + i * sizeof(char *));
17387 if (*ptr)
17388 *ptr = (char *)fs + (*ptr - fs->old_base);
17389 }
17390 }
17391
17392 if (fs->fpid == FS_OPENHERE)
17393 goto end;
17394
17395 /* Now fix up stuff that can't be transferred */
17396 for (i = 0; i < CMDTABLESIZE; i++) {
17397 struct tblentry *e = fs->cmdtable[i];
17398 while (e) {
17399 if (e->cmdtype == CMDBUILTIN)
17400 e->param.cmd = builtintab + e->param.index;
17401 e = e->next;
17402 }
17403 }
17404 fs->gmp->trap_ptr = fs->gmp->trap;
17405
17406 /* Set global variables */
17407 ASSIGN_CONST_PTR(&ash_ptr_to_globals_var, fs->gvp);
17408 ASSIGN_CONST_PTR(&ash_ptr_to_globals_misc, fs->gmp);
17409 cmdtable = fs->cmdtable;
17410#if ENABLE_ASH_ALIAS
17411 atab = fs->atab; /* will be NULL for FS_SHELLEXEC */
17412#endif
17413#if MAX_HISTORY
17414 if (fs->cnt_history) {
17415 line_input_state = new_line_input_t(FOR_SHELL);
17416 line_input_state->cnt_history = fs->cnt_history;
17417 for (i = 0; i < line_input_state->cnt_history; i++)
17418 line_input_state->history[i] = fs->history[i];
17419 }
17420#endif
17421#if JOBS_WIN32
17422 jobtab = fs->jobtab;
17423 njobs = fs->njobs;
17424 curjob = fs->curjob;
17425#endif
17426
17427 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
17428
17429 reinitvar();
17430
17431 if (setjmp(jmploc.loc)) {
17432 exitreset();
17433 exitshell();
17434 }
17435 exception_handler = &jmploc;
17436
17437 shlvl++;
17438 if (fs->mode == FORK_BG) {
17439 SetConsoleCtrlHandler(NULL, TRUE);
17440 if (fs->nprocs == 0) {
17441 close(0);
17442 if (open(bb_dev_null, O_RDONLY) != 0)
17443 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
17444 }
17445 }
17446 else {
17447 SetConsoleCtrlHandler(ctrl_handler, TRUE);
17448 }
17449
17450 if (fs->n && fs->n->type == NCMD /* is it single cmd? */
17451 /* && n->ncmd.args->type == NARG - always true? */
17452 && fs->n->ncmd.args && strcmp(fs->n->ncmd.args->narg.text, "trap") == 0
17453 && fs->n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
17454 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
17455 ) {
17456 TRACE(("Trap hack\n"));
17457 /* Save trap handler strings for trap builtin to print */
17458 fs->gmp->trap_ptr = xmemdup(fs->gmp->trap, sizeof(fs->gmp->trap));
17459 /* Fall through into clearing traps */
17460 }
17461 clear_traps();
17462#if JOBS_WIN32
17463 /* do job control only in root shell */
17464 doing_jobctl = 0;
17465
17466 if (fs->n && fs->n->type == NCMD && fs->n->ncmd.args &&
17467 strcmp(fs->n->ncmd.args->narg.text, "jobs") == 0) {
17468 TRACE(("Job hack\n"));
17469 if (!fs->jpnull)
17470 freejob(curjob);
17471 goto end;
17472 }
17473 for (struct job *jp = curjob; jp; jp = jp->prev_job)
17474 freejob(jp);
17475#endif
17476 end:
17477 forkshell_child(fs);
17478}
17479
17480#undef free
17481static void
17482sticky_free(void *base)
17483{
17484 if (base >= sticky_mem_start && base < sticky_mem_end)
17485 return;
17486 free(base);
17487}
17488#endif
14823 17489
14824/*- 17490/*-
14825 * Copyright (c) 1989, 1991, 1993, 1994 17491 * Copyright (c) 1989, 1991, 1993, 1994