aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--shell/Config.src7
-rw-r--r--shell/Kbuild.src1
-rw-r--r--shell/ash.c2841
-rw-r--r--shell/ash_test/ash-read/read_ifs2.right9
-rwxr-xr-xshell/ash_test/ash-read/read_ifs2.tests9
-rw-r--r--shell/ash_test/ash-read/read_t.right8
-rwxr-xr-xshell/ash_test/ash-read/read_t.tests18
-rw-r--r--shell/ash_test/printenv.c4
-rw-r--r--shell/ash_test/recho.c2
-rw-r--r--shell/hush.c486
-rwxr-xr-xshell/hush_leaktool.sh18
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests2
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests2
-rwxr-xr-xshell/hush_test/hush-misc/sig_exitcode.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait1.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait2.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait3.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait4.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait5.tests3
-rwxr-xr-xshell/hush_test/hush-misc/wait6.tests3
-rw-r--r--shell/hush_test/hush-read/read_t.right8
-rwxr-xr-xshell/hush_test/hush-read/read_t.tests18
-rwxr-xr-xshell/hush_test/hush-signals/catch.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal1.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal8.tests3
-rwxr-xr-xshell/hush_test/hush-signals/signal_read2.tests3
-rwxr-xr-xshell/hush_test/hush-signals/subshell.tests3
-rwxr-xr-xshell/hush_test/run-all7
-rw-r--r--shell/math.h2
-rw-r--r--shell/random.c11
-rw-r--r--shell/random.h3
-rw-r--r--shell/shell_common.c105
34 files changed, 3283 insertions, 316 deletions
diff --git a/shell/Config.src b/shell/Config.src
index 5efbf9995..5b3fe08f3 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -166,9 +166,10 @@ config FEATURE_SH_HISTFILESIZE
166 default y 166 default y
167 depends on SHELL_ASH || SHELL_HUSH 167 depends on SHELL_ASH || SHELL_HUSH
168 help 168 help
169 This option makes busybox shells to use $HISTFILESIZE variable 169 This option makes busybox shells to use $HISTSIZE and
170 to set shell history size. Note that its max value is capped 170 $HISTFILESIZE variables to set shell history size.
171 by "History size" setting in library tuning section. 171 Note that its max value is capped by "History size" setting
172 in library tuning section.
172 173
173config FEATURE_SH_EMBEDDED_SCRIPTS 174config FEATURE_SH_EMBEDDED_SCRIPTS
174 bool "Embed scripts in the binary" 175 bool "Embed scripts in the binary"
diff --git a/shell/Kbuild.src b/shell/Kbuild.src
index 6bba4989f..a287fce4e 100644
--- a/shell/Kbuild.src
+++ b/shell/Kbuild.src
@@ -9,3 +9,4 @@ lib-y:=
9INSERT 9INSERT
10 10
11lib-$(CONFIG_FEATURE_SH_MATH) += math.o 11lib-$(CONFIG_FEATURE_SH_MATH) += math.o
12lib-$(CONFIG_FEATURE_PRNG_SHELL) += random.o
diff --git a/shell/ash.c b/shell/ash.c
index 9173b8608..605215e41 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -15,6 +15,20 @@
15 * 15 *
16 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 16 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
17 */ 17 */
18
19/*
20 * MinGW notes
21 *
22 * - Environment variables from Windows will all be turned to uppercase.
23 * - PATH accepts both ; and : as separator, but can't be mixed
24 * - command without ".exe" extension is still understood as executable
25 * - shell scripts on the path are detected by the presence of '#!'
26 * - both / and \ are supported in PATH. Usually you must use /
27 * - job control doesn't work, though the jobs builtin is available
28 * - trap doesn't work for signals, only EXIT
29 * - /dev/null is supported for redirection
30 */
31
18//config:config SHELL_ASH 32//config:config SHELL_ASH
19//config: bool #hidden option 33//config: bool #hidden option
20//config: depends on !NOMMU 34//config: depends on !NOMMU
@@ -170,11 +184,36 @@
170//config: you to run the specified command or builtin, 184//config: you to run the specified command or builtin,
171//config: even when there is a function with the same name. 185//config: even when there is a function with the same name.
172//config: 186//config:
187//config:
188//config:config ASH_NOCONSOLE
189//config: bool "'noconsole' option"
190//config: default y
191//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
192//config: help
193//config: Enable support for the 'noconsole' option, which attempts to
194//config: conceal the console normally associated with a command line
195//config: application. This may be useful when running a shell script
196//config: from a GUI application. Also the 'noiconify' option, which
197//config: controls whether the console is iconified or hidden.
198//config:
199//config:config ASH_GLOB_OPTIONS
200//config: bool "Globbing options"
201//config: default y
202//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
203//config: help
204//config: Enable support for options to control globbing:
205//config: - 'nocaseglob' allows case-insensitive filename globbing
206//config: - 'nohiddenglob' allows hidden files to be omitted from globbing
207//config: - 'nohidsysglob' allows hidden system files to be omitted
208//config:
173//config:endif # ash options 209//config:endif # ash options
174 210
175//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 211//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
176// APPLET_ODDNAME:name main location suid_type help 212// APPLET_ODDNAME:name main location suid_type help
177//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash)) 213//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
214//applet:IF_PLATFORM_MINGW32(
215//applet:IF_SH_IS_ASH( APPLET_ODDNAME(lash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
216//applet:)
178//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash)) 217//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
179 218
180//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o 219//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o
@@ -195,7 +234,17 @@
195 234
196#define PROFILE 0 235#define PROFILE 0
197 236
237/*
238 * Only one of JOBS or JOBS_WIN32 is enabled at a time (or neither).
239 * JOBS_WIN32 doesn't enable job control, just some job-related features.
240 */
241#if ENABLE_PLATFORM_MINGW32
242#define JOBS_WIN32 ENABLE_ASH_JOB_CONTROL
243#define JOBS 0
244#else
245#define JOBS_WIN32 0
198#define JOBS ENABLE_ASH_JOB_CONTROL 246#define JOBS ENABLE_ASH_JOB_CONTROL
247#endif
199 248
200#include <fnmatch.h> 249#include <fnmatch.h>
201#include <sys/times.h> 250#include <sys/times.h>
@@ -206,6 +255,10 @@
206#else 255#else
207# define NUM_SCRIPTS 0 256# define NUM_SCRIPTS 0
208#endif 257#endif
258#if ENABLE_PLATFORM_MINGW32
259# include <conio.h>
260# include "lazyload.h"
261#endif
209 262
210/* So far, all bash compat is controlled by one config option */ 263/* So far, all bash compat is controlled by one config option */
211/* Separate defines document which part of code implements what */ 264/* Separate defines document which part of code implements what */
@@ -316,10 +369,89 @@ typedef long arith_t;
316# define unlikely(cond) (cond) 369# define unlikely(cond) (cond)
317#endif 370#endif
318 371
372#if !ENABLE_PLATFORM_MINGW32
373# define is_relative_path(path) ((path)[0] != '/')
374#endif
375
319#if !BB_MMU 376#if !BB_MMU
320# error "Do not even bother, ash will not run on NOMMU machine" 377# error "Do not even bother, ash will not run on NOMMU machine"
321#endif 378#endif
322 379
380#if ENABLE_PLATFORM_MINGW32
381# define FORKSHELL_DEBUG 0
382
383union node;
384struct strlist;
385struct job;
386
387struct forkshell {
388 /* filled by forkshell_copy() */
389 struct globals_var *gvp;
390 struct globals_misc *gmp;
391 struct tblentry **cmdtable;
392#if ENABLE_ASH_ALIAS
393 struct alias **atab;
394#endif
395#if MAX_HISTORY
396 char **history;
397 int cnt_history;
398#endif
399#if JOBS_WIN32
400 struct job *jobtab;
401 unsigned njobs;
402 struct job *curjob;
403#endif
404 /* struct parsefile *g_parsefile; */
405 HANDLE hMapFile;
406 char *old_base;
407 int size;
408# if FORKSHELL_DEBUG
409 int funcblocksize;
410 int funcstringsize;
411# endif
412 int relocatesize;
413
414 /* type of forkshell */
415 int fpid;
416
417 /* generic data, used by forkshell_child */
418 int mode;
419 int nprocs;
420#if JOBS_WIN32
421 int jpnull;
422#endif
423
424 /* optional data, used by forkshell_child */
425 int flags;
426 int fd[3];
427 union node *n;
428 char **argv;
429 char *path;
430};
431
432enum {
433 FS_OPENHERE,
434 FS_EVALBACKCMD,
435 FS_EVALSUBSHELL,
436 FS_EVALPIPE,
437 FS_SHELLEXEC
438};
439
440static struct forkshell* forkshell_prepare(struct forkshell *fs);
441static void forkshell_init(const char *idstr);
442static void *sticky_mem_start, *sticky_mem_end;
443static void sticky_free(void *p);
444# define free(p) sticky_free(p)
445#if !JOBS && !JOBS_WIN32
446#define spawn_forkshell(fs, jp, n, mode) spawn_forkshell(fs, jp, mode)
447#endif
448static void spawn_forkshell(struct forkshell *fs, struct job *jp,
449 union node *n, int mode);
450# if FORKSHELL_DEBUG
451static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes);
452# endif
453#endif
454
323/* ============ Hash table sizes. Configurable. */ 455/* ============ Hash table sizes. Configurable. */
324 456
325#define VTABSIZE 39 457#define VTABSIZE 39
@@ -329,7 +461,7 @@ typedef long arith_t;
329 461
330/* ============ Shell options */ 462/* ============ Shell options */
331 463
332/* If you add/change options hare, update --help text too */ 464/* If you add/change options here, update --help text too */
333static const char *const optletters_optnames[] ALIGN_PTR = { 465static const char *const optletters_optnames[] ALIGN_PTR = {
334 "e" "errexit", 466 "e" "errexit",
335 "f" "noglob", 467 "f" "noglob",
@@ -347,7 +479,11 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
347 "m" "monitor", 479 "m" "monitor",
348 "n" "noexec", 480 "n" "noexec",
349/* Ditto: bash has no "set -s", "set -c" */ 481/* Ditto: bash has no "set -s", "set -c" */
482#if !ENABLE_PLATFORM_MINGW32
350 "s" "", 483 "s" "",
484#else
485 "s" "stdin",
486#endif
351 "c" "", 487 "c" "",
352 "x" "xtrace", 488 "x" "xtrace",
353 "v" "verbose", 489 "v" "verbose",
@@ -364,6 +500,18 @@ static const char *const optletters_optnames[] ALIGN_PTR = {
364 ,"\0" "nolog" 500 ,"\0" "nolog"
365 ,"\0" "debug" 501 ,"\0" "debug"
366#endif 502#endif
503#if ENABLE_PLATFORM_MINGW32
504 ,"X" "winxp"
505#endif
506#if ENABLE_ASH_NOCONSOLE
507 ,"\0" "noconsole"
508 ,"\0" "noiconify"
509#endif
510#if ENABLE_ASH_GLOB_OPTIONS
511 ,"\0" "nocaseglob"
512 ,"\0" "nohiddenglob"
513 ,"\0" "nohidsysglob"
514#endif
367}; 515};
368//bash 4.4.23 also has these opts (with these defaults): 516//bash 4.4.23 also has these opts (with these defaults):
369//braceexpand on 517//braceexpand on
@@ -406,21 +554,36 @@ struct jmploc {
406struct globals_misc { 554struct globals_misc {
407 uint8_t exitstatus; /* exit status of last command */ 555 uint8_t exitstatus; /* exit status of last command */
408 uint8_t back_exitstatus;/* exit status of backquoted command */ 556 uint8_t back_exitstatus;/* exit status of backquoted command */
557#if !ENABLE_PLATFORM_MINGW32
409 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ 558 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
559#endif
410 smallint inps4; /* Prevent PS4 nesting. */ 560 smallint inps4; /* Prevent PS4 nesting. */
411 int savestatus; /* exit status of last command outside traps */ 561 int savestatus; /* exit status of last command outside traps */
412 int rootpid; /* pid of main shell */ 562 int rootpid; /* pid of main shell */
413 /* shell level: 0 for the main shell, 1 for its children, and so on */ 563 /* shell level: 0 for the main shell, 1 for its children, and so on */
414 int shlvl; 564 int shlvl;
565#if ENABLE_PLATFORM_MINGW32
566 int loopnest; /* current loop nesting level */
567#endif
415#define rootshell (!shlvl) 568#define rootshell (!shlvl)
416 int errlinno; 569 int errlinno;
417 570
418 char *minusc; /* argument to -c option */ 571 char *minusc; /* argument to -c option */
572#if ENABLE_PLATFORM_MINGW32
573 char *dirarg; /* argument to -d option */
574 char *title; /* argument to -t option */
575#if ENABLE_SUW32
576 int delayexit; /* set by -N option */
577# endif
578#endif
419 579
420 char *curdir; // = nullstr; /* current working directory */ 580 char *curdir; // = nullstr; /* current working directory */
421 char *physdir; // = nullstr; /* physical working directory */ 581 char *physdir; // = nullstr; /* physical working directory */
422 582
423 char *arg0; /* value of $0 */ 583 char *arg0; /* value of $0 */
584#if ENABLE_PLATFORM_MINGW32
585 char *commandname;
586#endif
424 587
425 struct jmploc *exception_handler; 588 struct jmploc *exception_handler;
426 589
@@ -431,8 +594,10 @@ struct globals_misc {
431 * but we do read it async. 594 * but we do read it async.
432 */ 595 */
433 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ 596 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
597#if !ENABLE_PLATFORM_MINGW32
434 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ 598 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
435 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ 599 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
600#endif
436 smallint exception_type; /* kind of exception: */ 601 smallint exception_type; /* kind of exception: */
437#define EXINT 0 /* SIGINT received */ 602#define EXINT 0 /* SIGINT received */
438#define EXERROR 1 /* a generic error */ 603#define EXERROR 1 /* a generic error */
@@ -467,8 +632,21 @@ struct globals_misc {
467# define nolog optlist[16 + BASH_PIPEFAIL] 632# define nolog optlist[16 + BASH_PIPEFAIL]
468# define debug optlist[17 + BASH_PIPEFAIL] 633# define debug optlist[17 + BASH_PIPEFAIL]
469#endif 634#endif
635#if ENABLE_PLATFORM_MINGW32
636# define winxp optlist[16 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
637# if ENABLE_ASH_NOCONSOLE
638# define noconsole optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
639# define noiconify optlist[18 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
640# endif
641# if ENABLE_ASH_GLOB_OPTIONS
642# define nocaseglob optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
643# define nohiddenglob optlist[18 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
644# define nohidsysglob optlist[19 + BASH_PIPEFAIL + 2*(DEBUG != 0) + 2*ENABLE_ASH_NOCONSOLE]
645# endif
646#endif
470 647
471 /* trap handler commands */ 648 /* trap handler commands */
649#if !ENABLE_PLATFORM_MINGW32
472 /* 650 /*
473 * Sigmode records the current value of the signal handlers for the various 651 * Sigmode records the current value of the signal handlers for the various
474 * modes. A value of zero means that the current handler is not known. 652 * modes. A value of zero means that the current handler is not known.
@@ -482,6 +660,7 @@ struct globals_misc {
482 660
483 /* indicates specified signal received */ 661 /* indicates specified signal received */
484 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ 662 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
663#endif
485 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ 664 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
486 char *trap[NSIG + 1]; 665 char *trap[NSIG + 1];
487/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */ 666/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
@@ -510,10 +689,21 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
510#define rootpid (G_misc.rootpid ) 689#define rootpid (G_misc.rootpid )
511#define shlvl (G_misc.shlvl ) 690#define shlvl (G_misc.shlvl )
512#define errlinno (G_misc.errlinno ) 691#define errlinno (G_misc.errlinno )
692#if ENABLE_PLATFORM_MINGW32
693#define loopnest (G_misc.loopnest )
694#endif
513#define minusc (G_misc.minusc ) 695#define minusc (G_misc.minusc )
696#if ENABLE_PLATFORM_MINGW32
697#define dirarg (G_misc.dirarg )
698#define title (G_misc.title )
699#define delayexit (G_misc.delayexit )
700#endif
514#define curdir (G_misc.curdir ) 701#define curdir (G_misc.curdir )
515#define physdir (G_misc.physdir ) 702#define physdir (G_misc.physdir )
516#define arg0 (G_misc.arg0 ) 703#define arg0 (G_misc.arg0 )
704#if ENABLE_PLATFORM_MINGW32
705#define commandname (G_misc.commandname)
706#endif
517#define exception_handler (G_misc.exception_handler) 707#define exception_handler (G_misc.exception_handler)
518#define exception_type (G_misc.exception_type ) 708#define exception_type (G_misc.exception_type )
519#define suppress_int (G_misc.suppress_int ) 709#define suppress_int (G_misc.suppress_int )
@@ -530,6 +720,13 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
530#define groupinfo (G_misc.groupinfo ) 720#define groupinfo (G_misc.groupinfo )
531#define random_gen (G_misc.random_gen ) 721#define random_gen (G_misc.random_gen )
532#define backgndpid (G_misc.backgndpid ) 722#define backgndpid (G_misc.backgndpid )
723
724#if ENABLE_PLATFORM_MINGW32
725#undef got_sigchld
726#undef pending_sig
727#define pending_sig (0)
728#endif
729
533#define INIT_G_misc() do { \ 730#define INIT_G_misc() do { \
534 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \ 731 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \
535 savestatus = -1; \ 732 savestatus = -1; \
@@ -547,6 +744,9 @@ static void trace_printf(const char *fmt, ...);
547static void trace_vprintf(const char *fmt, va_list va); 744static void trace_vprintf(const char *fmt, va_list va);
548# define TRACE(param) trace_printf param 745# define TRACE(param) trace_printf param
549# define TRACEV(param) trace_vprintf param 746# define TRACEV(param) trace_vprintf param
747# if ENABLE_PLATFORM_MINGW32 && defined(close)
748# undef close
749# endif
550# define close(fd) do { \ 750# define close(fd) do { \
551 int dfd = (fd); \ 751 int dfd = (fd); \
552 if (close(dfd) < 0) \ 752 if (close(dfd) < 0) \
@@ -635,11 +835,18 @@ struct parsefile {
635 835
636 /* Number of outstanding calls to pungetc. */ 836 /* Number of outstanding calls to pungetc. */
637 int unget; 837 int unget;
838
839#if ENABLE_PLATFORM_MINGW32
840 /* True if a trailing CR from a previous read was left unprocessed. */
841 int cr;
842#endif
638}; 843};
639 844
640static struct parsefile basepf; /* top level input file */ 845static struct parsefile basepf; /* top level input file */
641static struct parsefile *g_parsefile = &basepf; /* current input file */ 846static struct parsefile *g_parsefile = &basepf; /* current input file */
847#if ENABLE_PLATFORM_POSIX
642static char *commandname; /* currently executing command */ 848static char *commandname; /* currently executing command */
849#endif
643 850
644 851
645/* ============ Interrupts / exceptions */ 852/* ============ Interrupts / exceptions */
@@ -696,21 +903,40 @@ raise_exception(int e)
696 * are held using the INT_OFF macro. (The test for iflag is just 903 * are held using the INT_OFF macro. (The test for iflag is just
697 * defensive programming.) 904 * defensive programming.)
698 */ 905 */
699static void raise_interrupt(void) NORETURN; 906static void raise_interrupt(void) IF_NOT_PLATFORM_MINGW32(NORETURN);
700static void 907static void
701raise_interrupt(void) 908raise_interrupt(void)
702{ 909{
910#if ENABLE_PLATFORM_MINGW32
911 /* Contrary to the comment above on Windows raise_interrupt() is
912 * called when SIGINT is trapped or ignored. We detect this here
913 * and return without doing anything. */
914 if (trap[SIGINT])
915 return;
916#endif
703 pending_int = 0; 917 pending_int = 0;
918#if !ENABLE_PLATFORM_MINGW32
704 /* Signal is not automatically unmasked after it is raised, 919 /* Signal is not automatically unmasked after it is raised,
705 * do it ourself - unmask all signals */ 920 * do it ourself - unmask all signals */
706 sigprocmask_allsigs(SIG_UNBLOCK); 921 sigprocmask_allsigs(SIG_UNBLOCK);
922#endif
707 /* pending_sig = 0; - now done in signal_handler() */ 923 /* pending_sig = 0; - now done in signal_handler() */
708 924
709 if (!(rootshell && iflag)) { 925 if (!(rootshell && iflag)) {
926#if !ENABLE_PLATFORM_MINGW32
710 /* Kill ourself with SIGINT */ 927 /* Kill ourself with SIGINT */
711 signal(SIGINT, SIG_DFL); 928 signal(SIGINT, SIG_DFL);
712 raise(SIGINT); 929 raise(SIGINT);
930#else
931 fflush_all();
932 kill(-getpid(), SIGINT);
933 _exit(SIGINT << 24);
934#endif
713 } 935 }
936#if ENABLE_PLATFORM_MINGW32
937 if (iflag)
938 write(STDOUT_FILENO, "^C", 2);
939#endif
714 /* bash: ^C even on empty command line sets $? */ 940 /* bash: ^C even on empty command line sets $? */
715 exitstatus = SIGINT + 128; 941 exitstatus = SIGINT + 128;
716 raise_exception(EXINT); 942 raise_exception(EXINT);
@@ -1596,7 +1822,6 @@ struct stackmark {
1596 size_t stacknleft; 1822 size_t stacknleft;
1597}; 1823};
1598 1824
1599
1600struct globals_memstack { 1825struct globals_memstack {
1601 struct stack_block *g_stackp; // = &stackbase; 1826 struct stack_block *g_stackp; // = &stackbase;
1602 char *g_stacknxt; // = stackbase.space; 1827 char *g_stacknxt; // = stackbase.space;
@@ -1619,7 +1844,6 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
1619 sstrend = stackbase.space + MINSIZE; \ 1844 sstrend = stackbase.space + MINSIZE; \
1620} while (0) 1845} while (0)
1621 1846
1622
1623#define stackblock() ((void *)g_stacknxt) 1847#define stackblock() ((void *)g_stacknxt)
1624#define stackblocksize() g_stacknleft 1848#define stackblocksize() g_stacknleft
1625 1849
@@ -1998,6 +2222,18 @@ maybe_single_quote(const char *s)
1998 return single_quote(s); 2222 return single_quote(s);
1999} 2223}
2000 2224
2225#if ENABLE_PLATFORM_MINGW32
2226/* Copy path to a string on the stack long enough to allow a file extension
2227 * to be added. */
2228static char *
2229stack_add_ext_space(const char *path)
2230{
2231 char *p = growstackto(strlen(path) + 5);
2232 strcpy(p, path);
2233 return p;
2234}
2235#endif
2236
2001 2237
2002/* ============ nextopt */ 2238/* ============ nextopt */
2003 2239
@@ -2120,7 +2356,9 @@ struct localvar {
2120#else 2356#else
2121# define VDYNAMIC 0 2357# define VDYNAMIC 0
2122#endif 2358#endif
2123 2359#if ENABLE_PLATFORM_MINGW32
2360# define VIMPORT 0x400 /* variable was imported from environment */
2361#endif
2124 2362
2125/* Need to be before varinit_data[] */ 2363/* Need to be before varinit_data[] */
2126#if ENABLE_LOCALE_SUPPORT 2364#if ENABLE_LOCALE_SUPPORT
@@ -2149,6 +2387,24 @@ static void change_seconds(const char *) FAST_FUNC;
2149static void change_realtime(const char *) FAST_FUNC; 2387static void change_realtime(const char *) FAST_FUNC;
2150#endif 2388#endif
2151 2389
2390#if ENABLE_PLATFORM_MINGW32
2391static void FAST_FUNC
2392change_terminal_mode(const char *newval UNUSED_PARAM)
2393{
2394 terminal_mode(TRUE);
2395}
2396
2397static void clearcmdentry(void);
2398static void FAST_FUNC
2399change_override_applets(const char *newval UNUSED_PARAM)
2400{
2401 clearcmdentry();
2402}
2403
2404# define LINENO_INDEX (5 + 2 * ENABLE_ASH_MAIL + ENABLE_ASH_GETOPTS)
2405# define FUNCNAME_INDEX (LINENO_INDEX + 1)
2406#endif
2407
2152static const struct { 2408static const struct {
2153 int flags; 2409 int flags;
2154 const char *var_text; 2410 const char *var_text;
@@ -2186,6 +2442,12 @@ static const struct {
2186#if ENABLE_FEATURE_EDITING_SAVEHISTORY 2442#if ENABLE_FEATURE_EDITING_SAVEHISTORY
2187 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL }, 2443 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
2188#endif 2444#endif
2445#if ENABLE_PLATFORM_MINGW32
2446 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_SKIP_ANSI_EMULATION, change_terminal_mode },
2447 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_TERMINAL_MODE, change_terminal_mode },
2448 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_OVERRIDE_APPLETS, change_override_applets },
2449 { VSTRFIXED|VTEXTFIXED|VUNSET, BB_CRITICAL_ERROR_DIALOGS, change_critical_error_dialogs },
2450#endif
2189}; 2451};
2190 2452
2191struct redirtab; 2453struct redirtab;
@@ -2409,6 +2671,65 @@ bltinlookup(const char *name)
2409 return lookupvar(name); 2671 return lookupvar(name);
2410} 2672}
2411 2673
2674#if ENABLE_PLATFORM_MINGW32
2675static char *
2676fix_pathvar(const char *path, int len)
2677{
2678 char *newpath = xstrdup(path);
2679 char *p;
2680 int modified = FALSE;
2681
2682 p = newpath + len;
2683 while (*p) {
2684 if (*p != ':' && *p != ';') {
2685 /* skip drive */
2686 if (isalpha(*p) && p[1] == ':')
2687 p += 2;
2688 /* skip through path component */
2689 for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2690 continue;
2691 }
2692 /* *p is ':', ';' or '\0' here */
2693 if (*p == ':') {
2694 *p++ = ';';
2695 modified = TRUE;
2696 }
2697 else if (*p == ';') {
2698 ++p;
2699 }
2700 }
2701
2702 if (!modified) {
2703 free(newpath);
2704 newpath = NULL;
2705 }
2706 return newpath;
2707}
2708
2709#define BB_VAR_EXACT 1 /* exact match for name */
2710#define BB_VAR_ASSIGN -1 /* matches name followed by '=' */
2711
2712/* Match variables that should be placed in the environment immediately
2713 * they're exported and removed immediately they're no longer exported */
2714static int
2715is_bb_var(const char *s)
2716{
2717 const char *p;
2718 int len;
2719
2720 for (p = bbvar; *p; p += len + 1) {
2721 len = strlen(p);
2722 if (strncmp(s, p, len) == 0) {
2723 if (s[len] == '\0')
2724 return BB_VAR_EXACT;
2725 else if (s[len] == '=')
2726 return BB_VAR_ASSIGN;
2727 }
2728 }
2729 return FALSE;
2730}
2731#endif
2732
2412/* 2733/*
2413 * Same as setvar except that the variable and value are passed in 2734 * Same as setvar except that the variable and value are passed in
2414 * the first argument as name=value. Since the first argument will 2735 * the first argument as name=value. Since the first argument will
@@ -2420,6 +2741,26 @@ static struct var *
2420setvareq(char *s, int flags) 2741setvareq(char *s, int flags)
2421{ 2742{
2422 struct var *vp, **vpp; 2743 struct var *vp, **vpp;
2744#if ENABLE_PLATFORM_MINGW32
2745 const char *paths = "PATH=\0""CDPATH=\0""MANPATH=\0";
2746 const char *p;
2747 int len;
2748
2749 for (p = paths; *p; p += len + 1) {
2750 len = strlen(p);
2751 if (strncmp(s, p, len) == 0) {
2752 char *newpath = fix_pathvar(s, len);
2753 if (newpath) {
2754 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2755 free(s);
2756 flags |= VNOSAVE;
2757 flags &= ~(VTEXTFIXED|VSTACK);
2758 s = newpath;
2759 }
2760 break;
2761 }
2762 }
2763#endif
2423 2764
2424 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2765 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2425 vpp = findvar(s); 2766 vpp = findvar(s);
@@ -2444,6 +2785,11 @@ setvareq(char *s, int flags)
2444 if (!(vp->flags & (VTEXTFIXED|VSTACK))) 2785 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2445 free((char*)vp->var_text); 2786 free((char*)vp->var_text);
2446 2787
2788#if ENABLE_PLATFORM_MINGW32
2789 if ((flags & VUNSET) && (vp->flags & VEXPORT) &&
2790 is_bb_var(s) == BB_VAR_EXACT)
2791 unsetenv(s);
2792#endif
2447 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) { 2793 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2448 *vpp = vp->next; 2794 *vpp = vp->next;
2449 free(vp); 2795 free(vp);
@@ -2473,6 +2819,10 @@ setvareq(char *s, int flags)
2473 s = ckstrdup(s); 2819 s = ckstrdup(s);
2474 vp->var_text = s; 2820 vp->var_text = s;
2475 vp->flags = flags; 2821 vp->flags = flags;
2822#if ENABLE_PLATFORM_MINGW32
2823 if ((flags & VEXPORT) && is_bb_var(s) == BB_VAR_ASSIGN)
2824 putenv(s);
2825#endif
2476 2826
2477 out: 2827 out:
2478 return vp; 2828 return vp;
@@ -2594,6 +2944,65 @@ listvars(int on, int off, struct strlist *lp, char ***end)
2594 return grabstackstr(ep); 2944 return grabstackstr(ep);
2595} 2945}
2596 2946
2947#if ENABLE_PLATFORM_MINGW32
2948/* Adjust directory separator in variables imported from the environment */
2949static void
2950setwinxp(int on)
2951{
2952 static smallint is_winxp = 1;
2953 struct var **vpp;
2954 struct var *vp;
2955
2956 if (on == is_winxp)
2957 return;
2958 is_winxp = on;
2959
2960 for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
2961 for (vp = *vpp; vp; vp = vp->next) {
2962 if ((vp->flags & VIMPORT)) {
2963 char *end = strchr(vp->var_text, '=');
2964 if (!end || is_prefixed_with(vp->var_text, "COMSPEC=") ||
2965 is_prefixed_with(vp->var_text, "SYSTEMROOT="))
2966 continue;
2967 if (!on)
2968 bs_to_slash(end + 1);
2969 else
2970 slash_to_bs(end + 1);
2971 }
2972 }
2973 }
2974}
2975
2976# if ENABLE_ASH_NOCONSOLE
2977/*
2978 * Console state is either:
2979 * 0 normal
2980 * 1 iconified/hidden
2981 * 2 unknown
2982 */
2983static int console_state(void)
2984{
2985 DECLARE_PROC_ADDR(BOOL, ShowWindow, HWND, int);
2986
2987 if (INIT_PROC_ADDR(user32.dll, ShowWindow)) {
2988 BOOL visible = IsWindowVisible(GetConsoleWindow());
2989 BOOL iconified = IsIconic(GetConsoleWindow());
2990
2991 return !visible || iconified;
2992 }
2993 return 2;
2994}
2995
2996static void hide_console(int hide)
2997{
2998 // Switch console state if it's known and isn't the required state
2999 if (console_state() == !hide)
3000 ShowWindow(GetConsoleWindow(), hide ?
3001 (noiconify ? SW_HIDE : SW_MINIMIZE) : SW_NORMAL);
3002}
3003# endif
3004#endif
3005
2597 3006
2598/* ============ Path search helper */ 3007/* ============ Path search helper */
2599static const char * 3008static const char *
@@ -2637,7 +3046,7 @@ static const char *pathopt; /* set by padvance */
2637static int 3046static int
2638padvance_magic(const char **path, const char *name, int magic) 3047padvance_magic(const char **path, const char *name, int magic)
2639{ 3048{
2640 const char *term = "%:"; 3049 const char *term = "%"PATH_SEP_STR;
2641 const char *lpathopt; 3050 const char *lpathopt;
2642 const char *p; 3051 const char *p;
2643 char *q; 3052 char *q;
@@ -2654,14 +3063,14 @@ padvance_magic(const char **path, const char *name, int magic)
2654 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) { 3063 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2655 lpathopt = start + 1; 3064 lpathopt = start + 1;
2656 start = p; 3065 start = p;
2657 term = ":"; 3066 term = PATH_SEP_STR;
2658 } 3067 }
2659 3068
2660 len = strcspn(start, term); 3069 len = strcspn(start, term);
2661 p = start + len; 3070 p = start + len;
2662 3071
2663 if (*p == '%') { 3072 if (*p == '%') {
2664 size_t extra = strchrnul(p, ':') - p; 3073 size_t extra = strchrnul(p, PATH_SEP) - p;
2665 3074
2666 if (legal_pathopt(p + 1, term, magic)) 3075 if (legal_pathopt(p + 1, term, magic))
2667 lpathopt = p + 1; 3076 lpathopt = p + 1;
@@ -2672,14 +3081,18 @@ padvance_magic(const char **path, const char *name, int magic)
2672 } 3081 }
2673 3082
2674 pathopt = lpathopt; 3083 pathopt = lpathopt;
2675 *path = *p == ':' ? p + 1 : NULL; 3084 *path = *p == PATH_SEP ? p + 1 : NULL;
2676 3085
2677 /* "2" is for '/' and '\0' */ 3086 /* "2" is for '/' and '\0' */
2678 qlen = len + strlen(name) + 2; 3087 /* reserve space for suffix on WIN32 */
3088 qlen = len + strlen(name) + 2 IF_PLATFORM_MINGW32(+ 4);
2679 q = growstackto(qlen); 3089 q = growstackto(qlen);
2680 3090
2681 if (len) { 3091 if (len) {
2682 q = mempcpy(q, start, len); 3092 q = mempcpy(q, start, len);
3093#if ENABLE_PLATFORM_MINGW32
3094 if (q[-1] != '/' && q[-1] != '\\')
3095#endif
2683 *q++ = '/'; 3096 *q++ = '/';
2684 } 3097 }
2685 strcpy(q, name); 3098 strcpy(q, name);
@@ -2770,6 +3183,7 @@ setprompt_if(smallint do_set, int whichprompt)
2770 3183
2771#define CD_PHYSICAL 1 3184#define CD_PHYSICAL 1
2772#define CD_PRINT 2 3185#define CD_PRINT 2
3186#define CD_PRINT_ALL 4
2773 3187
2774static int 3188static int
2775cdopt(void) 3189cdopt(void)
@@ -2778,7 +3192,14 @@ cdopt(void)
2778 int i, j; 3192 int i, j;
2779 3193
2780 j = 'L'; 3194 j = 'L';
3195#if ENABLE_PLATFORM_MINGW32
3196 while ((i = nextopt("LPa")) != '\0') {
3197 if (i == 'a')
3198 flags |= CD_PRINT_ALL;
3199 else
3200#else
2781 while ((i = nextopt("LP")) != '\0') { 3201 while ((i = nextopt("LP")) != '\0') {
3202#endif
2782 if (i != j) { 3203 if (i != j) {
2783 flags ^= CD_PHYSICAL; 3204 flags ^= CD_PHYSICAL;
2784 j = i; 3205 j = i;
@@ -2795,6 +3216,130 @@ cdopt(void)
2795static const char * 3216static const char *
2796updatepwd(const char *dir) 3217updatepwd(const char *dir)
2797{ 3218{
3219#if ENABLE_PLATFORM_MINGW32
3220 /*
3221 * Due to Windows drive notion, getting pwd is a completely
3222 * different thing. Handle it in a separate routine
3223 */
3224
3225 char *new;
3226 char *p;
3227 char *cdcomppath;
3228 const char *lim;
3229 int len;
3230 char buffer[PATH_MAX];
3231 /*
3232 * There are five cases that make some kind of sense
3233 *
3234 * Absolute paths:
3235 * c:/path
3236 * //host/share
3237 *
3238 * Relative to current working directory of other drive:
3239 * c:path
3240 *
3241 * Relative to current root (drive/share):
3242 * /path
3243 *
3244 * Relative to current working directory of current root (drive/share):
3245 * path
3246 */
3247 enum {ABS_DRIVE, ABS_SHARE, REL_OTHER, REL_ROOT, REL_CWD} target;
3248
3249 /* skip multiple leading separators unless dir is a UNC path */
3250 if (is_dir_sep(*dir) && unc_root_len(dir) == 0) {
3251 while (is_dir_sep(dir[1]))
3252 ++dir;
3253 }
3254
3255 len = strlen(dir);
3256 if (len >= 2 && has_dos_drive_prefix(dir))
3257 target = len >= 3 && is_dir_sep(dir[2]) ? ABS_DRIVE : REL_OTHER;
3258 else if (unc_root_len(dir) != 0)
3259 target = ABS_SHARE;
3260 else if (is_dir_sep(*dir))
3261 target = REL_ROOT;
3262 else
3263 target = REL_CWD;
3264
3265 cdcomppath = sstrdup(dir);
3266 STARTSTACKSTR(new);
3267
3268 switch (target) {
3269 case REL_OTHER:
3270 /* c:path */
3271 if (get_drive_cwd(dir, buffer, PATH_MAX) == NULL)
3272 return 0;
3273 new = stack_putstr(buffer, new);
3274 len = 2;
3275 cdcomppath += len;
3276 dir += len;
3277 break;
3278 case REL_CWD:
3279 case REL_ROOT:
3280 /* path or /path */
3281 len = root_len(curdir);
3282 if (len == 0)
3283 return 0;
3284 new = target == REL_CWD ? stack_putstr(curdir, new) :
3285 stnputs(curdir, len, new);
3286 break;
3287 default:
3288 /* //host/share or c:/path */
3289 len = root_len(dir);
3290 if (len == 0)
3291 return 0;
3292 new = stnputs(dir, len, new);
3293 cdcomppath += len;
3294 dir += len;
3295 break;
3296 }
3297
3298 new = makestrspace(strlen(dir) + 2, new);
3299 lim = (char *)stackblock() + len + 1;
3300
3301 if (!is_dir_sep(*dir)) {
3302 if (!is_dir_sep(new[-1]))
3303 USTPUTC('/', new);
3304 if (new > lim && is_dir_sep(*lim))
3305 lim++;
3306 } else {
3307 USTPUTC('/', new);
3308 cdcomppath++;
3309 if (is_dir_sep(dir[1]) && !is_dir_sep(dir[2])) {
3310 USTPUTC('/', new);
3311 cdcomppath++;
3312 lim++;
3313 }
3314 }
3315 p = strtok(cdcomppath, "/\\");
3316 while (p) {
3317 switch (*p) {
3318 case '.':
3319 if (p[1] == '.' && p[2] == '\0') {
3320 while (new > lim) {
3321 STUNPUTC(new);
3322 if (is_dir_sep(new[-1]))
3323 break;
3324 }
3325 break;
3326 }
3327 if (p[1] == '\0')
3328 break;
3329 /* fall through */
3330 default:
3331 new = stack_putstr(p, new);
3332 USTPUTC('/', new);
3333 }
3334 p = strtok(NULL, "/\\");
3335 }
3336 if (new > lim)
3337 STUNPUTC(new);
3338 *new = 0;
3339 strip_dot_space((char *)stackblock());
3340 fix_path_case((char *)stackblock());
3341 return bs_to_slash((char *)stackblock());
3342#else
2798 char *new; 3343 char *new;
2799 char *p; 3344 char *p;
2800 char *cdcomppath; 3345 char *cdcomppath;
@@ -2848,6 +3393,7 @@ updatepwd(const char *dir)
2848 STUNPUTC(new); 3393 STUNPUTC(new);
2849 *new = 0; 3394 *new = 0;
2850 return stackblock(); 3395 return stackblock();
3396#endif
2851} 3397}
2852 3398
2853/* 3399/*
@@ -2943,7 +3489,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2943 } 3489 }
2944 if (!dest) 3490 if (!dest)
2945 dest = nullstr; 3491 dest = nullstr;
2946 if (*dest == '/') 3492 if (!is_relative_path(dest))
2947 goto step6; 3493 goto step6;
2948 if (*dest == '.') { 3494 if (*dest == '.') {
2949 c = dest[1]; 3495 c = dest[1];
@@ -2966,7 +3512,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2966 p = stalloc(len); 3512 p = stalloc(len);
2967 3513
2968 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 3514 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2969 if (c && c != ':') 3515 if (c && c != PATH_SEP)
2970 flags |= CD_PRINT; 3516 flags |= CD_PRINT;
2971 docd: 3517 docd:
2972 if (!docd(p, flags)) 3518 if (!docd(p, flags))
@@ -2988,6 +3534,26 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2988 return 0; 3534 return 0;
2989} 3535}
2990 3536
3537#if ENABLE_PLATFORM_MINGW32
3538static void
3539print_all_cwd(void)
3540{
3541 FILE *mnt;
3542 struct mntent *entry;
3543 char buffer[PATH_MAX];
3544
3545 mnt = setmntent(bb_path_mtab_file, "r");
3546 if (mnt) {
3547 while ((entry=getmntent(mnt)) != NULL) {
3548 entry->mnt_dir[2] = '\0';
3549 if (get_drive_cwd(entry->mnt_dir, buffer, PATH_MAX) != NULL)
3550 out1fmt("%s\n", buffer);
3551 }
3552 endmntent(mnt);
3553 }
3554}
3555#endif
3556
2991static int FAST_FUNC 3557static int FAST_FUNC
2992pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 3558pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2993{ 3559{
@@ -2995,6 +3561,12 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2995 const char *dir = curdir; 3561 const char *dir = curdir;
2996 3562
2997 flags = cdopt(); 3563 flags = cdopt();
3564#if ENABLE_PLATFORM_MINGW32
3565 if (flags & CD_PRINT_ALL) {
3566 print_all_cwd();
3567 return 0;
3568 }
3569#endif
2998 if (flags) { 3570 if (flags) {
2999 if (physdir == nullstr) 3571 if (physdir == nullstr)
3000 setpwd(dir, 0); 3572 setpwd(dir, 0);
@@ -3007,7 +3579,6 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3007 3579
3008/* ============ ... */ 3580/* ============ ... */
3009 3581
3010
3011#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024) 3582#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
3012 3583
3013/* Syntax classes */ 3584/* Syntax classes */
@@ -3414,13 +3985,11 @@ struct alias {
3414 int flag; 3985 int flag;
3415}; 3986};
3416 3987
3417
3418static struct alias **atab; // [ATABSIZE]; 3988static struct alias **atab; // [ATABSIZE];
3419#define INIT_G_alias() do { \ 3989#define INIT_G_alias() do { \
3420 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ 3990 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3421} while (0) 3991} while (0)
3422 3992
3423
3424static struct alias ** 3993static struct alias **
3425__lookupalias(const char *name) 3994__lookupalias(const char *name)
3426{ 3995{
@@ -3602,7 +4171,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3602 4171
3603#endif /* ASH_ALIAS */ 4172#endif /* ASH_ALIAS */
3604 4173
3605
3606/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ 4174/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
3607#define FORK_FG 0 4175#define FORK_FG 0
3608#define FORK_BG 1 4176#define FORK_BG 1
@@ -3623,7 +4191,12 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3623struct procstat { 4191struct procstat {
3624 pid_t ps_pid; /* process id */ 4192 pid_t ps_pid; /* process id */
3625 int ps_status; /* last process status from wait() */ 4193 int ps_status; /* last process status from wait() */
4194#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3626 char *ps_cmd; /* text of command being run */ 4195 char *ps_cmd; /* text of command being run */
4196#endif
4197#if ENABLE_PLATFORM_MINGW32
4198 HANDLE ps_proc;
4199#endif
3627}; 4200};
3628 4201
3629struct job { 4202struct job {
@@ -3639,8 +4212,10 @@ struct job {
3639#define JOBDONE 2 /* all procs are completed */ 4212#define JOBDONE 2 /* all procs are completed */
3640 unsigned 4213 unsigned
3641 state: 8, 4214 state: 8,
3642#if JOBS 4215#if JOBS || ENABLE_PLATFORM_MINGW32
3643 sigint: 1, /* job was killed by SIGINT */ 4216 sigint: 1, /* job was killed by SIGINT */
4217#endif
4218#if JOBS
3644 jobctl: 1, /* job running under job control */ 4219 jobctl: 1, /* job running under job control */
3645#endif 4220#endif
3646 waited: 1, /* true if this entry has been waited for */ 4221 waited: 1, /* true if this entry has been waited for */
@@ -3650,17 +4225,23 @@ struct job {
3650}; 4225};
3651 4226
3652static struct job *makejob(/*union node *,*/ int); 4227static struct job *makejob(/*union node *,*/ int);
4228#if !ENABLE_PLATFORM_MINGW32
3653static int forkshell(struct job *, union node *, int); 4229static int forkshell(struct job *, union node *, int);
4230#endif
3654static int waitforjob(struct job *); 4231static int waitforjob(struct job *);
3655 4232
3656#if !JOBS 4233#if !JOBS && !JOBS_WIN32
3657enum { doing_jobctl = 0 }; 4234enum { doing_jobctl = 0 };
3658#define setjobctl(on) do {} while (0) 4235#define setjobctl(on) do {} while (0)
3659#else 4236#elif JOBS_WIN32
4237static smallint doing_jobctl; //references:8
4238#define setjobctl(on) do { if (rootshell) doing_jobctl = on; } while (0)
4239#else /* JOBS */
3660static smallint doing_jobctl; //references:8 4240static smallint doing_jobctl; //references:8
3661static void setjobctl(int); 4241static void setjobctl(int);
3662#endif 4242#endif
3663 4243
4244#if !ENABLE_PLATFORM_MINGW32
3664/* 4245/*
3665 * Ignore a signal. 4246 * Ignore a signal.
3666 */ 4247 */
@@ -3687,7 +4268,7 @@ signal_handler(int signo)
3687 return; 4268 return;
3688 } 4269 }
3689#if ENABLE_FEATURE_EDITING 4270#if ENABLE_FEATURE_EDITING
3690 bb_got_signal = signo; /* for read_line_input: "we got a signal" */ 4271 bb_got_signal = signo; /* for read_line_input / read builtin: "we got a signal" */
3691#endif 4272#endif
3692 gotsig[signo - 1] = 1; 4273 gotsig[signo - 1] = 1;
3693 pending_sig = signo; 4274 pending_sig = signo;
@@ -3809,6 +4390,10 @@ setsignal(int signo)
3809 4390
3810 sigaction_set(signo, &act); 4391 sigaction_set(signo, &act);
3811} 4392}
4393#else
4394#define setsignal(s)
4395#define ignoresig(s)
4396#endif
3812 4397
3813/* mode flags for set_curjob */ 4398/* mode flags for set_curjob */
3814#define CUR_DELETE 2 4399#define CUR_DELETE 2
@@ -3942,7 +4527,7 @@ set_curjob(struct job *jp, unsigned mode)
3942 } 4527 }
3943} 4528}
3944 4529
3945#if JOBS || DEBUG 4530#if JOBS || ENABLE_PLATFORM_MINGW32 || DEBUG
3946static int 4531static int
3947jobno(const struct job *jp) 4532jobno(const struct job *jp)
3948{ 4533{
@@ -3960,7 +4545,9 @@ static struct job *
3960getjob(const char *name, int getctl) 4545getjob(const char *name, int getctl)
3961{ 4546{
3962 struct job *jp; 4547 struct job *jp;
4548#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3963 struct job *found; 4549 struct job *found;
4550#endif
3964 const char *err_msg = "%s: no such job"; 4551 const char *err_msg = "%s: no such job";
3965 unsigned num; 4552 unsigned num;
3966 int c; 4553 int c;
@@ -4005,6 +4592,7 @@ getjob(const char *name, int getctl)
4005 } 4592 }
4006 } 4593 }
4007 4594
4595#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4008 found = NULL; 4596 found = NULL;
4009 while (jp) { 4597 while (jp) {
4010 if (*p == '?' 4598 if (*p == '?'
@@ -4021,6 +4609,9 @@ getjob(const char *name, int getctl)
4021 if (!found) 4609 if (!found)
4022 goto err; 4610 goto err;
4023 jp = found; 4611 jp = found;
4612#else
4613 goto err;
4614#endif
4024 4615
4025 gotit: 4616 gotit:
4026#if JOBS 4617#if JOBS
@@ -4039,14 +4630,18 @@ getjob(const char *name, int getctl)
4039static void 4630static void
4040freejob(struct job *jp) 4631freejob(struct job *jp)
4041{ 4632{
4633#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4042 struct procstat *ps; 4634 struct procstat *ps;
4043 int i; 4635 int i;
4636#endif
4044 4637
4045 INT_OFF; 4638 INT_OFF;
4639#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4046 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { 4640 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
4047 if (ps->ps_cmd != nullstr) 4641 if (ps->ps_cmd != nullstr)
4048 free(ps->ps_cmd); 4642 free(ps->ps_cmd);
4049 } 4643 }
4644#endif
4050 if (jp->ps != &jp->ps0) 4645 if (jp->ps != &jp->ps0)
4051 free(jp->ps); 4646 free(jp->ps);
4052 jp->used = 0; 4647 jp->used = 0;
@@ -4139,7 +4734,9 @@ setjobctl(int on)
4139 ttyfd = fd; 4734 ttyfd = fd;
4140 doing_jobctl = on; 4735 doing_jobctl = on;
4141} 4736}
4737#endif
4142 4738
4739#if JOBS || JOBS_WIN32
4143static int FAST_FUNC 4740static int FAST_FUNC
4144killcmd(int argc, char **argv) 4741killcmd(int argc, char **argv)
4145{ 4742{
@@ -4169,8 +4766,10 @@ killcmd(int argc, char **argv)
4169 * sh -c 'true|sleep 1 & sleep 2; kill %1' 4766 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4170 */ 4767 */
4171 n = jp->nprocs; /* can't be 0 (I hope) */ 4768 n = jp->nprocs; /* can't be 0 (I hope) */
4769#if !ENABLE_PLATFORM_MINGW32
4172 if (jp->jobctl) 4770 if (jp->jobctl)
4173 n = 1; 4771 n = 1;
4772#endif
4174 dst = alloca(n * sizeof(int)*4); 4773 dst = alloca(n * sizeof(int)*4);
4175 argv[i] = dst; 4774 argv[i] = dst;
4176 for (j = 0; j < n; j++) { 4775 for (j = 0; j < n; j++) {
@@ -4185,7 +4784,11 @@ killcmd(int argc, char **argv)
4185 * leading space. Needed to not confuse 4784 * leading space. Needed to not confuse
4186 * negative pids with "kill -SIGNAL_NO" syntax 4785 * negative pids with "kill -SIGNAL_NO" syntax
4187 */ 4786 */
4787#if !ENABLE_PLATFORM_MINGW32
4188 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); 4788 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4789#else
4790 dst += sprintf(dst, " -%u", (int)ps->ps_pid);
4791#endif
4189 } 4792 }
4190 *dst = '\0'; 4793 *dst = '\0';
4191 } 4794 }
@@ -4193,7 +4796,9 @@ killcmd(int argc, char **argv)
4193 } 4796 }
4194 return kill_main(argc, argv); 4797 return kill_main(argc, argv);
4195} 4798}
4799#endif
4196 4800
4801#if JOBS
4197static void 4802static void
4198showpipe(struct job *jp /*, FILE *out*/) 4803showpipe(struct job *jp /*, FILE *out*/)
4199{ 4804{
@@ -4298,6 +4903,65 @@ sprint_status48(char *os, int status, int sigonly)
4298 return s - os; 4903 return s - os;
4299} 4904}
4300 4905
4906#if ENABLE_PLATFORM_MINGW32
4907static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
4908{
4909 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
4910# if ENABLE_FEATURE_EDITING
4911 bb_got_signal = SIGINT; /* for read_line_input: "we got a signal" */
4912# endif
4913 if (!suppress_int && !(rootshell && iflag))
4914 raise_interrupt();
4915 pending_int = 1;
4916 return TRUE;
4917 }
4918 return FALSE;
4919}
4920
4921/*
4922 * Windows does not know about parent-child relationship
4923 * They don't support waitpid(-1)
4924 */
4925static pid_t
4926waitpid_child(int *status, int wait_flags)
4927{
4928 struct job *jb;
4929 int pid_nr = 0;
4930 static HANDLE *proclist = NULL;
4931 static int pid_max = 0;
4932 pid_t pid = -1;
4933 DWORD win_status, idx;
4934 int i;
4935
4936 for (jb = curjob; jb; jb = jb->prev_job) {
4937 if (jb->state != JOBDONE) {
4938 if (pid_nr + jb->nprocs > pid_max) {
4939 pid_max = pid_nr + jb->nprocs;
4940 proclist = ckrealloc(proclist, sizeof(*proclist) * pid_max);
4941 }
4942
4943 for (i = 0; i < jb->nprocs; ++i) {
4944 if (jb->ps[i].ps_proc) {
4945 proclist[pid_nr++] = jb->ps[i].ps_proc;
4946 }
4947 }
4948 }
4949 }
4950
4951 if (pid_nr) {
4952 idx = WaitForMultipleObjects(pid_nr, proclist, FALSE,
4953 wait_flags & WNOHANG ? 0 : INFINITE);
4954 if (idx < pid_nr) {
4955 GetExitCodeProcess(proclist[idx], &win_status);
4956 *status = exit_code_to_wait_status(win_status);
4957 pid = GetProcessId(proclist[idx]);
4958 }
4959 }
4960 return pid;
4961}
4962#define waitpid(p, s, f) waitpid_child(s, f)
4963#endif
4964
4301#define DOWAIT_NONBLOCK 0 4965#define DOWAIT_NONBLOCK 0
4302#define DOWAIT_BLOCK 1 4966#define DOWAIT_BLOCK 1
4303#define DOWAIT_BLOCK_OR_SIG 2 4967#define DOWAIT_BLOCK_OR_SIG 2
@@ -4308,6 +4972,7 @@ sprint_status48(char *os, int status, int sigonly)
4308static int 4972static int
4309waitproc(int block, int *status) 4973waitproc(int block, int *status)
4310{ 4974{
4975#if !ENABLE_PLATFORM_MINGW32
4311 sigset_t oldmask; 4976 sigset_t oldmask;
4312 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; 4977 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4313 int err; 4978 int err;
@@ -4338,6 +5003,11 @@ waitproc(int block, int *status)
4338 } while (got_sigchld); 5003 } while (got_sigchld);
4339 5004
4340 return err; 5005 return err;
5006#else
5007 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
5008 *status = 0;
5009 return waitpid(-1, status, flags);
5010#endif
4341} 5011}
4342 5012
4343static int 5013static int
@@ -4394,6 +5064,10 @@ waitone(int block, struct job *job)
4394 jobno(jp), pid, ps->ps_status, status)); 5064 jobno(jp), pid, ps->ps_status, status));
4395 ps->ps_status = status; 5065 ps->ps_status = status;
4396 thisjob = jp; 5066 thisjob = jp;
5067#if ENABLE_PLATFORM_MINGW32
5068 CloseHandle(ps->ps_proc);
5069 ps->ps_proc = NULL;
5070#endif
4397 } 5071 }
4398 if (ps->ps_status == -1) 5072 if (ps->ps_status == -1)
4399 jobstate = JOBRUNNING; 5073 jobstate = JOBRUNNING;
@@ -4456,6 +5130,7 @@ waitone(int block, struct job *job)
4456static int 5130static int
4457dowait(int block, struct job *jp) 5131dowait(int block, struct job *jp)
4458{ 5132{
5133#if !ENABLE_PLATFORM_MINGW32
4459 smallint gotchld = *(volatile smallint *)&got_sigchld; 5134 smallint gotchld = *(volatile smallint *)&got_sigchld;
4460 int rpid; 5135 int rpid;
4461 int pid; 5136 int pid;
@@ -4477,9 +5152,17 @@ dowait(int block, struct job *jp)
4477 } while (pid >= 0); 5152 } while (pid >= 0);
4478 5153
4479 return rpid; 5154 return rpid;
5155#else
5156 int pid = 1;
5157
5158 while (jp ? jp->state == JOBRUNNING : pid > 0)
5159 pid = waitone(block, jp);
5160
5161 return pid;
5162#endif
4480} 5163}
4481 5164
4482#if JOBS 5165#if JOBS || JOBS_WIN32
4483static void 5166static void
4484showjob(struct job *jp, int mode) 5167showjob(struct job *jp, int mode)
4485{ 5168{
@@ -4494,7 +5177,7 @@ showjob(struct job *jp, int mode)
4494 5177
4495 if (mode & SHOW_ONLY_PGID) { /* jobs -p */ 5178 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
4496 /* just output process (group) id of pipeline */ 5179 /* just output process (group) id of pipeline */
4497 fprintf(out, "%d\n", ps->ps_pid); 5180 fprintf(out, "%"PID_FMT"d\n", ps->ps_pid);
4498 return; 5181 return;
4499 } 5182 }
4500 5183
@@ -4507,7 +5190,7 @@ showjob(struct job *jp, int mode)
4507 s[col - 3] = '-'; 5190 s[col - 3] = '-';
4508 5191
4509 if (mode & SHOW_PIDS) 5192 if (mode & SHOW_PIDS)
4510 col += fmtstr(s + col, 16, "%d ", ps->ps_pid); 5193 col += fmtstr(s + col, 16, "%"PID_FMT"d ", ps->ps_pid);
4511 5194
4512 psend = ps + jp->nprocs; 5195 psend = ps + jp->nprocs;
4513 5196
@@ -4516,8 +5199,10 @@ showjob(struct job *jp, int mode)
4516 col += sizeof("Running") - 1; 5199 col += sizeof("Running") - 1;
4517 } else { 5200 } else {
4518 int status = psend[-1].ps_status; 5201 int status = psend[-1].ps_status;
5202#if !ENABLE_PLATFORM_MINGW32
4519 if (jp->state == JOBSTOPPED) 5203 if (jp->state == JOBSTOPPED)
4520 status = jp->stopstatus; 5204 status = jp->stopstatus;
5205#endif
4521 col += sprint_status48(s + col, status, 0); 5206 col += sprint_status48(s + col, status, 0);
4522 } 5207 }
4523 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ 5208 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
@@ -4536,14 +5221,18 @@ showjob(struct job *jp, int mode)
4536 s[0] = '\0'; 5221 s[0] = '\0';
4537 col = 33; 5222 col = 33;
4538 if (mode & SHOW_PIDS) 5223 if (mode & SHOW_PIDS)
4539 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; 5224 col = fmtstr(s, 48, "\n%*c%"PID_FMT"d ", indent_col, ' ', ps->ps_pid) - 1;
4540 start: 5225 start:
5226#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4541 fprintf(out, "%s%*c%s%s", 5227 fprintf(out, "%s%*c%s%s",
4542 s, 5228 s,
4543 33 - col >= 0 ? 33 - col : 0, ' ', 5229 33 - col >= 0 ? 33 - col : 0, ' ',
4544 ps == jp->ps ? "" : "| ", 5230 ps == jp->ps ? "" : "| ",
4545 ps->ps_cmd 5231 ps->ps_cmd
4546 ); 5232 );
5233#else
5234 fprintf(out, "%s", s);
5235#endif
4547 } while (++ps != psend); 5236 } while (++ps != psend);
4548 newline_and_flush(out); 5237 newline_and_flush(out);
4549 5238
@@ -4628,7 +5317,7 @@ getstatus(struct job *job)
4628 { 5317 {
4629 /* XXX: limits number of signals */ 5318 /* XXX: limits number of signals */
4630 retval = WTERMSIG(status); 5319 retval = WTERMSIG(status);
4631#if JOBS 5320#if JOBS || ENABLE_PLATFORM_MINGW32
4632 if (retval == SIGINT) 5321 if (retval == SIGINT)
4633 job->sigint = 1; 5322 job->sigint = 1;
4634#endif 5323#endif
@@ -4800,7 +5489,7 @@ makejob(/*union node *node,*/ int nprocs)
4800 break; 5489 break;
4801 if (jp->state != JOBDONE || !jp->waited) 5490 if (jp->state != JOBDONE || !jp->waited)
4802 continue; 5491 continue;
4803#if JOBS 5492#if JOBS || JOBS_WIN32
4804 if (doing_jobctl) 5493 if (doing_jobctl)
4805 continue; 5494 continue;
4806#endif 5495#endif
@@ -4826,7 +5515,7 @@ makejob(/*union node *node,*/ int nprocs)
4826 return jp; 5515 return jp;
4827} 5516}
4828 5517
4829#if JOBS 5518#if JOBS || JOBS_WIN32
4830/* 5519/*
4831 * Return a string identifying a command (to be printed by the 5520 * Return a string identifying a command (to be printed by the
4832 * jobs command). 5521 * jobs command).
@@ -5168,6 +5857,7 @@ clear_traps(void)
5168 INT_ON; 5857 INT_ON;
5169} 5858}
5170 5859
5860#if !ENABLE_PLATFORM_MINGW32
5171/* Lives far away from here, needed for forkchild */ 5861/* Lives far away from here, needed for forkchild */
5172static void closescript(void); 5862static void closescript(void);
5173 5863
@@ -5294,14 +5984,22 @@ forkchild(struct job *jp, union node *n, int mode)
5294 for (jp = curjob; jp; jp = jp->prev_job) 5984 for (jp = curjob; jp; jp = jp->prev_job)
5295 freejob(jp); 5985 freejob(jp);
5296} 5986}
5987#endif
5297 5988
5298/* Called after fork(), in parent */ 5989/* Called after fork(), in parent */
5299#if !JOBS 5990#if !JOBS && !JOBS_WIN32
5300#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) 5991#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5301#endif 5992#endif
5302static void 5993static void
5994#if !ENABLE_PLATFORM_MINGW32
5303forkparent(struct job *jp, union node *n, int mode, pid_t pid) 5995forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5996#else
5997forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
5998#endif
5304{ 5999{
6000#if ENABLE_PLATFORM_MINGW32
6001 pid_t pid = GetProcessId(proc);
6002#endif
5305 TRACE(("In parent shell: child = %d\n", pid)); 6003 TRACE(("In parent shell: child = %d\n", pid));
5306 if (!jp) /* jp is NULL when called by openhere() for heredoc support */ 6004 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
5307 return; 6005 return;
@@ -5320,19 +6018,29 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5320 if (mode == FORK_BG) { 6018 if (mode == FORK_BG) {
5321 backgndpid = pid; /* set $! */ 6019 backgndpid = pid; /* set $! */
5322 set_curjob(jp, CUR_RUNNING); 6020 set_curjob(jp, CUR_RUNNING);
6021#if ENABLE_PLATFORM_MINGW32
6022 if (iflag && jp && jp->nprocs == 0)
6023 fprintf(stderr, "[%d] %"PID_FMT"d\n", jobno(jp), pid);
6024#endif
5323 } 6025 }
5324 if (jp) { 6026 if (jp) {
5325 struct procstat *ps = &jp->ps[jp->nprocs++]; 6027 struct procstat *ps = &jp->ps[jp->nprocs++];
5326 ps->ps_pid = pid; 6028 ps->ps_pid = pid;
5327 ps->ps_status = -1; 6029 ps->ps_status = -1;
6030#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
5328 ps->ps_cmd = nullstr; 6031 ps->ps_cmd = nullstr;
5329#if JOBS 6032#endif
6033#if ENABLE_PLATFORM_MINGW32
6034 ps->ps_proc = proc;
6035#endif
6036#if JOBS || JOBS_WIN32
5330 if (doing_jobctl && n) 6037 if (doing_jobctl && n)
5331 ps->ps_cmd = commandtext(n); 6038 ps->ps_cmd = commandtext(n);
5332#endif 6039#endif
5333 } 6040 }
5334} 6041}
5335 6042
6043#if !ENABLE_PLATFORM_MINGW32
5336/* jp and n are NULL when called by openhere() for heredoc support */ 6044/* jp and n are NULL when called by openhere() for heredoc support */
5337static int 6045static int
5338forkshell(struct job *jp, union node *n, int mode) 6046forkshell(struct job *jp, union node *n, int mode)
@@ -5355,6 +6063,7 @@ forkshell(struct job *jp, union node *n, int mode)
5355 } 6063 }
5356 return pid; 6064 return pid;
5357} 6065}
6066#endif
5358 6067
5359/* 6068/*
5360 * Wait for job to finish. 6069 * Wait for job to finish.
@@ -5418,6 +6127,10 @@ waitforjob(struct job *jp)
5418 return exitstatus; 6127 return exitstatus;
5419 6128
5420 st = getstatus(jp); 6129 st = getstatus(jp);
6130#if ENABLE_PLATFORM_MINGW32
6131 if (!jp->sigint && iflag && rootshell)
6132 pending_int = 0;
6133#endif
5421#if JOBS 6134#if JOBS
5422 if (jp->jobctl) { 6135 if (jp->jobctl) {
5423 xtcsetpgrp(ttyfd, rootpid); 6136 xtcsetpgrp(ttyfd, rootpid);
@@ -5443,6 +6156,7 @@ waitforjob(struct job *jp)
5443/* 6156/*
5444 * return 1 if there are stopped jobs, otherwise 0 6157 * return 1 if there are stopped jobs, otherwise 0
5445 */ 6158 */
6159#if !ENABLE_PLATFORM_MINGW32
5446static int 6160static int
5447stoppedjobs(void) 6161stoppedjobs(void)
5448{ 6162{
@@ -5461,7 +6175,17 @@ stoppedjobs(void)
5461 out: 6175 out:
5462 return retval; 6176 return retval;
5463} 6177}
5464 6178#else
6179static int
6180stoppedjobs(void)
6181{
6182 if (iflag && curjob) {
6183 out2str("You have background jobs.\n");
6184 return 1;
6185 }
6186 return 0;
6187}
6188#endif
5465 6189
5466/* 6190/*
5467 * Code for dealing with input/output redirection. 6191 * Code for dealing with input/output redirection.
@@ -5485,6 +6209,7 @@ openhere(union node *redir)
5485 char *p; 6209 char *p;
5486 int pip[2]; 6210 int pip[2];
5487 size_t len = 0; 6211 size_t len = 0;
6212 IF_PLATFORM_MINGW32(struct forkshell fs);
5488 6213
5489 if (pipe(pip) < 0) 6214 if (pipe(pip) < 0)
5490 ash_msg_and_raise_perror("can't create pipe"); 6215 ash_msg_and_raise_perror("can't create pipe");
@@ -5501,6 +6226,14 @@ openhere(union node *redir)
5501 goto out; 6226 goto out;
5502 } 6227 }
5503 6228
6229#if ENABLE_PLATFORM_MINGW32
6230 memset(&fs, 0, sizeof(fs));
6231 fs.fpid = FS_OPENHERE;
6232 fs.fd[0] = pip[0];
6233 fs.fd[1] = pip[1];
6234 fs.path = p;
6235 spawn_forkshell(&fs, NULL, NULL, FORK_NOJOB);
6236#else
5504 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { 6237 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
5505 /* child */ 6238 /* child */
5506 close(pip[0]); 6239 close(pip[0]);
@@ -5512,6 +6245,7 @@ openhere(union node *redir)
5512 xwrite(pip[1], p, len); 6245 xwrite(pip[1], p, len);
5513 _exit_SUCCESS(); 6246 _exit_SUCCESS();
5514 } 6247 }
6248#endif
5515 out: 6249 out:
5516 close(pip[1]); 6250 close(pip[1]);
5517 return pip[0]; 6251 return pip[0];
@@ -5589,6 +6323,9 @@ openredirect(union node *redir)
5589 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); 6323 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5590 if (f < 0) 6324 if (f < 0)
5591 goto ecreate; 6325 goto ecreate;
6326#if ENABLE_PLATFORM_MINGW32
6327 lseek(f, 0, SEEK_END);
6328#endif
5592 break; 6329 break;
5593 } 6330 }
5594 6331
@@ -5799,6 +6536,12 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
5799 if (fd == preverrout_fd) 6536 if (fd == preverrout_fd)
5800 preverrout_fd = new_fd; 6537 preverrout_fd = new_fd;
5801 6538
6539#if ENABLE_PLATFORM_MINGW32 && !defined(_UCRT)
6540 // Workaround for problems with stderr in MSVCRT
6541 if (fd == fileno(stderr))
6542 setvbuf(stderr, NULL, _IONBF, 0);
6543#endif
6544
5802 return 0; /* "we did not close fd" */ 6545 return 0; /* "we did not close fd" */
5803} 6546}
5804 6547
@@ -6152,6 +6895,9 @@ ifsbreakup(char *string, struct arglist *arglist)
6152 const char *ifs, *realifs; 6895 const char *ifs, *realifs;
6153 int ifsspc; 6896 int ifsspc;
6154 int nulonly; 6897 int nulonly;
6898#if ENABLE_PLATFORM_MINGW32
6899 int lshift = 0;
6900#endif
6155 6901
6156 start = string; 6902 start = string;
6157 if (ifslastp != NULL) { 6903 if (ifslastp != NULL) {
@@ -6162,7 +6908,33 @@ ifsbreakup(char *string, struct arglist *arglist)
6162 do { 6908 do {
6163 int afternul; 6909 int afternul;
6164 6910
6911#if ENABLE_PLATFORM_MINGW32
6912 /* Adjust region offsets for left-shifted string. */
6913 ifsp->begoff -= lshift;
6914 ifsp->endoff -= lshift;
6915#endif
6165 p = string + ifsp->begoff; 6916 p = string + ifsp->begoff;
6917#if ENABLE_PLATFORM_MINGW32
6918 if (ifsp->endoff > ifsp->begoff + 1) {
6919 /* Transform CRLF to LF. Skip regions having zero or
6920 * one characters: they can't contain CRLF. If the
6921 * region shrinks shift the rest of the string left. */
6922 int oldlen = ifsp->endoff - ifsp->begoff;
6923 int newlen = remove_cr(p, oldlen);
6924 int delta = oldlen - newlen;
6925
6926 if (delta > 0) {
6927 char *t = string + ifsp->endoff;
6928 char *s = string + ifsp->endoff - delta;
6929
6930 while (*t)
6931 *s++ = *t++;
6932 *s = '\0';
6933 lshift += delta;
6934 ifsp->endoff -= delta;
6935 }
6936 }
6937#endif
6166 afternul = nulonly; 6938 afternul = nulonly;
6167 nulonly = ifsp->nulonly; 6939 nulonly = ifsp->nulonly;
6168 ifs = nulonly ? nullstr : realifs; 6940 ifs = nulonly ? nullstr : realifs;
@@ -6607,6 +7379,7 @@ evalbackcmd(union node *n, struct backcmd *result
6607 const int ip = 0; 7379 const int ip = 0;
6608 const int ic = 1; 7380 const int ic = 1;
6609#endif 7381#endif
7382 IF_PLATFORM_MINGW32(struct forkshell fs);
6610 7383
6611 result->fd = -1; 7384 result->fd = -1;
6612 result->buf = NULL; 7385 result->buf = NULL;
@@ -6620,6 +7393,15 @@ evalbackcmd(union node *n, struct backcmd *result
6620 ash_msg_and_raise_perror("can't create pipe"); 7393 ash_msg_and_raise_perror("can't create pipe");
6621 /* process substitution uses NULL job, like openhere() */ 7394 /* process substitution uses NULL job, like openhere() */
6622 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; 7395 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;
7396#if ENABLE_PLATFORM_MINGW32
7397 memset(&fs, 0, sizeof(fs));
7398 fs.fpid = FS_EVALBACKCMD;
7399 fs.n = n;
7400 fs.fd[0] = pip[0];
7401 fs.fd[1] = pip[1];
7402 fs.fd[2] = ctl;
7403 spawn_forkshell(&fs, jp, n, FORK_NOJOB);
7404#else
6623 if (forkshell(jp, n, FORK_NOJOB) == 0) { 7405 if (forkshell(jp, n, FORK_NOJOB) == 0) {
6624 /* child */ 7406 /* child */
6625 FORCE_INT_ON; 7407 FORCE_INT_ON;
@@ -6643,6 +7425,7 @@ evalbackcmd(union node *n, struct backcmd *result
6643 evaltreenr(n, EV_EXIT); 7425 evaltreenr(n, EV_EXIT);
6644 /* NOTREACHED */ 7426 /* NOTREACHED */
6645 } 7427 }
7428#endif
6646 /* parent */ 7429 /* parent */
6647#if BASH_PROCESS_SUBST 7430#if BASH_PROCESS_SUBST
6648 if (ctl != CTLBACKQ) { 7431 if (ctl != CTLBACKQ) {
@@ -6721,8 +7504,14 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
6721 7504
6722 /* Eat all trailing newlines */ 7505 /* Eat all trailing newlines */
6723 dest = expdest; 7506 dest = expdest;
6724 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) 7507 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) {
6725 STUNPUTC(dest); 7508 STUNPUTC(dest);
7509#if ENABLE_PLATFORM_MINGW32
7510 if (dest > ((char *)stackblock() + startloc) && dest[-1] == '\r') {
7511 STUNPUTC(dest);
7512 }
7513#endif
7514 }
6726 expdest = dest; 7515 expdest = dest;
6727 7516
6728 if (!(flag & EXP_QUOTED)) 7517 if (!(flag & EXP_QUOTED))
@@ -7869,6 +8658,26 @@ expandmeta(struct strlist *str /*, int flag*/)
7869#else 8658#else
7870/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */ 8659/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
7871 8660
8661#if ENABLE_ASH_GLOB_OPTIONS
8662static int FAST_FUNC
8663ash_accept_glob(const char *name)
8664{
8665 struct stat st;
8666
8667 if (nohiddenglob || nohidsysglob) {
8668 if (!lstat(name, &st)) {
8669 if ((st.st_attr & FILE_ATTRIBUTE_HIDDEN)) {
8670 if (nohiddenglob ||
8671 (st.st_attr & FILE_ATTRIBUTE_SYSTEM)) {
8672 return FALSE;
8673 }
8674 }
8675 }
8676 }
8677 return TRUE;
8678}
8679#endif
8680
7872/* 8681/*
7873 * Do metacharacter (i.e. *, ?, [...]) expansion. 8682 * Do metacharacter (i.e. *, ?, [...]) expansion.
7874 */ 8683 */
@@ -7896,6 +8705,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7896 8705
7897 metaflag = 0; 8706 metaflag = 0;
7898 start = name; 8707 start = name;
8708#if ENABLE_PLATFORM_MINGW32
8709 if (expdir_len == 0 && has_dos_drive_prefix(start) && start[2] != '/')
8710 start += 2;
8711#endif
7899 for (p = name; esc = 0, *p; p += esc + 1) { 8712 for (p = name; esc = 0, *p; p += esc + 1) {
7900 if (*p == '*' || *p == '?') 8713 if (*p == '*' || *p == '?')
7901 metaflag = 1; 8714 metaflag = 1;
@@ -7970,8 +8783,16 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7970 while (!pending_int && (dp = readdir(dirp)) != NULL) { 8783 while (!pending_int && (dp = readdir(dirp)) != NULL) {
7971 if (dp->d_name[0] == '.' && !matchdot) 8784 if (dp->d_name[0] == '.' && !matchdot)
7972 continue; 8785 continue;
8786#if ENABLE_ASH_GLOB_OPTIONS
8787# undef pmatch
8788# define pmatch(a, b) !fnmatch((a), (b), nocaseglob ? FNM_CASEFOLD : 0)
8789#endif
7973 if (pmatch(start, dp->d_name)) { 8790 if (pmatch(start, dp->d_name)) {
7974 if (atend) { 8791 if (atend) {
8792#if ENABLE_ASH_GLOB_OPTIONS
8793 if (!ash_accept_glob(dp->d_name))
8794 continue;
8795#endif
7975 strcpy(enddir, dp->d_name); 8796 strcpy(enddir, dp->d_name);
7976 addfname(expdir); 8797 addfname(expdir);
7977 } else { 8798 } else {
@@ -7999,6 +8820,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7999 endname[-esc - 1] = esc ? '\\' : '/'; 8820 endname[-esc - 1] = esc ? '\\' : '/';
8000#undef expdir 8821#undef expdir
8001#undef expdir_max 8822#undef expdir_max
8823#if ENABLE_ASH_GLOB_OPTIONS
8824# undef pmatch
8825# define pmatch(a, b) !fnmatch((a), (b), 0)
8826#endif
8002} 8827}
8003 8828
8004static struct strlist * 8829static struct strlist *
@@ -8271,14 +9096,40 @@ static int builtinloc = -1; /* index in path of %builtin, or -1 */
8271 9096
8272 9097
8273static void 9098static void
9099#if ENABLE_PLATFORM_MINGW32
9100tryexec(IF_FEATURE_SH_STANDALONE(int applet_no, const char *path, int noexec,)
9101 const char *cmd, char **argv, char **envp)
9102#else
8274tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp) 9103tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp)
9104#endif
8275{ 9105{
9106#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
9107 interp_t interp;
9108#endif
8276#if ENABLE_FEATURE_SH_STANDALONE 9109#if ENABLE_FEATURE_SH_STANDALONE
8277 if (applet_no >= 0) { 9110 if (applet_no >= 0) {
9111# if ENABLE_PLATFORM_MINGW32
9112 /* Treat all applets as NOEXEC, including the shell itself
9113 * if we were called from forkshell_shellexec(). */
9114 run_noexec:
9115 if (applet_main[applet_no] != ash_main || noexec) {
9116 /* mingw-w64's getopt() uses __argv[0] as the program name */
9117 __argv[0] = (char *)cmd;
9118 /* 'which' wants to know if it was invoked from a standalone
9119 * shell. 'Which' in argv[0] indicates this. */
9120 if (strcmp(argv[0], "which") == 0) {
9121 argv[0] = (char *)"Which";
9122 }
9123# else
8278 if (APPLET_IS_NOEXEC(applet_no)) { 9124 if (APPLET_IS_NOEXEC(applet_no)) {
9125# endif
9126#if !ENABLE_PLATFORM_MINGW32 || !defined(_UCRT)
9127 /* If building for UCRT move this up into shellexec() to
9128 * work around a bug. */
8279 clearenv(); 9129 clearenv();
8280 while (*envp) 9130 while (*envp)
8281 putenv(*envp++); 9131 putenv(*envp++);
9132#endif
8282 popredir(/*drop:*/ 1); 9133 popredir(/*drop:*/ 1);
8283 run_noexec_applet_and_exit(applet_no, cmd, argv); 9134 run_noexec_applet_and_exit(applet_no, cmd, argv);
8284 } 9135 }
@@ -8289,6 +9140,44 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8289 } 9140 }
8290#endif 9141#endif
8291 9142
9143#if ENABLE_PLATFORM_MINGW32
9144 /* Workaround for libtool, which assumes the host is an MSYS2
9145 * environment and requires special-case escaping for cmd.exe.
9146 * https://github.com/skeeto/w64devkit/issues/50 */
9147 if (string_array_len(argv) >= 3 &&
9148 strcmp(argv[0], "cmd") == 0 &&
9149 strcmp(argv[1], "//c") == 0 &&
9150 strcmp(argv[2], "echo") == 0) {
9151 argv[1]++; /* drop extra slash */
9152 }
9153
9154 /* cmd was allocated on the stack with room for an extension */
9155 add_win32_extension((char *)cmd);
9156
9157# if ENABLE_FEATURE_SH_STANDALONE
9158 /* If the command is a script with an interpreter which is an
9159 * applet, we can run it as if it were a noexec applet. */
9160 if (parse_interpreter(cmd, &interp)) {
9161 applet_no = find_applet_by_name_for_sh(interp.name, path);
9162 if (applet_no >= 0) {
9163 argv[0] = (char *)cmd;
9164 /* evalcommand()/spawn_forkshell() add two elements before argv */
9165 if (interp.opts) {
9166 argv--;
9167 argv[0] = (char *)interp.opts;
9168 }
9169 argv--;
9170 cmd = argv[0] = (char *)interp.name;
9171 /* Identify the index of the script file in argv */
9172 set_interp(1 + (interp.opts != NULL));
9173 goto run_noexec;
9174 }
9175 }
9176# endif
9177
9178 execve(cmd, argv, envp);
9179 /* skip POSIX-mandated retry on ENOEXEC */
9180#else /* !ENABLE_PLATFORM_MINGW32 */
8292 repeat: 9181 repeat:
8293#ifdef SYSV 9182#ifdef SYSV
8294 do { 9183 do {
@@ -8324,15 +9213,23 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8324 argv[0] = (char*) "ash"; 9213 argv[0] = (char*) "ash";
8325 goto repeat; 9214 goto repeat;
8326 } 9215 }
9216#endif /* !ENABLE_PLATFORM_MINGW32 */
8327} 9217}
8328 9218
9219#if !ENABLE_PLATFORM_MINGW32 || !ENABLE_FEATURE_SH_STANDALONE
9220# define shellexec(prg, a, pth, i, n) shellexec(prg, a, pth, i)
9221#endif
9222
8329/* 9223/*
8330 * Exec a program. Never returns. If you change this routine, you may 9224 * Exec a program. Never returns. If you change this routine, you may
8331 * have to change the find_command routine as well. 9225 * have to change the find_command routine as well.
8332 * argv[-1] must exist and be writable! See tryexec() for why. 9226 * argv[-1] must exist and be writable! See tryexec() for why.
8333 */ 9227 */
8334static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; 9228static struct builtincmd *find_builtin(const char *name);
8335static void shellexec(char *prog, char **argv, const char *path, int idx) 9229static void shellexec(char *prog, char **argv, const char *path, int idx,
9230 int noexec) NORETURN;
9231static void shellexec(char *prog, char **argv, const char *path, int idx,
9232 int noexec)
8336{ 9233{
8337 char *cmdname; 9234 char *cmdname;
8338 int e; 9235 int e;
@@ -8341,12 +9238,30 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8341 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ 9238 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
8342 9239
8343 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 9240 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
9241#if ENABLE_FEATURE_SH_STANDALONE && ENABLE_PLATFORM_MINGW32 && defined(_UCRT)
9242 /* Avoid UCRT bug by updating parent's environment and passing a
9243 * NULL environment pointer to execve(). */
9244 clearenv();
9245 while (*envp)
9246 putenv(*envp++);
9247 envp = NULL;
9248#endif
9249#if !ENABLE_PLATFORM_MINGW32
8344 if (strchr(prog, '/') != NULL 9250 if (strchr(prog, '/') != NULL
9251#else
9252 if (has_path(prog)
9253#endif
8345#if ENABLE_FEATURE_SH_STANDALONE 9254#if ENABLE_FEATURE_SH_STANDALONE
8346 || (applet_no = find_applet_by_name(prog)) >= 0 9255 || (applet_no = find_applet_by_name_for_sh(prog, path)) >= 0
8347#endif 9256#endif
8348 ) { 9257 ) {
9258#if ENABLE_PLATFORM_MINGW32
9259 char *progext = stack_add_ext_space(prog);
9260 tryexec(IF_FEATURE_SH_STANDALONE(applet_no, path, noexec,)
9261 progext, argv, envp);
9262#else
8349 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp); 9263 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
9264#endif
8350 if (applet_no >= 0) { 9265 if (applet_no >= 0) {
8351 /* We tried execing ourself, but it didn't work. 9266 /* We tried execing ourself, but it didn't work.
8352 * Maybe /proc/self/exe doesn't exist? 9267 * Maybe /proc/self/exe doesn't exist?
@@ -8355,13 +9270,33 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8355 goto try_PATH; 9270 goto try_PATH;
8356 } 9271 }
8357 e = errno; 9272 e = errno;
9273#if ENABLE_PLATFORM_MINGW32
9274 if (unix_path(prog)) {
9275 const char *name = bb_basename(prog);
9276# if ENABLE_FEATURE_SH_STANDALONE
9277 if ((applet_no = find_applet_by_name_for_sh(name, path)) >= 0) {
9278 tryexec(applet_no, path, noexec, name, argv, envp);
9279 e = errno;
9280 }
9281# endif
9282 if (!find_builtin(name)) {
9283 argv[0] = (char *)name;
9284 goto try_PATH;
9285 }
9286 }
9287#endif
8358 } else { 9288 } else {
8359 try_PATH: 9289 try_PATH:
8360 e = ENOENT; 9290 e = ENOENT;
8361 while (padvance(&path, argv[0]) >= 0) { 9291 while (padvance(&path, argv[0]) >= 0) {
8362 cmdname = stackblock(); 9292 cmdname = stackblock();
8363 if (--idx < 0 && pathopt == NULL) { 9293 if (--idx < 0 && pathopt == NULL) {
9294#if ENABLE_PLATFORM_MINGW32
9295 tryexec(IF_FEATURE_SH_STANDALONE(-1, path, noexec,)
9296 cmdname, argv, envp);
9297#else
8364 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); 9298 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
9299#endif
8365 if (errno != ENOENT && errno != ENOTDIR) 9300 if (errno != ENOENT && errno != ENOTDIR)
8366 e = errno; 9301 e = errno;
8367 } 9302 }
@@ -8400,6 +9335,9 @@ printentry(struct tblentry *cmdp)
8400 padvance(&path, cmdp->cmdname); 9335 padvance(&path, cmdp->cmdname);
8401 } while (--idx >= 0); 9336 } while (--idx >= 0);
8402 name = stackblock(); 9337 name = stackblock();
9338#if ENABLE_PLATFORM_MINGW32
9339 add_win32_extension(name);
9340#endif
8403 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); 9341 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8404} 9342}
8405 9343
@@ -8600,7 +9538,7 @@ changepath(const char *newval)
8600 bltin = idx; 9538 bltin = idx;
8601 break; 9539 break;
8602 } 9540 }
8603 new = strchr(new, ':'); 9541 new = strchr(new, PATH_SEP);
8604 if (!new) 9542 if (!new)
8605 break; 9543 break;
8606 idx++; 9544 idx++;
@@ -8777,14 +9715,37 @@ describe_command(char *command, const char *path, int describe_command_verbose)
8777 case CMDNORMAL: { 9715 case CMDNORMAL: {
8778 int j = entry.u.index; 9716 int j = entry.u.index;
8779 char *p; 9717 char *p;
9718#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
9719 if (j < -1) {
9720 p = (char *)bb_basename(command);
9721 if (describe_command_verbose) {
9722 out1fmt(" is a builtin applet");
9723 } else {
9724 out1str(applet_to_exe(p));
9725 }
9726 break;
9727 }
9728#endif
8780 if (j < 0) { 9729 if (j < 0) {
9730#if ENABLE_PLATFORM_MINGW32
9731 p = stack_add_ext_space(command);
9732#else
8781 p = command; 9733 p = command;
9734#endif
8782 } else { 9735 } else {
9736#if ENABLE_PLATFORM_MINGW32
9737 if (unix_path(command))
9738 command = (char *)bb_basename(command);
9739#endif
8783 do { 9740 do {
8784 padvance(&path, command); 9741 padvance(&path, command);
8785 } while (--j >= 0); 9742 } while (--j >= 0);
8786 p = stackblock(); 9743 p = stackblock();
8787 } 9744 }
9745#if ENABLE_PLATFORM_MINGW32
9746 add_win32_extension(p);
9747 bs_to_slash(p);
9748#endif
8788 if (describe_command_verbose) { 9749 if (describe_command_verbose) {
8789 out1fmt(" is %s", p); 9750 out1fmt(" is %s", p);
8790 } else { 9751 } else {
@@ -8935,11 +9896,19 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8935} 9896}
8936#endif 9897#endif
8937 9898
8938
8939/*static int funcblocksize; // size of structures in function */ 9899/*static int funcblocksize; // size of structures in function */
8940/*static int funcstringsize; // size of strings in node */ 9900/*static int funcstringsize; // size of strings in node */
8941static void *funcblock; /* block to allocate function from */ 9901static void *funcblock; /* block to allocate function from */
8942static char *funcstring_end; /* end of block to allocate strings from */ 9902static char *funcstring_end; /* end of block to allocate strings from */
9903#if ENABLE_PLATFORM_MINGW32
9904static int fs_size;
9905static void *fs_start;
9906# if FORKSHELL_DEBUG
9907static void *fs_funcstring;
9908static const char **annot;
9909static char *annot_free;
9910# endif
9911#endif
8943 9912
8944static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 9913static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8945 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 9914 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -9072,14 +10041,79 @@ calcsize(int funcblocksize, union node *n)
9072} 10041}
9073 10042
9074static char * 10043static char *
9075nodeckstrdup(char *s) 10044nodeckstrdup(const char *s)
9076{ 10045{
10046#if ENABLE_PLATFORM_MINGW32
10047 if(!s)
10048 return NULL;
10049#endif
9077 funcstring_end -= SHELL_ALIGN(strlen(s) + 1); 10050 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
9078 return strcpy(funcstring_end, s); 10051 return strcpy(funcstring_end, s);
9079} 10052}
9080 10053
9081static union node *copynode(union node *); 10054static union node *copynode(union node *);
9082 10055
10056#if ENABLE_PLATFORM_MINGW32
10057# if FORKSHELL_DEBUG
10058# define MARK_PTR(dst,note,flag) forkshell_mark_ptr((void *)&dst, note, flag)
10059# else
10060# define MARK_PTR(dst,note,flag) forkshell_mark_ptr((void *)&dst)
10061# endif
10062
10063#define NO_FREE 0
10064#define FREE 1
10065
10066#if FORKSHELL_DEBUG
10067static void forkshell_mark_ptr(void *dst, const char *note, int flag)
10068#else
10069static void forkshell_mark_ptr(void *dst)
10070#endif
10071{
10072 char *lrelocate = (char *)fs_start + fs_size;
10073 int index = ((char *)dst - (char *)fs_start)/sizeof(char *);
10074
10075 lrelocate[index/8] |= 1 << (index % 8);
10076
10077#if FORKSHELL_DEBUG
10078 if (dst < fs_start || dst >= fs_funcstring) {
10079 fprintf(stderr, "dst (%p) out of range (%p %p)\n",
10080 dst, fs_start, fs_funcstring);
10081 }
10082 if (annot) {
10083 if (annot[index]) {
10084 fprintf(stderr, "duplicate annotation: %s %s\n",
10085 annot[index], note);
10086 }
10087 annot[index] = note;
10088 annot_free[index] = flag;
10089 }
10090#endif
10091}
10092
10093# define SAVE_PTR(dst,note,flag) { \
10094 if (fs_size) { \
10095 MARK_PTR(dst,note,flag); \
10096 } \
10097}
10098# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2) { \
10099 if (fs_size) { \
10100 MARK_PTR(dst1,note1,flag1); \
10101 MARK_PTR(dst2,note2,flag2); \
10102 } \
10103}
10104# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3) { \
10105 if (fs_size) { \
10106 MARK_PTR(dst1,note1,flag1); \
10107 MARK_PTR(dst2,note2,flag2); \
10108 MARK_PTR(dst3,note3,flag3); \
10109 } \
10110}
10111#else
10112# define SAVE_PTR(dst,note,flag)
10113# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2)
10114# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3)
10115#endif
10116
9083static struct nodelist * 10117static struct nodelist *
9084copynodelist(struct nodelist *lp) 10118copynodelist(struct nodelist *lp)
9085{ 10119{
@@ -9091,6 +10125,8 @@ copynodelist(struct nodelist *lp)
9091 *lpp = funcblock; 10125 *lpp = funcblock;
9092 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); 10126 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
9093 (*lpp)->n = copynode(lp->n); 10127 (*lpp)->n = copynode(lp->n);
10128 SAVE_PTR2((*lpp)->n, "(*lpp)->next", NO_FREE,
10129 (*lpp)->next, "(*lpp)->next", NO_FREE);
9094 lp = lp->next; 10130 lp = lp->next;
9095 lpp = &(*lpp)->next; 10131 lpp = &(*lpp)->next;
9096 } 10132 }
@@ -9114,10 +10150,14 @@ copynode(union node *n)
9114 new->ncmd.args = copynode(n->ncmd.args); 10150 new->ncmd.args = copynode(n->ncmd.args);
9115 new->ncmd.assign = copynode(n->ncmd.assign); 10151 new->ncmd.assign = copynode(n->ncmd.assign);
9116 new->ncmd.linno = n->ncmd.linno; 10152 new->ncmd.linno = n->ncmd.linno;
10153 SAVE_PTR3(new->ncmd.redirect, "ncmd.redirect", NO_FREE,
10154 new->ncmd.args, "ncmd.args", NO_FREE,
10155 new->ncmd.assign, "ncmd.assign", NO_FREE);
9117 break; 10156 break;
9118 case NPIPE: 10157 case NPIPE:
9119 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); 10158 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
9120 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; 10159 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
10160 SAVE_PTR(new->npipe.cmdlist, "npipe.cmdlist", NO_FREE);
9121 break; 10161 break;
9122 case NREDIR: 10162 case NREDIR:
9123 case NBACKGND: 10163 case NBACKGND:
@@ -9125,6 +10165,8 @@ copynode(union node *n)
9125 new->nredir.redirect = copynode(n->nredir.redirect); 10165 new->nredir.redirect = copynode(n->nredir.redirect);
9126 new->nredir.n = copynode(n->nredir.n); 10166 new->nredir.n = copynode(n->nredir.n);
9127 new->nredir.linno = n->nredir.linno; 10167 new->nredir.linno = n->nredir.linno;
10168 SAVE_PTR2(new->nredir.redirect, "nredir.redirect", NO_FREE,
10169 new->nredir.n, "nredir.n", NO_FREE);
9128 break; 10170 break;
9129 case NAND: 10171 case NAND:
9130 case NOR: 10172 case NOR:
@@ -9133,37 +10175,58 @@ copynode(union node *n)
9133 case NUNTIL: 10175 case NUNTIL:
9134 new->nbinary.ch2 = copynode(n->nbinary.ch2); 10176 new->nbinary.ch2 = copynode(n->nbinary.ch2);
9135 new->nbinary.ch1 = copynode(n->nbinary.ch1); 10177 new->nbinary.ch1 = copynode(n->nbinary.ch1);
10178 SAVE_PTR2(new->nbinary.ch1, "nbinary.ch1", NO_FREE,
10179 new->nbinary.ch2, "nbinary.ch2", NO_FREE);
9136 break; 10180 break;
9137 case NIF: 10181 case NIF:
9138 new->nif.elsepart = copynode(n->nif.elsepart); 10182 new->nif.elsepart = copynode(n->nif.elsepart);
9139 new->nif.ifpart = copynode(n->nif.ifpart); 10183 new->nif.ifpart = copynode(n->nif.ifpart);
9140 new->nif.test = copynode(n->nif.test); 10184 new->nif.test = copynode(n->nif.test);
10185 SAVE_PTR3(new->nif.elsepart, "nif.elsepart", NO_FREE,
10186 new->nif.ifpart, "nif.ifpart", NO_FREE,
10187 new->nif.test, "nif.test", NO_FREE);
9141 break; 10188 break;
9142 case NFOR: 10189 case NFOR:
9143 new->nfor.var = nodeckstrdup(n->nfor.var); 10190 new->nfor.var = nodeckstrdup(n->nfor.var);
9144 new->nfor.body = copynode(n->nfor.body); 10191 new->nfor.body = copynode(n->nfor.body);
9145 new->nfor.args = copynode(n->nfor.args); 10192 new->nfor.args = copynode(n->nfor.args);
9146 new->nfor.linno = n->nfor.linno; 10193 new->nfor.linno = n->nfor.linno;
10194 SAVE_PTR3(new->nfor.var,
10195 xasprintf("nfor.var '%s'", n->nfor.var ?: "NULL"), FREE,
10196 new->nfor.body, "nfor.body", NO_FREE,
10197 new->nfor.args, "nfor.args", NO_FREE);
9147 break; 10198 break;
9148 case NCASE: 10199 case NCASE:
9149 new->ncase.cases = copynode(n->ncase.cases); 10200 new->ncase.cases = copynode(n->ncase.cases);
9150 new->ncase.expr = copynode(n->ncase.expr); 10201 new->ncase.expr = copynode(n->ncase.expr);
9151 new->ncase.linno = n->ncase.linno; 10202 new->ncase.linno = n->ncase.linno;
10203 SAVE_PTR2(new->ncase.cases, "ncase.cases", NO_FREE,
10204 new->ncase.expr, "ncase.expr", NO_FREE);
9152 break; 10205 break;
9153 case NCLIST: 10206 case NCLIST:
9154 new->nclist.body = copynode(n->nclist.body); 10207 new->nclist.body = copynode(n->nclist.body);
9155 new->nclist.pattern = copynode(n->nclist.pattern); 10208 new->nclist.pattern = copynode(n->nclist.pattern);
9156 new->nclist.next = copynode(n->nclist.next); 10209 new->nclist.next = copynode(n->nclist.next);
10210 SAVE_PTR3(new->nclist.body, "nclist.body", NO_FREE,
10211 new->nclist.pattern, "nclist.pattern", NO_FREE,
10212 new->nclist.next, "nclist.next", NO_FREE);
9157 break; 10213 break;
9158 case NDEFUN: 10214 case NDEFUN:
9159 new->ndefun.body = copynode(n->ndefun.body); 10215 new->ndefun.body = copynode(n->ndefun.body);
9160 new->ndefun.text = nodeckstrdup(n->ndefun.text); 10216 new->ndefun.text = nodeckstrdup(n->ndefun.text);
9161 new->ndefun.linno = n->ndefun.linno; 10217 new->ndefun.linno = n->ndefun.linno;
10218 SAVE_PTR2(new->ndefun.body, "ndefun.body", NO_FREE,
10219 new->ndefun.text,
10220 xasprintf("ndefun.text '%s'", n->ndefun.text ?: "NULL"), FREE);
9162 break; 10221 break;
9163 case NARG: 10222 case NARG:
9164 new->narg.backquote = copynodelist(n->narg.backquote); 10223 new->narg.backquote = copynodelist(n->narg.backquote);
9165 new->narg.text = nodeckstrdup(n->narg.text); 10224 new->narg.text = nodeckstrdup(n->narg.text);
9166 new->narg.next = copynode(n->narg.next); 10225 new->narg.next = copynode(n->narg.next);
10226 SAVE_PTR3(new->narg.backquote, "narg.backquote", NO_FREE,
10227 new->narg.text,
10228 xasprintf("narg.text '%s'", n->narg.text ?: "NULL"), FREE,
10229 new->narg.next, "narg.next", NO_FREE);
9167 break; 10230 break;
9168 case NTO: 10231 case NTO:
9169#if BASH_REDIR_OUTPUT 10232#if BASH_REDIR_OUTPUT
@@ -9176,6 +10239,8 @@ copynode(union node *n)
9176 new->nfile.fname = copynode(n->nfile.fname); 10239 new->nfile.fname = copynode(n->nfile.fname);
9177 new->nfile.fd = n->nfile.fd; 10240 new->nfile.fd = n->nfile.fd;
9178 new->nfile.next = copynode(n->nfile.next); 10241 new->nfile.next = copynode(n->nfile.next);
10242 SAVE_PTR2(new->nfile.fname, "nfile.fname", NO_FREE,
10243 new->nfile.next, "nfile.next", NO_FREE);
9179 break; 10244 break;
9180 case NTOFD: 10245 case NTOFD:
9181 case NFROMFD: 10246 case NFROMFD:
@@ -9183,15 +10248,20 @@ copynode(union node *n)
9183 new->ndup.dupfd = n->ndup.dupfd; 10248 new->ndup.dupfd = n->ndup.dupfd;
9184 new->ndup.fd = n->ndup.fd; 10249 new->ndup.fd = n->ndup.fd;
9185 new->ndup.next = copynode(n->ndup.next); 10250 new->ndup.next = copynode(n->ndup.next);
10251 SAVE_PTR2(new->ndup.vname, "ndup.vname", NO_FREE,
10252 new->ndup.next, "ndup.next", NO_FREE);
9186 break; 10253 break;
9187 case NHERE: 10254 case NHERE:
9188 case NXHERE: 10255 case NXHERE:
9189 new->nhere.doc = copynode(n->nhere.doc); 10256 new->nhere.doc = copynode(n->nhere.doc);
9190 new->nhere.fd = n->nhere.fd; 10257 new->nhere.fd = n->nhere.fd;
9191 new->nhere.next = copynode(n->nhere.next); 10258 new->nhere.next = copynode(n->nhere.next);
10259 SAVE_PTR2(new->nhere.doc, "nhere.doc", NO_FREE,
10260 new->nhere.next, "nhere.next", NO_FREE);
9192 break; 10261 break;
9193 case NNOT: 10262 case NNOT:
9194 new->nnot.com = copynode(n->nnot.com); 10263 new->nnot.com = copynode(n->nnot.com);
10264 SAVE_PTR(new->nnot.com, "nnot.com", NO_FREE);
9195 break; 10265 break;
9196 }; 10266 };
9197 new->type = n->type; 10267 new->type = n->type;
@@ -9212,6 +10282,7 @@ copyfunc(union node *n)
9212 f = ckzalloc(blocksize /* + funcstringsize */); 10282 f = ckzalloc(blocksize /* + funcstringsize */);
9213 funcblock = (char *) f + offsetof(struct funcnode, n); 10283 funcblock = (char *) f + offsetof(struct funcnode, n);
9214 funcstring_end = (char *) f + blocksize; 10284 funcstring_end = (char *) f + blocksize;
10285 IF_PLATFORM_MINGW32(fs_size = 0);
9215 copynode(n); 10286 copynode(n);
9216 /* f->count = 0; - ckzalloc did it */ 10287 /* f->count = 0; - ckzalloc did it */
9217 return f; 10288 return f;
@@ -9239,12 +10310,15 @@ defun(union node *func)
9239#define SKIPFUNCDEF (1 << 3) 10310#define SKIPFUNCDEF (1 << 3)
9240static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 10311static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
9241static int skipcount; /* number of levels to skip */ 10312static int skipcount; /* number of levels to skip */
10313#if ENABLE_PLATFORM_POSIX
9242static int loopnest; /* current loop nesting level */ 10314static int loopnest; /* current loop nesting level */
10315#endif
9243static int funcline; /* starting line number of current function, or 0 if not in a function */ 10316static int funcline; /* starting line number of current function, or 0 if not in a function */
9244 10317
9245/* Forward decl way out to parsing code - dotrap needs it */ 10318/* Forward decl way out to parsing code - dotrap needs it */
9246static int evalstring(char *s, int flags); 10319static int evalstring(char *s, int flags);
9247 10320
10321#if !ENABLE_PLATFORM_MINGW32
9248/* Called to execute a trap. 10322/* Called to execute a trap.
9249 * Single callsite - at the end of evaltree(). 10323 * Single callsite - at the end of evaltree().
9250 * If we return non-zero, evaltree raises EXEXIT exception. 10324 * If we return non-zero, evaltree raises EXEXIT exception.
@@ -9303,6 +10377,45 @@ dotrap(void)
9303 savestatus = last_status; 10377 savestatus = last_status;
9304 TRACE(("dotrap returns\n")); 10378 TRACE(("dotrap returns\n"));
9305} 10379}
10380#else
10381static void
10382dotrap(void)
10383{
10384 int status, last_status;
10385 char *p;
10386
10387 if (!pending_int)
10388 return;
10389
10390 status = savestatus;
10391 last_status = status;
10392 if (status < 0) {
10393 status = exitstatus;
10394 savestatus = status;
10395 }
10396 pending_int = 0;
10397 barrier();
10398
10399 TRACE(("dotrap entered\n"));
10400 if (evalskip) {
10401 pending_int = 1;
10402 return;
10403 }
10404
10405 p = trap[SIGINT];
10406 if (p) {
10407 TRACE(("sig %d is active, will run handler '%s'\n", SIGINT, p));
10408 trap_depth++;
10409 evalstring(p, 0);
10410 trap_depth--;
10411 if (evalskip != SKIPFUNC)
10412 exitstatus = status;
10413 }
10414
10415 savestatus = last_status;
10416 TRACE(("dotrap returns\n"));
10417}
10418#endif
9306 10419
9307/* forward declarations - evaluation is fairly recursive business... */ 10420/* forward declarations - evaluation is fairly recursive business... */
9308static int evalloop(union node *, int); 10421static int evalloop(union node *, int);
@@ -9588,19 +10701,36 @@ evalcase(union node *n, int flags)
9588static int 10701static int
9589evalsubshell(union node *n, int flags) 10702evalsubshell(union node *n, int flags)
9590{ 10703{
10704 IF_PLATFORM_MINGW32(struct forkshell fs;)
9591 struct job *jp; 10705 struct job *jp;
9592 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ 10706 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9593 int status; 10707 int status;
9594 10708
9595 errlinno = lineno = n->nredir.linno; 10709 errlinno = lineno = n->nredir.linno;
9596 10710
10711#if ENABLE_PLATFORM_MINGW32
10712 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) {
10713 expredir(n->nredir.redirect);
10714 redirect(n->nredir.redirect, 0);
10715 evaltreenr(n->nredir.n, flags);
10716 /* never returns */
10717 }
10718#else
9597 expredir(n->nredir.redirect); 10719 expredir(n->nredir.redirect);
9598 if (!backgnd && (flags & EV_EXIT) && !may_have_traps) 10720 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
9599 goto nofork; 10721 goto nofork;
10722#endif
9600 INT_OFF; 10723 INT_OFF;
9601 if (backgnd == FORK_FG) 10724 if (backgnd == FORK_FG)
9602 get_tty_state(); 10725 get_tty_state();
9603 jp = makejob(/*n,*/ 1); 10726 jp = makejob(/*n,*/ 1);
10727#if ENABLE_PLATFORM_MINGW32
10728 memset(&fs, 0, sizeof(fs));
10729 fs.fpid = FS_EVALSUBSHELL;
10730 fs.n = n;
10731 fs.flags = flags;
10732 spawn_forkshell(&fs, jp, n, backgnd);
10733#else
9604 if (forkshell(jp, n, backgnd) == 0) { 10734 if (forkshell(jp, n, backgnd) == 0) {
9605 /* child */ 10735 /* child */
9606 INT_ON; 10736 INT_ON;
@@ -9612,6 +10742,7 @@ evalsubshell(union node *n, int flags)
9612 evaltreenr(n->nredir.n, flags); 10742 evaltreenr(n->nredir.n, flags);
9613 /* never returns */ 10743 /* never returns */
9614 } 10744 }
10745#endif
9615 /* parent */ 10746 /* parent */
9616 status = 0; 10747 status = 0;
9617 if (backgnd == FORK_FG) 10748 if (backgnd == FORK_FG)
@@ -9692,6 +10823,7 @@ expredir(union node *n)
9692static int 10823static int
9693evalpipe(union node *n, int flags) 10824evalpipe(union node *n, int flags)
9694{ 10825{
10826 IF_PLATFORM_MINGW32(struct forkshell fs;)
9695 struct job *jp; 10827 struct job *jp;
9696 struct nodelist *lp; 10828 struct nodelist *lp;
9697 int pipelen; 10829 int pipelen;
@@ -9718,6 +10850,16 @@ evalpipe(union node *n, int flags)
9718 ash_msg_and_raise_perror("can't create pipe"); 10850 ash_msg_and_raise_perror("can't create pipe");
9719 } 10851 }
9720 } 10852 }
10853#if ENABLE_PLATFORM_MINGW32
10854 memset(&fs, 0, sizeof(fs));
10855 fs.fpid = FS_EVALPIPE;
10856 fs.flags = flags;
10857 fs.n = lp->n;
10858 fs.fd[0] = pip[0];
10859 fs.fd[1] = pip[1];
10860 fs.fd[2] = prevfd;
10861 spawn_forkshell(&fs, jp, lp->n, n->npipe.pipe_backgnd);
10862#else
9721 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 10863 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
9722 /* child */ 10864 /* child */
9723 INT_ON; 10865 INT_ON;
@@ -9735,6 +10877,7 @@ evalpipe(union node *n, int flags)
9735 evaltreenr(lp->n, flags); 10877 evaltreenr(lp->n, flags);
9736 /* never returns */ 10878 /* never returns */
9737 } 10879 }
10880#endif
9738 /* parent */ 10881 /* parent */
9739 if (prevfd >= 0) 10882 if (prevfd >= 0)
9740 close(prevfd); 10883 close(prevfd);
@@ -9792,6 +10935,9 @@ setinteractive(int on)
9792 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP); 10935 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
9793# if ENABLE_FEATURE_TAB_COMPLETION 10936# if ENABLE_FEATURE_TAB_COMPLETION
9794 line_input_state->get_exe_name = ash_command_name; 10937 line_input_state->get_exe_name = ash_command_name;
10938# if ENABLE_ASH_GLOB_OPTIONS
10939 line_input_state->sh_accept_glob = ash_accept_glob;
10940# endif
9795# endif 10941# endif
9796# if EDITING_HAS_sh_get_var 10942# if EDITING_HAS_sh_get_var
9797 line_input_state->sh_get_var = lookupvar; 10943 line_input_state->sh_get_var = lookupvar;
@@ -9819,6 +10965,12 @@ optschanged(void)
9819#else 10965#else
9820 viflag = 0; /* forcibly keep the option off */ 10966 viflag = 0; /* forcibly keep the option off */
9821#endif 10967#endif
10968#if ENABLE_ASH_NOCONSOLE
10969 hide_console(noconsole);
10970#endif
10971#if ENABLE_PLATFORM_MINGW32
10972 setwinxp(winxp);
10973#endif
9822} 10974}
9823 10975
9824struct localvar_list { 10976struct localvar_list {
@@ -9838,6 +10990,9 @@ poplocalvars(int keep)
9838 struct localvar_list *ll; 10990 struct localvar_list *ll;
9839 struct localvar *lvp, *next; 10991 struct localvar *lvp, *next;
9840 struct var *vp; 10992 struct var *vp;
10993#if ENABLE_PLATFORM_MINGW32
10994 int var_type;
10995#endif
9841 10996
9842 INT_OFF; 10997 INT_OFF;
9843 ll = localvar_stack; 10998 ll = localvar_stack;
@@ -9880,6 +11035,17 @@ poplocalvars(int keep)
9880 free((char*)vp->var_text); 11035 free((char*)vp->var_text);
9881 vp->flags = lvp->flags; 11036 vp->flags = lvp->flags;
9882 vp->var_text = lvp->text; 11037 vp->var_text = lvp->text;
11038#if ENABLE_PLATFORM_MINGW32
11039 var_type = is_bb_var(lvp->text);
11040 if (var_type == BB_VAR_ASSIGN && (lvp->flags & VEXPORT))
11041 putenv(lvp->text);
11042 else if (var_type) {
11043 char *var = xstrdup(lvp->text);
11044 *strchrnul(var, '=') = '\0';
11045 unsetenv(var);
11046 free(var);
11047 }
11048#endif
9883 } 11049 }
9884 free(lvp); 11050 free(lvp);
9885 } 11051 }
@@ -10102,7 +11268,7 @@ execcmd(int argc UNUSED_PARAM, char **argv)
10102 prog = argv[0]; 11268 prog = argv[0];
10103 if (optionarg) 11269 if (optionarg)
10104 argv[0] = optionarg; 11270 argv[0] = optionarg;
10105 shellexec(prog, argv, pathval(), 0); 11271 shellexec(prog, argv, pathval(), 0, FALSE);
10106 /* NOTREACHED */ 11272 /* NOTREACHED */
10107 } 11273 }
10108 return 0; 11274 return 0;
@@ -10155,6 +11321,9 @@ static int readcmd(int, char **) FAST_FUNC;
10155static int setcmd(int, char **) FAST_FUNC; 11321static int setcmd(int, char **) FAST_FUNC;
10156static int shiftcmd(int, char **) FAST_FUNC; 11322static int shiftcmd(int, char **) FAST_FUNC;
10157static int timescmd(int, char **) FAST_FUNC; 11323static int timescmd(int, char **) FAST_FUNC;
11324#if ENABLE_PLATFORM_MINGW32
11325static int titlecmd(int, char **) FAST_FUNC;
11326#endif
10158static int trapcmd(int, char **) FAST_FUNC; 11327static int trapcmd(int, char **) FAST_FUNC;
10159static int umaskcmd(int, char **) FAST_FUNC; 11328static int umaskcmd(int, char **) FAST_FUNC;
10160static int unsetcmd(int, char **) FAST_FUNC; 11329static int unsetcmd(int, char **) FAST_FUNC;
@@ -10227,7 +11396,7 @@ static const struct builtincmd builtintab[] = {
10227#if MAX_HISTORY 11396#if MAX_HISTORY
10228 { BUILTIN_NOSPEC "history" , historycmd }, 11397 { BUILTIN_NOSPEC "history" , historycmd },
10229#endif 11398#endif
10230#if JOBS 11399#if JOBS || JOBS_WIN32
10231 { BUILTIN_REGULAR "jobs" , jobscmd }, 11400 { BUILTIN_REGULAR "jobs" , jobscmd },
10232 { BUILTIN_REGULAR "kill" , killcmd }, 11401 { BUILTIN_REGULAR "kill" , killcmd },
10233#endif 11402#endif
@@ -10254,6 +11423,9 @@ static const struct builtincmd builtintab[] = {
10254 { BUILTIN_REGULAR "test" , testcmd }, 11423 { BUILTIN_REGULAR "test" , testcmd },
10255#endif 11424#endif
10256 { BUILTIN_SPEC_REG "times" , timescmd }, 11425 { BUILTIN_SPEC_REG "times" , timescmd },
11426#if ENABLE_PLATFORM_MINGW32
11427 { BUILTIN_REGULAR "title" , titlecmd },
11428#endif
10257 { BUILTIN_SPEC_REG "trap" , trapcmd }, 11429 { BUILTIN_SPEC_REG "trap" , trapcmd },
10258 { BUILTIN_REGULAR "true" , truecmd }, 11430 { BUILTIN_REGULAR "true" , truecmd },
10259 { BUILTIN_REGULAR "type" , typecmd }, 11431 { BUILTIN_REGULAR "type" , typecmd },
@@ -10272,7 +11444,7 @@ static const struct builtincmd builtintab[] = {
10272 /* [ */ 1 * ENABLE_ASH_TEST + \ 11444 /* [ */ 1 * ENABLE_ASH_TEST + \
10273 /* [[ */ 1 * BASH_TEST2 + \ 11445 /* [[ */ 1 * BASH_TEST2 + \
10274 /* alias */ 1 * ENABLE_ASH_ALIAS + \ 11446 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10275 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ 11447 /* bg */ 1 * JOBS + \
10276 /* break cd cddir */ 3) 11448 /* break cd cddir */ 3)
10277#define EVALCMD (COMMANDCMD + \ 11449#define EVALCMD (COMMANDCMD + \
10278 /* command */ 1 * ENABLE_ASH_CMDCMD + \ 11450 /* command */ 1 * ENABLE_ASH_CMDCMD + \
@@ -10353,6 +11525,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10353 * as POSIX mandates */ 11525 * as POSIX mandates */
10354 return back_exitstatus; 11526 return back_exitstatus;
10355} 11527}
11528
10356static int 11529static int
10357evalcommand(union node *cmd, int flags) 11530evalcommand(union node *cmd, int flags)
10358{ 11531{
@@ -10452,9 +11625,19 @@ evalcommand(union node *cmd, int flags)
10452 11625
10453 localvar_stop = pushlocalvars(vlocal); 11626 localvar_stop = pushlocalvars(vlocal);
10454 11627
11628#if ENABLE_PLATFORM_MINGW32
11629# if ENABLE_FEATURE_SH_STANDALONE
11630 /* Reserve two extra spots at the front for shellexec. */
11631 nargv = stalloc(sizeof(char *) * (argc + 3));
11632 argv = nargv = nargv + 2;
11633# else
11634 argv = nargv = stalloc(sizeof(char *) * (argc + 1));
11635# endif
11636#else
10455 /* Reserve one extra spot at the front for shellexec. */ 11637 /* Reserve one extra spot at the front for shellexec. */
10456 nargv = stalloc(sizeof(char *) * (argc + 2)); 11638 nargv = stalloc(sizeof(char *) * (argc + 2));
10457 argv = ++nargv; 11639 argv = ++nargv;
11640#endif
10458 for (sp = arglist.list; sp; sp = sp->next) { 11641 for (sp = arglist.list; sp; sp = sp->next) {
10459 TRACE(("evalcommand arg: %s\n", sp->text)); 11642 TRACE(("evalcommand arg: %s\n", sp->text));
10460 *nargv++ = sp->text; 11643 *nargv++ = sp->text;
@@ -10563,9 +11746,11 @@ evalcommand(union node *cmd, int flags)
10563 11746
10564 default: { 11747 default: {
10565 11748
11749//TODO: find a better solution for Windows on ARM than ignoring NOFORK
10566#if ENABLE_FEATURE_SH_STANDALONE \ 11750#if ENABLE_FEATURE_SH_STANDALONE \
10567 && ENABLE_FEATURE_SH_NOFORK \ 11751 && ENABLE_FEATURE_SH_NOFORK \
10568 && NUM_APPLETS > 1 11752 && NUM_APPLETS > 1 \
11753 && !(defined(_ARM64_) && !defined(_UCRT) && ENABLE_PLATFORM_MINGW32)
10569/* (1) BUG: if variables are set, we need to fork, or save/restore them 11754/* (1) BUG: if variables are set, we need to fork, or save/restore them
10570 * around run_nofork_applet() call. 11755 * around run_nofork_applet() call.
10571 * (2) Should this check also be done in forkshell()? 11756 * (2) Should this check also be done in forkshell()?
@@ -10575,6 +11760,9 @@ evalcommand(union node *cmd, int flags)
10575 int applet_no = (- cmdentry.u.index - 2); 11760 int applet_no = (- cmdentry.u.index - 2);
10576 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { 11761 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
10577 char **sv_environ; 11762 char **sv_environ;
11763#if ENABLE_PLATFORM_MINGW32
11764 char *sv_argv0;
11765#endif
10578 11766
10579 INT_OFF; 11767 INT_OFF;
10580 sv_environ = environ; 11768 sv_environ = environ;
@@ -10587,8 +11775,16 @@ evalcommand(union node *cmd, int flags)
10587 * and/or wait for user input ineligible for NOFORK: 11775 * and/or wait for user input ineligible for NOFORK:
10588 * for example, "yes" or "rm" (rm -i waits for input). 11776 * for example, "yes" or "rm" (rm -i waits for input).
10589 */ 11777 */
11778#if ENABLE_PLATFORM_MINGW32
11779 sv_argv0 = __argv[0];
11780 argv[0] = (char *)bb_basename(argv[0]);
11781 __argv[0] = argv[0];
11782#endif
10590 exitstatus = run_nofork_applet(applet_no, argv); 11783 exitstatus = run_nofork_applet(applet_no, argv);
10591 environ = sv_environ; 11784 environ = sv_environ;
11785#if ENABLE_PLATFORM_MINGW32
11786 __argv[0] = sv_argv0;
11787#endif
10592 /* 11788 /*
10593 * Try enabling NOFORK for "yes" applet. 11789 * Try enabling NOFORK for "yes" applet.
10594 * ^C _will_ stop it (write returns EINTR), 11790 * ^C _will_ stop it (write returns EINTR),
@@ -10606,6 +11802,22 @@ evalcommand(union node *cmd, int flags)
10606 * in a script or a subshell does not need forking, 11802 * in a script or a subshell does not need forking,
10607 * we can just exec it. 11803 * we can just exec it.
10608 */ 11804 */
11805#if ENABLE_PLATFORM_MINGW32
11806 if (!(flags & EV_EXIT) || may_have_traps IF_SUW32(|| delayexit)) {
11807 /* No, forking off a child is necessary */
11808 struct forkshell fs;
11809
11810 INT_OFF;
11811 memset(&fs, 0, sizeof(fs));
11812 fs.fpid = FS_SHELLEXEC;
11813 fs.argv = argv;
11814 fs.path = (char*)path;
11815 fs.fd[0] = cmdentry.u.index;
11816 jp = makejob(/*cmd,*/ 1);
11817 spawn_forkshell(&fs, jp, cmd, FORK_FG);
11818 break;
11819 }
11820#else
10609 if (!(flags & EV_EXIT) || may_have_traps) { 11821 if (!(flags & EV_EXIT) || may_have_traps) {
10610 /* No, forking off a child is necessary */ 11822 /* No, forking off a child is necessary */
10611 INT_OFF; 11823 INT_OFF;
@@ -10619,7 +11831,8 @@ evalcommand(union node *cmd, int flags)
10619 FORCE_INT_ON; 11831 FORCE_INT_ON;
10620 /* fall through to exec'ing external program */ 11832 /* fall through to exec'ing external program */
10621 } 11833 }
10622 shellexec(argv[0], argv, path, cmdentry.u.index); 11834#endif
11835 shellexec(argv[0], argv, path, cmdentry.u.index, FALSE);
10623 /* NOTREACHED */ 11836 /* NOTREACHED */
10624 } /* default */ 11837 } /* default */
10625 case CMDBUILTIN: 11838 case CMDBUILTIN:
@@ -10698,7 +11911,6 @@ goodname(const char *p)
10698 return endofname(p)[0] == '\0'; 11911 return endofname(p)[0] == '\0';
10699} 11912}
10700 11913
10701
10702/* 11914/*
10703 * Search for a command. This is called before we fork so that the 11915 * Search for a command. This is called before we fork so that the
10704 * location of the command will be available in the parent as well as 11916 * location of the command will be available in the parent as well as
@@ -10831,6 +12043,54 @@ static void popstring(void)
10831 INT_ON; 12043 INT_ON;
10832} 12044}
10833 12045
12046#if ENABLE_PLATFORM_MINGW32
12047/*
12048 * Wrapper around nonblock_immune_read() to remove CRs, but only from
12049 * CRLF pairs. The tricky part is handling a CR at the end of the buffer.
12050 */
12051static inline ssize_t
12052nonblock_immune_wrapper(struct parsefile *pf, char *buffer, size_t count)
12053{
12054 int nr, injected_cr;
12055
12056 // Inject unprocessed CR from previous read into the buffer.
12057 if (pf->cr)
12058 *buffer = '\r';
12059 retry:
12060 nr = nonblock_immune_read(pf->pf_fd, buffer + pf->cr, count - pf->cr);
12061 if (nr < 0)
12062 return nr;
12063
12064 injected_cr = pf->cr;
12065 nr += pf->cr;
12066 pf->cr = 0;
12067
12068 if (nr > 0) {
12069 nr = remove_cr(buffer, nr);
12070 // remove_cr() won't reduce size to zero, so [nr - 1] is OK.
12071 if (buffer[nr - 1] == '\r') {
12072 if (nr > 1) {
12073 // Ignore trailing CR for now: we'll deal with it later.
12074 pf->cr = 1;
12075 --nr;
12076 } else if (injected_cr) { // nr == 1
12077 // Buffer only contains an injected CR. This means the
12078 // read returned EOF. Return the buffer as-is. The
12079 // next call will detect EOF.
12080 } else {
12081 // Buffer only contains a CR from the most recent read.
12082 // Try another read, treating the CR as injected. We'll
12083 // either get more characters or EOF. Either way we
12084 // won't end up here again.
12085 pf->cr = 1;
12086 goto retry;
12087 }
12088 }
12089 }
12090 return nr;
12091}
12092#endif
12093
10834static int 12094static int
10835preadfd(void) 12095preadfd(void)
10836{ 12096{
@@ -10841,7 +12101,11 @@ preadfd(void)
10841#if ENABLE_FEATURE_EDITING 12101#if ENABLE_FEATURE_EDITING
10842 /* retry: */ 12102 /* retry: */
10843 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) 12103 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
12104#if ENABLE_PLATFORM_MINGW32
12105 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
12106#else
10844 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 12107 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
12108#endif
10845 else { 12109 else {
10846# if ENABLE_ASH_IDLE_TIMEOUT 12110# if ENABLE_ASH_IDLE_TIMEOUT
10847 int timeout = -1; 12111 int timeout = -1;
@@ -10880,12 +12144,21 @@ preadfd(void)
10880 INT_ON; /* here non-blocked SIGINT will longjmp */ 12144 INT_ON; /* here non-blocked SIGINT will longjmp */
10881 if (nr == 0) { 12145 if (nr == 0) {
10882 /* ^C pressed, "convert" to SIGINT */ 12146 /* ^C pressed, "convert" to SIGINT */
12147# if !ENABLE_PLATFORM_MINGW32
10883 write(STDOUT_FILENO, "^C\n", 3); 12148 write(STDOUT_FILENO, "^C\n", 3);
10884 raise(SIGINT); /* here non-blocked SIGINT will longjmp */ 12149 raise(SIGINT); /* here non-blocked SIGINT will longjmp */
10885 /* raise(SIGINT) did not work! (e.g. if SIGINT 12150 /* raise(SIGINT) did not work! (e.g. if SIGINT
10886 * is SIG_IGNed on startup, it stays SIG_IGNed) 12151 * is SIG_IGNed on startup, it stays SIG_IGNed)
10887 */ 12152 */
12153# else
12154 raise_interrupt();
12155 write(STDOUT_FILENO, "^C\n", 3);
12156# endif
10888 if (trap[SIGINT]) { 12157 if (trap[SIGINT]) {
12158# if ENABLE_PLATFORM_MINGW32
12159 pending_int = 1;
12160 dotrap();
12161# endif
10889 empty_line_input: 12162 empty_line_input:
10890 buf[0] = '\n'; 12163 buf[0] = '\n';
10891 buf[1] = '\0'; 12164 buf[1] = '\0';
@@ -10914,7 +12187,11 @@ preadfd(void)
10914 } 12187 }
10915 } 12188 }
10916#else 12189#else
12190# if ENABLE_PLATFORM_MINGW32
12191 nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1);
12192# else
10917 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); 12193 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
12194# endif
10918#endif 12195#endif
10919 12196
10920#if 0 /* disabled: nonblock_immune_read() handles this problem */ 12197#if 0 /* disabled: nonblock_immune_read() handles this problem */
@@ -11213,6 +12490,7 @@ popallfiles(void)
11213 unwindfiles(&basepf); 12490 unwindfiles(&basepf);
11214} 12491}
11215 12492
12493#if !ENABLE_PLATFORM_MINGW32
11216/* 12494/*
11217 * Close the file(s) that the shell is reading commands from. Called 12495 * Close the file(s) that the shell is reading commands from. Called
11218 * after a fork is done. 12496 * after a fork is done.
@@ -11226,6 +12504,7 @@ closescript(void)
11226 g_parsefile->pf_fd = 0; 12504 g_parsefile->pf_fd = 0;
11227 } 12505 }
11228} 12506}
12507#endif
11229 12508
11230/* 12509/*
11231 * Like setinputfile, but takes an open file descriptor. Call this with 12510 * Like setinputfile, but takes an open file descriptor. Call this with
@@ -11462,8 +12741,19 @@ options(int *login_sh)
11462 int val; 12741 int val;
11463 int c; 12742 int c;
11464 12743
11465 if (login_sh != NULL) /* if we came from startup code */ 12744#if ENABLE_ASH_NOCONSOLE
12745 noconsole = console_state();
12746#endif
12747 if (login_sh != NULL) { /* if we came from startup code */
11466 minusc = NULL; 12748 minusc = NULL;
12749#if ENABLE_PLATFORM_MINGW32
12750 dirarg = NULL;
12751 title = NULL;
12752# if ENABLE_SUW32
12753 delayexit = 0;
12754# endif
12755#endif
12756 }
11467 while ((p = *argptr) != NULL) { 12757 while ((p = *argptr) != NULL) {
11468 c = *p++; 12758 c = *p++;
11469 if (c != '-' && c != '+') 12759 if (c != '-' && c != '+')
@@ -11493,6 +12783,31 @@ options(int *login_sh)
11493 cflag = 1; 12783 cflag = 1;
11494 continue; 12784 continue;
11495 } 12785 }
12786#if ENABLE_PLATFORM_MINGW32
12787 /* Undocumented flags;
12788 * -d force current directory
12789 * -t title to display in console window
12790 * -N prompt user before exit
12791 * Must appear before -s or -c. */
12792 if (c == 'd' && val == 1) {
12793 if (*argptr == NULL)
12794 ash_msg_and_raise_error(bb_msg_requires_arg, "-d");
12795 dirarg = *argptr++;
12796 continue;
12797 }
12798 if (c == 't' && val == 1) {
12799 if (*argptr == NULL)
12800 ash_msg_and_raise_error(bb_msg_requires_arg, "-t");
12801 title = *argptr++;
12802 continue;
12803 }
12804# if ENABLE_SUW32
12805 if (c == 'N' && val == 1) {
12806 delayexit = 1;
12807 continue;
12808 }
12809# endif
12810#endif
11496 if (c == 's') { /* -s, +s */ 12811 if (c == 's') { /* -s, +s */
11497 sflag = 1; 12812 sflag = 1;
11498 continue; 12813 continue;
@@ -13448,7 +14763,6 @@ parseheredoc(void)
13448 } 14763 }
13449} 14764}
13450 14765
13451
13452static const char * 14766static const char *
13453expandstr(const char *ps, int syntax_type) 14767expandstr(const char *ps, int syntax_type)
13454{ 14768{
@@ -13528,6 +14842,9 @@ evalstring(char *s, int flags)
13528 int status; 14842 int status;
13529 14843
13530 s = sstrdup(s); 14844 s = sstrdup(s);
14845#if ENABLE_PLATFORM_MINGW32
14846 remove_cr(s, strlen(s)+1);
14847#endif
13531 setinputstring(s); 14848 setinputstring(s);
13532 setstackmark(&smark); 14849 setstackmark(&smark);
13533 14850
@@ -13615,7 +14932,7 @@ cmdloop(int top)
13615 int skip; 14932 int skip;
13616 14933
13617 setstackmark(&smark); 14934 setstackmark(&smark);
13618#if JOBS 14935#if JOBS || JOBS_WIN32
13619 if (doing_jobctl) 14936 if (doing_jobctl)
13620 showjobs(SHOW_CHANGED|SHOW_STDERR); 14937 showjobs(SHOW_CHANGED|SHOW_STDERR);
13621#endif 14938#endif
@@ -13623,6 +14940,9 @@ cmdloop(int top)
13623 if (iflag && top) { 14940 if (iflag && top) {
13624 inter++; 14941 inter++;
13625 chkmail(); 14942 chkmail();
14943#if ENABLE_PLATFORM_MINGW32
14944 terminal_mode(TRUE);
14945#endif
13626 } 14946 }
13627 n = parsecmd(inter); 14947 n = parsecmd(inter);
13628#if DEBUG 14948#if DEBUG
@@ -13646,8 +14966,10 @@ cmdloop(int top)
13646 } else { 14966 } else {
13647 int i; 14967 int i;
13648 14968
14969#if !ENABLE_PLATFORM_MINGW32
13649 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ 14970 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13650 job_warning >>= 1; 14971 job_warning >>= 1;
14972#endif
13651 numeof = 0; 14973 numeof = 0;
13652 i = evaltree(n, 0); 14974 i = evaltree(n, 0);
13653 if (n) 14975 if (n)
@@ -13677,7 +14999,7 @@ find_dot_file(char *basename)
13677 int len; 14999 int len;
13678 15000
13679 /* don't try this for absolute or relative paths */ 15001 /* don't try this for absolute or relative paths */
13680 if (strchr(basename, '/')) 15002 if (strchr(basename, '/') IF_PLATFORM_MINGW32(|| strchr(basename, '\\')))
13681 return basename; 15003 return basename;
13682 15004
13683 path = pathval(); 15005 path = pathval();
@@ -13842,6 +15164,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13842 struct builtincmd *bcmd; 15164 struct builtincmd *bcmd;
13843 int len; 15165 int len;
13844 15166
15167#if !ENABLE_PLATFORM_MINGW32
13845 /* If name contains a slash, don't use PATH or hash table */ 15168 /* If name contains a slash, don't use PATH or hash table */
13846 if (strchr(name, '/') != NULL) { 15169 if (strchr(name, '/') != NULL) {
13847 entry->u.index = -1; 15170 entry->u.index = -1;
@@ -13861,6 +15184,35 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13861 entry->cmdtype = CMDNORMAL; 15184 entry->cmdtype = CMDNORMAL;
13862 return; 15185 return;
13863 } 15186 }
15187#else /* ENABLE_PLATFORM_MINGW32 */
15188 /* If name contains a slash or drive prefix, don't use PATH or hash table */
15189 if (has_path(name)) {
15190 entry->u.index = -1;
15191 entry->cmdtype = CMDNORMAL;
15192 fullname = stack_add_ext_space(name);
15193 if (add_win32_extension(fullname)) {
15194 return;
15195 } else if (unix_path(name)) {
15196 name = (char *)bb_basename(name);
15197 if (
15198# if ENABLE_FEATURE_SH_STANDALONE
15199 find_applet_by_name_for_sh(name, path) >= 0 ||
15200# endif
15201 !find_builtin(bb_basename(name))
15202 ) {
15203 act |= DO_NOFUNC;
15204 } else if (act & DO_ABS) {
15205 entry->cmdtype = CMDUNKNOWN;
15206 return;
15207 }
15208 } else if (act & DO_ABS) {
15209 entry->cmdtype = CMDUNKNOWN;
15210 return;
15211 } else {
15212 return;
15213 }
15214 }
15215#endif /* ENABLE_PLATFORM_MINGW32 */
13864 15216
13865/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ 15217/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
13866 15218
@@ -13915,7 +15267,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13915 15267
13916#if ENABLE_FEATURE_SH_STANDALONE 15268#if ENABLE_FEATURE_SH_STANDALONE
13917 { 15269 {
13918 int applet_no = find_applet_by_name(name); 15270 int applet_no = find_applet_by_name_for_sh(name, path);
13919 if (applet_no >= 0) { 15271 if (applet_no >= 0) {
13920 entry->cmdtype = CMDNORMAL; 15272 entry->cmdtype = CMDNORMAL;
13921 entry->u.index = -2 - applet_no; 15273 entry->u.index = -2 - applet_no;
@@ -13954,12 +15306,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13954 } 15306 }
13955 } 15307 }
13956 /* if rehash, don't redo absolute path names */ 15308 /* if rehash, don't redo absolute path names */
13957 if (fullname[0] == '/' && idx <= prev) { 15309 if (!is_relative_path(fullname) && idx <= prev) {
13958 if (idx < prev) 15310 if (idx < prev)
13959 continue; 15311 continue;
13960 TRACE(("searchexec \"%s\": no change\n", name)); 15312 TRACE(("searchexec \"%s\": no change\n", name));
13961 goto success; 15313 goto success;
13962 } 15314 }
15315#if ENABLE_PLATFORM_MINGW32
15316 add_win32_extension(fullname);
15317#endif
13963 while (stat(fullname, &statb) < 0) { 15318 while (stat(fullname, &statb) < 0) {
13964#ifdef SYSV 15319#ifdef SYSV
13965 if (errno == EINTR) 15320 if (errno == EINTR)
@@ -14037,7 +15392,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
14037 entry->u = cmdp->param; 15392 entry->u = cmdp->param;
14038} 15393}
14039 15394
14040
14041/* 15395/*
14042 * The trap builtin. 15396 * The trap builtin.
14043 */ 15397 */
@@ -14098,19 +15452,45 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14098 if (LONE_DASH(action)) 15452 if (LONE_DASH(action))
14099 action = NULL; 15453 action = NULL;
14100 else { 15454 else {
15455#if !ENABLE_PLATFORM_MINGW32
14101 if (action[0]) /* not NULL and not "" and not "-" */ 15456 if (action[0]) /* not NULL and not "" and not "-" */
14102 may_have_traps = 1; 15457 may_have_traps = 1;
15458#endif
14103 action = ckstrdup(action); 15459 action = ckstrdup(action);
14104 } 15460 }
14105 } 15461 }
14106 free(trap[signo]); 15462 free(trap[signo]);
14107 trap[signo] = action; 15463 trap[signo] = action;
15464#if ENABLE_PLATFORM_MINGW32
15465 if (signo == SIGINT) {
15466 // trap '' INT disables Ctrl-C, anything else enables it
15467 if (action && action[0] == '\0') {
15468 SetConsoleCtrlHandler(NULL, TRUE);
15469# if ENABLE_FEATURE_EDITING
15470 if (line_input_state) {
15471 line_input_state->flags |= IGNORE_CTRL_C;
15472 }
15473# endif
15474 } else {
15475 SetConsoleCtrlHandler(NULL, FALSE);
15476# if ENABLE_FEATURE_EDITING
15477 if (line_input_state) {
15478 line_input_state->flags &= ~IGNORE_CTRL_C;
15479 }
15480# endif
15481 }
15482 }
15483#else
14108 if (signo != 0 && signo < NSIG) 15484 if (signo != 0 && signo < NSIG)
14109 setsignal(signo); 15485 setsignal(signo);
15486#endif
14110 INT_ON; 15487 INT_ON;
14111 next: 15488 next:
14112 ap++; 15489 ap++;
14113 } 15490 }
15491#if ENABLE_PLATFORM_MINGW32
15492 may_have_traps = trap[SIGINT] && trap[SIGINT][0] != '\0';
15493#endif
14114 return exitcode; 15494 return exitcode;
14115} 15495}
14116 15496
@@ -14139,10 +15519,12 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14139 { 15519 {
14140 const char *a = applet_names; 15520 const char *a = applet_names;
14141 while (*a) { 15521 while (*a) {
14142 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a); 15522 if (prefer_applet(a, pathval())) {
14143 if (col > 60) { 15523 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
14144 out1fmt("\n"); 15524 if (col > 60) {
14145 col = 0; 15525 out1fmt("\n");
15526 col = 0;
15527 }
14146 } 15528 }
14147 while (*a++ != '\0') 15529 while (*a++ != '\0')
14148 continue; 15530 continue;
@@ -14204,7 +15586,24 @@ exportcmd(int argc UNUSED_PARAM, char **argv)
14204 } else { 15586 } else {
14205 vp = *findvar(name); 15587 vp = *findvar(name);
14206 if (vp) { 15588 if (vp) {
15589#if ENABLE_PLATFORM_MINGW32
15590 if (is_bb_var(name) == BB_VAR_EXACT) {
15591 if (flag_off == ~VEXPORT)
15592 unsetenv(name);
15593 else if (flag == VEXPORT && !(vp->flags & VUNSET))
15594 putenv(vp->var_text);
15595 }
15596#endif
14207 vp->flags = ((vp->flags | flag) & flag_off); 15597 vp->flags = ((vp->flags | flag) & flag_off);
15598#if ENABLE_PLATFORM_MINGW32
15599 /* Unexporting a variable imported from the
15600 * environment restores its original value and
15601 * removes the VIMPORT flag. */
15602 if ((vp->flags & VIMPORT) && (flag_off == ~VEXPORT)) {
15603 vp->flags &= ~VIMPORT;
15604 p = getenv(name);
15605 } else
15606#endif
14208 continue; 15607 continue;
14209 } 15608 }
14210 } 15609 }
@@ -14295,6 +15694,21 @@ timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14295 return 0; 15694 return 0;
14296} 15695}
14297 15696
15697#if ENABLE_PLATFORM_MINGW32
15698static int FAST_FUNC
15699titlecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15700{
15701 if (*argptr == NULL) {
15702 char buffer[256];
15703 if (get_title(buffer, sizeof(buffer)))
15704 puts(buffer);
15705 } else {
15706 set_title(*argptr);
15707 }
15708 return 0;
15709}
15710#endif
15711
14298#if ENABLE_FEATURE_SH_MATH 15712#if ENABLE_FEATURE_SH_MATH
14299/* 15713/*
14300 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell. 15714 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
@@ -14387,6 +15801,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14387 r = shell_builtin_read(&params); 15801 r = shell_builtin_read(&params);
14388 INT_ON; 15802 INT_ON;
14389 15803
15804#if !ENABLE_PLATFORM_MINGW32
14390 if ((uintptr_t)r == 1 && errno == EINTR) { 15805 if ((uintptr_t)r == 1 && errno == EINTR) {
14391 /* To get SIGCHLD: sleep 1 & read x; echo $x 15806 /* To get SIGCHLD: sleep 1 & read x; echo $x
14392 * Correct behavior is to not exit "read" 15807 * Correct behavior is to not exit "read"
@@ -14395,6 +15810,34 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14395 goto again; 15810 goto again;
14396 } 15811 }
14397 15812
15813 if ((uintptr_t)r == 2) /* -t SEC timeout? */
15814 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
15815 /* The actual value observed with bash 5.2.15: */
15816 return 128 + SIGALRM;
15817#else /* ENABLE_PLATFORM_MINGW32 */
15818 if ((uintptr_t)r == 2) {
15819 /* Timeout, return 128 + SIGALRM */
15820 return 142;
15821 } else if ((uintptr_t)r == 3) {
15822 /* ^C pressed, propagate event */
15823 if (trap[SIGINT]) {
15824 write(STDOUT_FILENO, "^C", 2);
15825 pending_int = 1;
15826 dotrap();
15827 if (!(rootshell && iflag))
15828 return (uintptr_t)0;
15829 else
15830 goto again;
15831 } else if (iflag) {
15832 raise_interrupt();
15833 } else {
15834 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
15835 exitshell();
15836 }
15837 return (uintptr_t)r;
15838 }
15839#endif
15840
14398 if ((uintptr_t)r > 1) 15841 if ((uintptr_t)r > 1)
14399 ash_msg_and_raise_error(r); 15842 ash_msg_and_raise_error(r);
14400 15843
@@ -14455,6 +15898,9 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14455 if (!isdigit(modestr[0])) 15898 if (!isdigit(modestr[0]))
14456 mask ^= 0777; 15899 mask ^= 0777;
14457 umask(mask); 15900 umask(mask);
15901#if ENABLE_PLATFORM_MINGW32
15902 setvareq(xasprintf("BB_UMASK=0%o", mask), VEXPORT|VNOSAVE);
15903#endif
14458 } 15904 }
14459 return 0; 15905 return 0;
14460} 15906}
@@ -14520,8 +15966,25 @@ exitshell(void)
14520 char *p; 15966 char *p;
14521 15967
14522#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 15968#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
14523 save_history(line_input_state); /* may be NULL */ 15969 if (line_input_state) {
15970 const char *hp;
15971# if ENABLE_FEATURE_SH_HISTFILESIZE
15972// in bash:
15973// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
15974// "When this variable is assigned a value, the history file is truncated, if necessary"
15975// but we do it only at exit, not on assignment:
15976 /* Use HISTFILESIZE to limit file size */
15977 hp = lookupvar("HISTFILESIZE");
15978 if (hp)
15979 line_input_state->max_history = size_from_HISTFILESIZE(hp);
15980# endif
15981 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
15982 hp = lookupvar("HISTFILE");
15983 line_input_state->hist_file = hp;
15984 save_history(line_input_state); /* no-op if hist_file is NULL or "" */
15985 }
14524#endif 15986#endif
15987
14525 savestatus = exitstatus; 15988 savestatus = exitstatus;
14526 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); 15989 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
14527 if (setjmp(loc.loc)) 15990 if (setjmp(loc.loc))
@@ -14538,6 +16001,13 @@ exitshell(void)
14538 /*free(p); - we'll exit soon */ 16001 /*free(p); - we'll exit soon */
14539 } 16002 }
14540 out: 16003 out:
16004#if ENABLE_SUW32
16005 if (delayexit) {
16006#define EXIT_MSG "Press any key to exit..."
16007 console_write(EXIT_MSG, sizeof(EXIT_MSG) - 1);
16008 _getch();
16009 }
16010#endif
14541 exitreset(); 16011 exitreset();
14542 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}". 16012 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14543 * our setjobctl(0) does not panic if tcsetpgrp fails inside it. 16013 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
@@ -14548,22 +16018,97 @@ exitshell(void)
14548 /* NOTREACHED */ 16018 /* NOTREACHED */
14549} 16019}
14550 16020
16021#if ENABLE_PLATFORM_MINGW32
16022/* We need to see if HOME is *really* unset */
16023# undef getenv
16024static void setvar_if_unset(const char *key, const char *value)
16025{
16026 if (!getenv(key) || getuid() == 0)
16027 setvar(key, value, VEXPORT);
16028}
16029#endif
16030
14551/* Don't inline: conserve stack of caller from having our locals too */ 16031/* Don't inline: conserve stack of caller from having our locals too */
14552static NOINLINE void 16032static NOINLINE void
14553init(void) 16033init(void)
14554{ 16034{
16035#if ENABLE_PLATFORM_MINGW32
16036 int import = 0;
16037#else
14555 /* we will never free this */ 16038 /* we will never free this */
14556 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ); 16039 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ);
14557 basepf.linno = 1; 16040 basepf.linno = 1;
14558 16041
14559 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 16042 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
14560 setsignal(SIGCHLD); 16043 setsignal(SIGCHLD);
16044#endif
14561 16045
14562 { 16046 {
14563 char **envp; 16047 char **envp;
14564 const char *p; 16048 const char *p;
14565 16049
14566 initvar(); 16050 initvar();
16051
16052#if ENABLE_PLATFORM_MINGW32
16053 /*
16054 * case insensitive env names from Windows world
16055 *
16056 * Some standard env names such as PATH is named Path and so on
16057 * ash itself is case sensitive, so "Path" will confuse it, as
16058 * MSVC getenv() is case insensitive.
16059 *
16060 * We may end up having both Path and PATH. Then Path will be chosen
16061 * because it appears first.
16062 */
16063 if (windows_env()) {
16064 /*
16065 * If we get here it's because the environment suggests we
16066 * haven't been invoked from an earlier instance of BusyBox.
16067 */
16068 char *start, *end;
16069 struct passwd *pw;
16070
16071 /* mintty sets HOME: unset it */
16072 const char *tty = getenv("TERM_PROGRAM");
16073 if (tty && strcmp(tty, "mintty") == 0) {
16074 unsetenv("HOME");
16075 }
16076
16077 import = VIMPORT;
16078 for (envp = environ; envp && *envp; envp++) {
16079 if (!(end=strchr(*envp, '=')))
16080 continue;
16081
16082 /* check for invalid characters in name */
16083 start = (char *)endofname(*envp);
16084 if (*start != '=') {
16085 /* Make a copy of the original variable */
16086 setvareq(xstrdup(*envp), VEXPORT|VNOSAVE);
16087
16088 /* Replace invalid characters with underscores */
16089 for (; start < end; start++) {
16090 if (!isalnum(*start)) {
16091 *start = '_';
16092 }
16093 }
16094 }
16095
16096 /* make all variable names uppercase */
16097 for (start = *envp;start < end;start++)
16098 *start = toupper(*start);
16099 }
16100
16101 /* Initialise some variables normally set at login, but
16102 * only if someone hasn't already set them or we're root. */
16103 pw = getpwuid(getuid());
16104 if (pw) {
16105 setvar_if_unset("USER", pw->pw_name);
16106 setvar_if_unset("LOGNAME", pw->pw_name);
16107 setvar_if_unset("HOME", pw->pw_dir);
16108 }
16109 setvar_if_unset("SHELL", DEFAULT_SHELL);
16110 }
16111#endif
14567 for (envp = environ; envp && *envp; envp++) { 16112 for (envp = environ; envp && *envp; envp++) {
14568/* Used to have 16113/* Used to have
14569 * p = endofname(*envp); 16114 * p = endofname(*envp);
@@ -14577,7 +16122,11 @@ init(void)
14577 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this 16122 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14578 */ 16123 */
14579 if (strchr(*envp, '=')) { 16124 if (strchr(*envp, '=')) {
16125#if !ENABLE_PLATFORM_MINGW32
14580 setvareq(*envp, VEXPORT|VTEXTFIXED); 16126 setvareq(*envp, VEXPORT|VTEXTFIXED);
16127#else
16128 setvareq(*envp, VEXPORT|import);
16129#endif
14581 } 16130 }
14582 } 16131 }
14583 16132
@@ -14609,7 +16158,6 @@ init(void)
14609 } 16158 }
14610} 16159}
14611 16160
14612
14613//usage:#define ash_trivial_usage 16161//usage:#define ash_trivial_usage
14614//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]" 16162//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
14615//////// comes from ^^^^^^^^^^optletters 16163//////// comes from ^^^^^^^^^^optletters
@@ -14628,7 +16176,11 @@ procargs(char **argv)
14628 int login_sh; 16176 int login_sh;
14629 16177
14630 xargv = argv; 16178 xargv = argv;
16179#if ENABLE_PLATFORM_MINGW32
16180 login_sh = applet_name[0] == 'l';
16181#else
14631 login_sh = xargv[0] && xargv[0][0] == '-'; 16182 login_sh = xargv[0] && xargv[0][0] == '-';
16183#endif
14632#if NUM_SCRIPTS > 0 16184#if NUM_SCRIPTS > 0
14633 if (minusc) 16185 if (minusc)
14634 goto setarg0; 16186 goto setarg0;
@@ -14652,7 +16204,9 @@ procargs(char **argv)
14652 } 16204 }
14653 if (iflag == 2 /* no explicit -i given */ 16205 if (iflag == 2 /* no explicit -i given */
14654 && sflag == 1 /* -s given (or implied) */ 16206 && sflag == 1 /* -s given (or implied) */
16207#if !ENABLE_PLATFORM_MINGW32
14655 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */ 16208 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
16209#endif
14656 && isatty(0) && isatty(1) /* we are on tty */ 16210 && isatty(0) && isatty(1) /* we are on tty */
14657 ) { 16211 ) {
14658 iflag = 1; 16212 iflag = 1;
@@ -14672,6 +16226,9 @@ procargs(char **argv)
14672 goto setarg0; 16226 goto setarg0;
14673 } else if (!sflag) { 16227 } else if (!sflag) {
14674 setinputfile(*xargv, 0); 16228 setinputfile(*xargv, 0);
16229#if ENABLE_PLATFORM_MINGW32
16230 bs_to_slash(*xargv);
16231#endif
14675 setarg0: 16232 setarg0:
14676 arg0 = *xargv++; 16233 arg0 = *xargv++;
14677 commandname = arg0; 16234 commandname = arg0;
@@ -14695,8 +16252,10 @@ procargs(char **argv)
14695 * NB: must do it before setting up signals (in optschanged()) 16252 * NB: must do it before setting up signals (in optschanged())
14696 * and reading .profile etc (after we return from here): 16253 * and reading .profile etc (after we return from here):
14697 */ 16254 */
16255#if !ENABLE_PLATFORM_MINGW32
14698 if (iflag) 16256 if (iflag)
14699 signal(SIGHUP, SIG_DFL); 16257 signal(SIGHUP, SIG_DFL);
16258#endif
14700 16259
14701 optschanged(); 16260 optschanged();
14702 16261
@@ -14741,9 +16300,25 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14741 struct stackmark smark; 16300 struct stackmark smark;
14742 int login_sh; 16301 int login_sh;
14743 16302
16303#if ENABLE_PLATFORM_MINGW32
16304 INIT_G_memstack();
16305
16306 /* from init() */
16307 basepf.next_to_pgetc = basepf.buf = ckzalloc(IBUFSIZ);
16308 basepf.linno = 1;
16309
16310 if (argc == 3 && !strcmp(argv[1], "--fs")) {
16311 forkshell_init(argv[2]);
16312 /* only reached in case of error */
16313 bb_error_msg_and_die("forkshell failed");
16314 }
16315#endif
16316
14744 /* Initialize global data */ 16317 /* Initialize global data */
14745 INIT_G_misc(); 16318 INIT_G_misc();
16319#if !ENABLE_PLATFORM_MINGW32
14746 INIT_G_memstack(); 16320 INIT_G_memstack();
16321#endif
14747 INIT_G_var(); 16322 INIT_G_var();
14748#if ENABLE_ASH_ALIAS 16323#if ENABLE_ASH_ALIAS
14749 INIT_G_alias(); 16324 INIT_G_alias();
@@ -14789,6 +16364,10 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14789 init(); 16364 init();
14790 setstackmark(&smark); 16365 setstackmark(&smark);
14791 16366
16367#if ENABLE_PLATFORM_MINGW32
16368 SetConsoleCtrlHandler(ctrl_handler, TRUE);
16369#endif
16370
14792#if NUM_SCRIPTS > 0 16371#if NUM_SCRIPTS > 0
14793 if (argc < 0) 16372 if (argc < 0)
14794 /* Non-NULL minusc tells procargs that an embedded script is being run */ 16373 /* Non-NULL minusc tells procargs that an embedded script is being run */
@@ -14800,11 +16379,45 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14800 trace_puts_args(argv); 16379 trace_puts_args(argv);
14801#endif 16380#endif
14802 16381
16382#if ENABLE_PLATFORM_MINGW32
16383 if (!dirarg && !login_sh && iflag) {
16384 char *cwd = getcwd(NULL, 0);
16385 if (cwd) {
16386 chdir(cwd);
16387 setpwd(NULL, 0);
16388 free(cwd);
16389 }
16390 }
16391
16392 if (title)
16393 set_title(title);
16394#endif
16395
14803 if (login_sh) { 16396 if (login_sh) {
14804 const char *hp; 16397 const char *hp;
14805 16398
16399#if ENABLE_PLATFORM_MINGW32
16400 if (!dirarg) {
16401 hp = lookupvar("HOME");
16402 if (hp == NULL || *hp == '\0')
16403 hp = xgetpwuid(getuid())->pw_dir;
16404 chdir(hp);
16405 setpwd(NULL, 0);
16406 }
16407#endif
16408
14806 state = 1; 16409 state = 1;
16410#if ENABLE_PLATFORM_MINGW32
16411 hp = concat_path_file(get_system_drive(), "/etc/profile");
16412 read_profile(hp);
16413 free((void *)hp);
16414
16415 hp = exe_relative_path("/etc/profile");
16416 read_profile(hp);
16417 free((void *)hp);
16418#else
14807 read_profile("/etc/profile"); 16419 read_profile("/etc/profile");
16420#endif
14808 state1: 16421 state1:
14809 state = 2; 16422 state = 2;
14810 hp = lookupvar("HOME"); 16423 hp = lookupvar("HOME");
@@ -14813,10 +16426,18 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14813 } 16426 }
14814 state2: 16427 state2:
14815 state = 3; 16428 state = 3;
16429#if ENABLE_PLATFORM_MINGW32
16430 if (dirarg) {
16431 chdir(dirarg);
16432 setpwd(NULL, 0);
16433 }
16434#endif
14816 if (iflag 16435 if (iflag
16436#if ENABLE_PLATFORM_POSIX
14817#ifndef linux 16437#ifndef linux
14818 && getuid() == geteuid() && getgid() == getegid() 16438 && getuid() == geteuid() && getgid() == getegid()
14819#endif 16439#endif
16440#endif
14820 ) { 16441 ) {
14821 const char *shinit = lookupvar("ENV"); 16442 const char *shinit = lookupvar("ENV");
14822 if (shinit != NULL && *shinit != '\0') 16443 if (shinit != NULL && *shinit != '\0')
@@ -14841,7 +16462,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14841 // ash -sc 'echo $-' 16462 // ash -sc 'echo $-'
14842 // continue reading input from stdin after running 'echo'. 16463 // continue reading input from stdin after running 'echo'.
14843 // bash does not do this: it prints "hBcs" and exits. 16464 // bash does not do this: it prints "hBcs" and exits.
16465#if !ENABLE_PLATFORM_MINGW32
14844 evalstring(minusc, EV_EXIT); 16466 evalstring(minusc, EV_EXIT);
16467#else
16468 evalstring(minusc, sflag ? 0 : EV_EXIT);
16469#endif
14845 } 16470 }
14846 16471
14847 if (sflag || minusc == NULL) { 16472 if (sflag || minusc == NULL) {
@@ -14862,7 +16487,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14862 if (hp) 16487 if (hp)
14863 line_input_state->hist_file = xstrdup(hp); 16488 line_input_state->hist_file = xstrdup(hp);
14864# if ENABLE_FEATURE_SH_HISTFILESIZE 16489# if ENABLE_FEATURE_SH_HISTFILESIZE
14865 hp = lookupvar("HISTFILESIZE"); 16490 hp = lookupvar("HISTSIZE");
16491 /* Using HISTFILESIZE above to limit max_history would be WRONG:
16492 * users may set HISTFILESIZE=0 in their profile scripts
16493 * to prevent _saving_ of history files, but still want to have
16494 * non-zero history limit for in-memory list.
16495 */
14866 line_input_state->max_history = size_from_HISTFILESIZE(hp); 16496 line_input_state->max_history = size_from_HISTFILESIZE(hp);
14867# endif 16497# endif
14868 } 16498 }
@@ -14884,6 +16514,1101 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14884 /* NOTREACHED */ 16514 /* NOTREACHED */
14885} 16515}
14886 16516
16517#if ENABLE_PLATFORM_MINGW32
16518static void
16519forkshell_openhere(struct forkshell *fs)
16520{
16521 const char *p = fs->path;
16522 size_t len = strlen(p);
16523 int pip[2];
16524
16525 pip[0] = fs->fd[0];
16526 pip[1] = fs->fd[1];
16527
16528 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16529
16530 close(pip[0]);
16531 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
16532 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
16533 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
16534 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
16535 signal(SIGPIPE, SIG_DFL);
16536 xwrite(pip[1], p, len);
16537 _exit_SUCCESS();
16538}
16539
16540static void
16541forkshell_evalbackcmd(struct forkshell *fs)
16542{
16543#if BASH_PROCESS_SUBST
16544 /* determine end of pipe used by parent (ip) and child (ic) */
16545 const int ctl = fs->fd[2];
16546 const int ip = (ctl == CTLTOPROC);
16547 const int ic = !(ctl == CTLTOPROC);
16548#else
16549 const int ip = 0;
16550 const int ic = 1;
16551#endif
16552 union node *n = fs->n;
16553 int pip[2];
16554
16555 pip[ip] = fs->fd[ip];
16556 pip[ic] = fs->fd[ic];
16557
16558 FORCE_INT_ON;
16559 close(pip[ip]);
16560 if (pip[ic] != ic) {
16561 /*close(ic);*/
16562 dup2_or_raise(pip[ic], ic);
16563 close(pip[ic]);
16564 }
16565 eflag = 0;
16566 ifsfree();
16567 evaltreenr(n, EV_EXIT);
16568 /* NOTREACHED */
16569}
16570
16571static void
16572forkshell_evalsubshell(struct forkshell *fs)
16573{
16574 union node *n = fs->n;
16575 int flags = fs->flags;
16576
16577 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16578 INT_ON;
16579 flags |= EV_EXIT;
16580 if (fs->mode)
16581 flags &= ~EV_TESTED;
16582 expredir(n->nredir.redirect);
16583 redirect(n->nredir.redirect, 0);
16584 evaltreenr(n->nredir.n, flags);
16585 /* never returns */
16586}
16587
16588static void
16589forkshell_evalpipe(struct forkshell *fs)
16590{
16591 union node *n = fs->n;
16592 int flags = fs->flags;
16593 int prevfd = fs->fd[2];
16594 int pip[2] = {fs->fd[0], fs->fd[1]};
16595
16596 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
16597 INT_ON;
16598 if (pip[1] >= 0) {
16599 close(pip[0]);
16600 }
16601 if (prevfd > 0) {
16602 dup2(prevfd, 0);
16603 close(prevfd);
16604 }
16605 if (pip[1] > 1) {
16606 dup2(pip[1], 1);
16607 close(pip[1]);
16608 }
16609 evaltreenr(n, flags);
16610}
16611
16612static void
16613forkshell_shellexec(struct forkshell *fs)
16614{
16615 int idx = fs->fd[0];
16616 char **argv = fs->argv;
16617 char *path = fs->path;
16618
16619 FORCE_INT_ON;
16620 shellexec(argv[0], argv, path, idx, TRUE);
16621}
16622
16623static void
16624forkshell_child(struct forkshell *fs)
16625{
16626 switch ( fs->fpid ) {
16627 case FS_OPENHERE:
16628 forkshell_openhere(fs);
16629 break;
16630 case FS_EVALBACKCMD:
16631 forkshell_evalbackcmd(fs);
16632 break;
16633 case FS_EVALSUBSHELL:
16634 forkshell_evalsubshell(fs);
16635 break;
16636 case FS_EVALPIPE:
16637 forkshell_evalpipe(fs);
16638 break;
16639 case FS_SHELLEXEC:
16640 forkshell_shellexec(fs);
16641 break;
16642 }
16643}
16644
16645/*
16646 * Reinitialise the builtin environment variables in varinit. Their
16647 * current settings have been copied from the parent in vartab. Look
16648 * these up using the names from varinit_data, copy the details from
16649 * vartab to varinit and replace the old copy in vartab with the new
16650 * one in varinit.
16651 *
16652 * Also reinitialise the function pointers and line number variable.
16653 */
16654static void
16655reinitvar(void)
16656{
16657 int i;
16658 const char *name;
16659 struct var **old;
16660
16661 for (i=0; i<ARRAY_SIZE(varinit); ++i) {
16662 if (i == LINENO_INDEX)
16663 name = "LINENO=";
16664 else if (i == FUNCNAME_INDEX)
16665 name = "FUNCNAME=";
16666 else
16667 name = varinit_data[i].var_text;
16668 if ((old = findvar(name)) != NULL) {
16669 varinit[i] = **old;
16670 *old = varinit+i;
16671 }
16672 varinit[i].var_func = varinit_data[i].var_func;
16673 }
16674 vlineno.var_text = linenovar;
16675 vfuncname.var_text = funcnamevar;
16676}
16677
16678static void
16679spawn_forkshell(struct forkshell *fs, struct job *jp, union node *n, int mode)
16680{
16681 struct forkshell *new;
16682 char buf[32];
16683 const char *argv[] = { "sh", "--fs", NULL, NULL };
16684 intptr_t ret;
16685
16686 new = forkshell_prepare(fs);
16687 if (new == NULL)
16688 goto fail;
16689
16690 new->mode = mode;
16691 new->nprocs = jp == NULL ? 0 : jp->nprocs;
16692#if JOBS_WIN32
16693 new->jpnull = jp == NULL;
16694#endif
16695 sprintf(buf, "%p", new->hMapFile);
16696 argv[2] = buf;
16697 ret = spawnve(P_NOWAIT, bb_busybox_exec_path, (char *const *)argv, NULL);
16698 CloseHandle(new->hMapFile);
16699 UnmapViewOfFile(new);
16700 if (ret == -1) {
16701 fail:
16702 if (jp)
16703 freejob(jp);
16704 ash_msg_and_raise_error("unable to spawn shell");
16705 }
16706 forkparent(jp, n, mode, (HANDLE)ret);
16707}
16708
16709/*
16710 * forkshell_prepare() and friends
16711 *
16712 * The sequence is as follows:
16713 * - funcblocksize is initialized
16714 * - forkshell_size(fs) is called to calculate the exact memory needed
16715 * - a new struct is allocated
16716 * - funcblock, funcstring, relocate are initialized from the new block
16717 * - forkshell_copy(fs) is called to copy recursively everything over
16718 * it will record all relocations along the way
16719 *
16720 * When this memory is mapped elsewhere, pointer fixup will be needed
16721 */
16722
16723/* redefine without test that fs_size is nonzero */
16724#undef SAVE_PTR
16725#undef SAVE_PTR2
16726#undef SAVE_PTR3
16727#define SAVE_PTR(dst,note,flag) {MARK_PTR(dst,note,flag);}
16728
16729static int align_len(const char *s)
16730{
16731 return s ? SHELL_ALIGN(strlen(s)+1) : 0;
16732}
16733
16734struct datasize {
16735 int funcblocksize;
16736 int funcstringsize;
16737};
16738
16739#define SLIST_SIZE_BEGIN(name,type) \
16740static struct datasize \
16741name(struct datasize ds, type *p) \
16742{ \
16743 while (p) { \
16744 ds.funcblocksize += sizeof(type);
16745 /* do something here with p */
16746#define SLIST_SIZE_END() \
16747 p = p->next; \
16748 } \
16749 return ds; \
16750}
16751
16752#define SLIST_COPY_BEGIN(name,type) \
16753static type * \
16754name(type *vp) \
16755{ \
16756 type *start; \
16757 type **vpp; \
16758 vpp = &start; \
16759 while (vp) { \
16760 *vpp = funcblock; \
16761 funcblock = (char *) funcblock + sizeof(type);
16762 /* do something here with vpp and vp */
16763#define SLIST_COPY_END() \
16764 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE); \
16765 vp = vp->next; \
16766 vpp = &(*vpp)->next; \
16767 } \
16768 *vpp = NULL; \
16769 return start; \
16770}
16771
16772/*
16773 * struct var
16774 */
16775SLIST_SIZE_BEGIN(var_size,struct var)
16776ds.funcstringsize += align_len(p->var_text);
16777SLIST_SIZE_END()
16778
16779SLIST_COPY_BEGIN(var_copy,struct var)
16780(*vpp)->var_text = nodeckstrdup(vp->var_text);
16781(*vpp)->flags = vp->flags;
16782(*vpp)->var_func = NULL;
16783SAVE_PTR((*vpp)->var_text, xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"), FREE);
16784SLIST_COPY_END()
16785
16786/*
16787 * struct tblentry
16788 */
16789static struct datasize
16790tblentry_size(struct datasize ds, struct tblentry *tep)
16791{
16792 while (tep) {
16793 ds.funcblocksize += sizeof(struct tblentry) + align_len(tep->cmdname);
16794 /* CMDBUILTIN, e->param.cmd needs no pointer relocation */
16795 if (tep->cmdtype == CMDFUNCTION) {
16796 ds.funcblocksize += offsetof(struct funcnode, n);
16797 ds.funcblocksize = calcsize(ds.funcblocksize, &tep->param.func->n);
16798 }
16799 tep = tep->next;
16800 }
16801 return ds;
16802}
16803
16804static struct tblentry *
16805tblentry_copy(struct tblentry *tep)
16806{
16807 struct tblentry *start;
16808 struct tblentry **newp;
16809 int size;
16810
16811 newp = &start;
16812 while (tep) {
16813 *newp = funcblock;
16814 size = sizeof(struct tblentry) + align_len(tep->cmdname);
16815
16816 funcblock = (char *) funcblock + size;
16817 memcpy(*newp, tep, sizeof(struct tblentry)+strlen(tep->cmdname));
16818 switch (tep->cmdtype) {
16819 case CMDBUILTIN:
16820 /* Save index of builtin, not pointer; fixed by forkshell_init() */
16821 (*newp)->param.index = tep->param.cmd - builtintab;
16822 break;
16823 case CMDFUNCTION:
16824 (*newp)->param.func = funcblock;
16825 funcblock = (char *) funcblock + offsetof(struct funcnode, n);
16826 copynode(&tep->param.func->n);
16827 SAVE_PTR((*newp)->param.func, "param.func", NO_FREE);
16828 break;
16829 default:
16830 break;
16831 }
16832 SAVE_PTR((*newp)->next, xasprintf("cmdname '%s'", tep->cmdname), FREE);
16833 tep = tep->next;
16834 newp = &(*newp)->next;
16835 }
16836 *newp = NULL;
16837 return start;
16838}
16839
16840static struct datasize
16841cmdtable_size(struct datasize ds)
16842{
16843 int i;
16844 ds.funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE;
16845 for (i = 0; i < CMDTABLESIZE; i++)
16846 ds = tblentry_size(ds, cmdtable[i]);
16847 return ds;
16848}
16849
16850static struct tblentry **
16851cmdtable_copy(void)
16852{
16853 struct tblentry **new = funcblock;
16854 int i;
16855
16856 funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE;
16857 for (i = 0; i < CMDTABLESIZE; i++) {
16858 new[i] = tblentry_copy(cmdtable[i]);
16859 SAVE_PTR(new[i], xasprintf("cmdtable[%d]", i), FREE);
16860 }
16861 return new;
16862}
16863
16864#if ENABLE_ASH_ALIAS
16865/*
16866 * struct alias
16867 */
16868SLIST_SIZE_BEGIN(alias_size,struct alias)
16869ds.funcstringsize += align_len(p->name);
16870ds.funcstringsize += align_len(p->val);
16871SLIST_SIZE_END()
16872
16873SLIST_COPY_BEGIN(alias_copy,struct alias)
16874(*vpp)->name = nodeckstrdup(vp->name);
16875(*vpp)->val = nodeckstrdup(vp->val);
16876(*vpp)->flag = vp->flag;
16877SAVE_PTR((*vpp)->name, xasprintf("(*vpp)->name '%s'", vp->name ?: "NULL"), FREE);
16878SAVE_PTR((*vpp)->val, xasprintf("(*vpp)->val '%s'", vp->val ?: "NULL"), FREE);
16879SLIST_COPY_END()
16880
16881static struct datasize
16882atab_size(struct datasize ds)
16883{
16884 int i;
16885 ds.funcblocksize += sizeof(struct alias *)*ATABSIZE;
16886 for (i = 0; i < ATABSIZE; i++)
16887 ds = alias_size(ds, atab[i]);
16888 return ds;
16889}
16890
16891static struct alias **
16892atab_copy(void)
16893{
16894 struct alias **new = funcblock;
16895 int i;
16896
16897 funcblock = (char *) funcblock + sizeof(struct alias *)*ATABSIZE;
16898 for (i = 0; i < ATABSIZE; i++) {
16899 new[i] = alias_copy(atab[i]);
16900 SAVE_PTR(new[i], xasprintf("atab[%d]", i), FREE);
16901 }
16902 return new;
16903}
16904#endif
16905
16906/*
16907 * char **
16908 */
16909static struct datasize
16910argv_size(struct datasize ds, char **p)
16911{
16912 if (p) {
16913 while (*p) {
16914 ds.funcblocksize += sizeof(char *);
16915 ds.funcstringsize += align_len(*p);
16916 p++;
16917 }
16918 // Allow two extra elements for tryexec().
16919 ds.funcblocksize += 3 * sizeof(char *);
16920 }
16921 return ds;
16922}
16923
16924static char **
16925argv_copy(char **p)
16926{
16927 char **new, **start = funcblock;
16928#if FORKSHELL_DEBUG
16929 int i = 0;
16930#endif
16931
16932 if (p) {
16933 // Allow two extra elements for tryexec().
16934 funcblock = (char *) funcblock + 2 * sizeof(char *);
16935 while (*p) {
16936 new = funcblock;
16937 funcblock = (char *) funcblock + sizeof(char *);
16938 *new = nodeckstrdup(*p);
16939 SAVE_PTR(*new, xasprintf("argv[%d] '%s'", i++, *p), FREE);
16940 p++;
16941 }
16942 new = funcblock;
16943 funcblock = (char *) funcblock + sizeof(char *);
16944 *new = NULL;
16945 return start + 2;
16946 }
16947 return NULL;
16948}
16949
16950#if MAX_HISTORY
16951static struct datasize
16952history_size(struct datasize ds)
16953{
16954 int i;
16955 line_input_t *st = line_input_state;
16956
16957 ds.funcblocksize += sizeof(char *) * st->cnt_history;
16958 for (i = 0; i < st->cnt_history; i++) {
16959 ds.funcstringsize += align_len(st->history[i]);
16960 }
16961 return ds;
16962}
16963
16964static char **
16965history_copy(void)
16966{
16967 line_input_t *st = line_input_state;
16968 char **new = funcblock;
16969 int i;
16970
16971 funcblock = (char *)funcblock + sizeof(char *) * st->cnt_history;
16972 for (i = 0; i < st->cnt_history; i++) {
16973 new[i] = nodeckstrdup(st->history[i]);
16974 SAVE_PTR(new[i],
16975 xasprintf("history[%d] '%s'", i, st->history[i]), FREE);
16976 }
16977 return new;
16978}
16979#endif
16980
16981#if JOBS_WIN32
16982/*
16983 * struct procstat
16984 */
16985static struct datasize
16986procstat_size(struct datasize ds, int nj)
16987{
16988 struct job *jp = jobtab + nj;
16989
16990 if (jp->ps != &jp->ps0)
16991 ds.funcblocksize += sizeof(struct procstat) * jp->nprocs;
16992
16993 for (int i = 0; i < jp->nprocs; i++)
16994 ds.funcstringsize += align_len(jp->ps[i].ps_cmd);
16995
16996 return ds;
16997}
16998
16999static struct procstat *
17000procstat_copy(int nj)
17001{
17002 struct job *jp = jobtab + nj;
17003 struct procstat *new = funcblock;
17004
17005 funcblock = (char *)funcblock + sizeof(struct procstat) * jp->nprocs;
17006 memcpy(new, jp->ps, sizeof(struct procstat) * jp->nprocs);
17007
17008 for (int i = 0; i < jp->nprocs; i++) {
17009 new[i].ps_cmd = nodeckstrdup(jp->ps[i].ps_cmd);
17010 SAVE_PTR(new[i].ps_cmd,
17011 xasprintf("jobtab[%d].ps[%d].ps_cmd '%s'",
17012 nj, i, jp->ps[i].ps_cmd), FREE);
17013 }
17014 return new;
17015}
17016
17017/*
17018 * struct jobs
17019 */
17020static struct datasize
17021jobtab_size(struct datasize ds)
17022{
17023 ds.funcblocksize += sizeof(struct job) * njobs;
17024 for (int i = 0; i < njobs; i++) {
17025 if (jobtab[i].used)
17026 ds = procstat_size(ds, i);
17027 }
17028 return ds;
17029}
17030
17031static struct job *
17032jobtab_copy(void)
17033{
17034 struct job *new = funcblock;
17035 int i;
17036
17037 funcblock = (char *)funcblock + sizeof(struct job) * njobs;
17038 memcpy(new, jobtab, sizeof(struct job) * njobs);
17039
17040 for (i = 0; i < njobs; i++) {
17041 if (!jobtab[i].used)
17042 continue;
17043
17044 if (jobtab[i].ps == &jobtab[i].ps0) {
17045 new[i].ps0.ps_cmd = nodeckstrdup(jobtab[i].ps0.ps_cmd);
17046 SAVE_PTR(new[i].ps0.ps_cmd,
17047 xasprintf("jobtab[%d].ps0.ps_cmd '%s'",
17048 i, jobtab[i].ps0.ps_cmd), FREE);
17049 new[i].ps = &new[i].ps0;
17050 } else if (jobtab[i].nprocs) {
17051 new[i].ps = procstat_copy(i);
17052 } else {
17053 new[i].ps = NULL;
17054 }
17055 SAVE_PTR(new[i].ps, xasprintf("jobtab[%d].ps", i), FREE);
17056
17057 if (jobtab[i].prev_job) {
17058 new[i].prev_job = new + (jobtab[i].prev_job - jobtab);
17059 SAVE_PTR(new[i].prev_job,
17060 xasprintf("jobtab[%d].prev_job", i), FREE);
17061 }
17062 }
17063 return new;
17064}
17065#endif
17066
17067/*
17068 * struct redirtab
17069 */
17070static int
17071redirtab_size(int funcblocksize, struct redirtab *rdtp)
17072{
17073 while (rdtp) {
17074 funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
17075 rdtp = rdtp->next;
17076 }
17077 return funcblocksize;
17078}
17079
17080static struct redirtab *
17081redirtab_copy(struct redirtab *rdtp)
17082{
17083 struct redirtab *start;
17084 struct redirtab **vpp;
17085
17086 vpp = &start;
17087 while (rdtp) {
17088 int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
17089 *vpp = funcblock;
17090 funcblock = (char *) funcblock + size;
17091 memcpy(*vpp, rdtp, size);
17092 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE);
17093 rdtp = rdtp->next;
17094 vpp = &(*vpp)->next;
17095 }
17096 *vpp = NULL;
17097 return start;
17098}
17099
17100static struct datasize
17101globals_var_size(struct datasize ds)
17102{
17103 int i;
17104
17105 ds.funcblocksize += sizeof(struct globals_var);
17106 ds.funcstringsize += align_len(funcname);
17107 ds = argv_size(ds, shellparam.p);
17108 ds.funcblocksize = redirtab_size(ds.funcblocksize, redirlist);
17109 for (i = 0; i < VTABSIZE; i++)
17110 ds = var_size(ds, vartab[i]);
17111 return ds;
17112}
17113
17114#undef funcname
17115#undef shellparam
17116#undef redirlist
17117#undef vartab
17118static struct globals_var *
17119globals_var_copy(void)
17120{
17121 int i;
17122 struct globals_var *gvp, *new;
17123
17124 gvp = ash_ptr_to_globals_var;
17125 new = funcblock;
17126 funcblock = (char *) funcblock + sizeof(struct globals_var);
17127 memcpy(new, gvp, sizeof(struct globals_var));
17128
17129 new->funcname = nodeckstrdup(gvp->funcname);
17130 SAVE_PTR(new->funcname, xasprintf("funcname '%s'", gvp->funcname ?: "NULL"), FREE);
17131
17132 /* shparam */
17133 new->shellparam.malloced = 0;
17134 new->shellparam.p = argv_copy(gvp->shellparam.p);
17135 SAVE_PTR(new->shellparam.p, "shellparam.p", NO_FREE);
17136
17137 new->redirlist = redirtab_copy(gvp->redirlist);
17138 SAVE_PTR(new->redirlist, "redirlist", NO_FREE);
17139
17140 for (i = 0; i < VTABSIZE; i++) {
17141 new->vartab[i] = var_copy(gvp->vartab[i]);
17142 SAVE_PTR(new->vartab[i], xasprintf("vartab[%d]", i), FREE);
17143 }
17144
17145 return new;
17146}
17147
17148static struct datasize
17149globals_misc_size(struct datasize ds)
17150{
17151 ds.funcblocksize += sizeof(struct globals_misc);
17152 ds.funcstringsize += align_len(minusc);
17153 if (curdir != nullstr)
17154 ds.funcstringsize += align_len(curdir);
17155 if (physdir != nullstr)
17156 ds.funcstringsize += align_len(physdir);
17157 ds.funcstringsize += align_len(arg0);
17158 ds.funcstringsize += align_len(commandname);
17159 for (int i = 0; i < ARRAY_SIZE(trap); i++)
17160 ds.funcstringsize += align_len(trap[i]);
17161 return ds;
17162}
17163
17164#undef minusc
17165#undef curdir
17166#undef physdir
17167#undef arg0
17168#undef commandname
17169#undef nullstr
17170#undef trap
17171static struct globals_misc *
17172globals_misc_copy(void)
17173{
17174 struct globals_misc *p = ash_ptr_to_globals_misc;
17175 struct globals_misc *new = funcblock;
17176
17177 funcblock = (char *) funcblock + sizeof(struct globals_misc);
17178 memcpy(new, p, sizeof(struct globals_misc));
17179
17180 new->minusc = nodeckstrdup(p->minusc);
17181 new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr;
17182 new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr;
17183 new->arg0 = nodeckstrdup(p->arg0);
17184 new->commandname = nodeckstrdup(p->commandname);
17185 SAVE_PTR(new->minusc, xasprintf("minusc '%s'", p->minusc ?: "NULL"), FREE);
17186 SAVE_PTR(new->curdir,
17187 xasprintf("curdir '%s'", new->curdir ?: "NULL"), FREE);
17188 SAVE_PTR(new->physdir,
17189 xasprintf("physdir '%s'", new->physdir ?: "NULL"), FREE);
17190 SAVE_PTR(new->arg0, xasprintf("arg0 '%s'", p->arg0 ?: "NULL"), FREE);
17191 SAVE_PTR(new->commandname,
17192 xasprintf("commandname '%s'", p->commandname ?: "NULL"), FREE);
17193 for (int i = 0; i < ARRAY_SIZE(p->trap); i++) {
17194 new->trap[i] = nodeckstrdup(p->trap[i]);
17195 SAVE_PTR(new->trap[i], xasprintf("trap[%d]", i), FREE);
17196 }
17197 return new;
17198}
17199
17200static struct datasize
17201forkshell_size(struct forkshell *fs)
17202{
17203 struct datasize ds = {0, 0};
17204
17205 ds.funcstringsize += align_len(fs->path);
17206 if (fs->fpid == FS_OPENHERE)
17207 return ds;
17208
17209 ds = globals_var_size(ds);
17210 ds = globals_misc_size(ds);
17211 ds = cmdtable_size(ds);
17212
17213 ds.funcblocksize = calcsize(ds.funcblocksize, fs->n);
17214 ds = argv_size(ds, fs->argv);
17215
17216 if ((ENABLE_ASH_ALIAS || MAX_HISTORY || JOBS_WIN32) &&
17217 fs->fpid != FS_SHELLEXEC) {
17218#if ENABLE_ASH_ALIAS
17219 ds = atab_size(ds);
17220#endif
17221#if MAX_HISTORY
17222 if (line_input_state)
17223 ds = history_size(ds);
17224#endif
17225#if JOBS_WIN32
17226 ds = jobtab_size(ds);
17227#endif
17228 }
17229 return ds;
17230}
17231
17232static void
17233forkshell_copy(struct forkshell *fs, struct forkshell *new)
17234{
17235 memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */
17236
17237 new->path = nodeckstrdup(fs->path);
17238 SAVE_PTR(new->path, xasprintf("path '%s'", fs->path ?: "NULL"), FREE);
17239 if (fs->fpid == FS_OPENHERE)
17240 return;
17241
17242 new->gvp = globals_var_copy();
17243 new->gmp = globals_misc_copy();
17244 new->cmdtable = cmdtable_copy();
17245 SAVE_PTR(new->gvp, "gvp", NO_FREE);
17246 SAVE_PTR(new->gmp, "gmp", NO_FREE);
17247 SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE);
17248
17249 new->n = copynode(fs->n);
17250 new->argv = argv_copy(fs->argv);
17251 SAVE_PTR(new->n, "n", NO_FREE);
17252 SAVE_PTR(new->argv, "argv", NO_FREE);
17253
17254 if ((ENABLE_ASH_ALIAS || MAX_HISTORY || JOBS_WIN32) &&
17255 fs->fpid != FS_SHELLEXEC) {
17256#if ENABLE_ASH_ALIAS
17257 new->atab = atab_copy();
17258 SAVE_PTR(new->atab, "atab", NO_FREE);
17259#endif
17260#if MAX_HISTORY
17261 if (line_input_state) {
17262 new->history = history_copy();
17263 SAVE_PTR(new->history, "history", NO_FREE);
17264 new->cnt_history = line_input_state->cnt_history;
17265 }
17266#endif
17267#if JOBS_WIN32
17268 if (njobs) {
17269 new->jobtab = jobtab_copy();
17270 SAVE_PTR(new->jobtab, "jobtab", NO_FREE);
17271 new->njobs = njobs;
17272 if (curjob) {
17273 new->curjob = new->jobtab + (curjob - jobtab);
17274 SAVE_PTR(new->curjob, "curjob", NO_FREE);
17275 }
17276 }
17277#endif
17278 }
17279}
17280
17281#if FORKSHELL_DEBUG
17282#define NUM_BLOCKS FUNCSTRING
17283enum {GVP, GMP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, JOBTAB, FUNCSTRING};
17284
17285/* fp0 and notes can each be NULL */
17286static void
17287forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
17288{
17289 FILE *fp;
17290 void *lfuncblock;
17291 char *lfuncstring;
17292 char *lrelocate;
17293 char *s;
17294 int count, i, total, bitmapsize;
17295 int size[NUM_BLOCKS];
17296 char *lptr[NUM_BLOCKS+1];
17297 const char *fsname[] = {
17298 "FS_OPENHERE",
17299 "FS_EVALBACKCMD",
17300 "FS_EVALSUBSHELL",
17301 "FS_EVALPIPE",
17302 "FS_SHELLEXEC"
17303 };
17304
17305 if (fp0 != NULL) {
17306 fp = fp0;
17307 }
17308 else {
17309 char name[64];
17310 static int num = 0;
17311
17312 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
17313 if ((fp=fopen(name, "w")) == NULL)
17314 return;
17315 }
17316
17317 bitmapsize = (fs->relocatesize + 7)/8;
17318 total = sizeof(struct forkshell) + fs->funcblocksize +
17319 fs->funcstringsize + bitmapsize;
17320 fprintf(fp, "total size %6d = %d + %d + %d + %d = %d\n",
17321 fs->size + bitmapsize,
17322 (int)sizeof(struct forkshell), fs->funcblocksize,
17323 fs->funcstringsize, bitmapsize, total);
17324
17325 lfuncblock = (char *)(fs + 1);
17326 lfuncstring = (char *)lfuncblock + fs->funcblocksize;
17327 lrelocate = (char *)lfuncstring + fs->funcstringsize;
17328
17329 /* funcblocksize is zero for FS_OPENHERE */
17330 if (fs->funcblocksize != 0) {
17331 /* Depending on the configuration and the type of forkshell
17332 * some items may not be present. */
17333 lptr[FUNCSTRING] = lfuncstring;
17334#if JOBS_WIN32
17335 lptr[JOBTAB] = fs->jobtab ? (char *)fs->jobtab : lptr[FUNCSTRING];
17336#else
17337 lptr[JOBTAB] = lptr[FUNCSTRING];
17338#endif
17339#if MAX_HISTORY
17340 lptr[HISTORY] = fs->history ? (char *)fs->history : lptr[JOBTAB];
17341#else
17342 lptr[HISTORY] = lptr[JOBTAB];
17343#endif
17344 lptr[ATAB] = IF_ASH_ALIAS(fs->atab ? (char *)fs->atab :) lptr[HISTORY];
17345 lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB];
17346 lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV];
17347 lptr[CMDTABLE] = (char *)fs->cmdtable;
17348 lptr[GMP] = (char *)fs->gmp;
17349 lptr[GVP] = (char *)fs->gvp;
17350
17351 fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize);
17352 total = 0;
17353 for (i=0; i<NUM_BLOCKS; ++i) {
17354 size[i] = (int)(lptr[i+1] - lptr[i]);
17355 total += size[i];
17356 fprintf(fp, "%d %c ", size[i], i == NUM_BLOCKS - 1 ? '=' : '+');
17357 }
17358 fprintf(fp, "%d\n\n", total);
17359 }
17360 else {
17361 fprintf(fp, "\n");
17362 }
17363
17364 fprintf(fp, "%s\n\n", fsname[fs->fpid]);
17365 fprintf(fp, "--- relocate ---\n");
17366 count = 0;
17367 for (i = 0; i < fs->relocatesize; ++i) {
17368 if (lrelocate[i/8] & (1 << i % 8)) {
17369 char **ptr = (char **)((char *)fs + i * sizeof(char *));
17370 fprintf(fp, "%p %p %s\n", ptr, *ptr,
17371 notes && notes[i] ? notes[i] : "");
17372 ++count;
17373 }
17374 }
17375 fprintf(fp, "--- %d relocations ---\n\n", count);
17376
17377 fprintf(fp, "--- funcstring ---\n");
17378 count = 0;
17379 s = lfuncstring;
17380 while (s-lfuncstring < fs->funcstringsize) {
17381 if (!*s) {
17382 ++s;
17383 continue;
17384 }
17385 fprintf(fp, "%p '%s'\n", s, s);
17386 s += strlen(s)+1;
17387 ++count;
17388 }
17389 fprintf(fp, "--- %d strings ---\n", count);
17390
17391 if (fp0 == NULL)
17392 fclose(fp);
17393}
17394#endif
17395
17396static struct forkshell *
17397forkshell_prepare(struct forkshell *fs)
17398{
17399 struct forkshell *new;
17400 struct datasize ds;
17401 int size, relocatesize, bitmapsize;
17402 HANDLE h;
17403 SECURITY_ATTRIBUTES sa;
17404#if FORKSHELL_DEBUG
17405 char *relocate;
17406 char name[64];
17407 FILE *fp;
17408 static int num = 0;
17409#endif
17410
17411 /* calculate size of structure, funcblock and funcstring */
17412 ds = forkshell_size(fs);
17413 size = sizeof(struct forkshell) + ds.funcblocksize + ds.funcstringsize;
17414 relocatesize = (sizeof(struct forkshell) + ds.funcblocksize)/sizeof(char *);
17415 bitmapsize = (relocatesize + 7)/8;
17416
17417 /* Allocate shared memory region */
17418 memset(&sa, 0, sizeof(sa));
17419 sa.nLength = sizeof(sa);
17420 sa.lpSecurityDescriptor = NULL;
17421 sa.bInheritHandle = TRUE;
17422 h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0,
17423 size+bitmapsize, NULL);
17424
17425 /* Initialise pointers */
17426 new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
17427 if (new == NULL)
17428 return NULL;
17429 fs_size = size;
17430 fs_start = new;
17431 funcblock = (char *)(new + 1);
17432 funcstring_end = (char *)new + size;
17433#if FORKSHELL_DEBUG
17434 fs_funcstring = (char *)new + sizeof(struct forkshell) + ds.funcblocksize;
17435 relocate = (char *)new + size;
17436 annot = (const char **)xzalloc(sizeof(char *)*relocatesize);
17437 annot_free = xzalloc(relocatesize);
17438#endif
17439
17440 /* Now pack them all */
17441 forkshell_copy(fs, new);
17442
17443 /* Finish it up */
17444 new->size = size;
17445 new->relocatesize = relocatesize;
17446 new->old_base = (char *)new;
17447 new->hMapFile = h;
17448#if FORKSHELL_DEBUG
17449 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
17450 if ((fp=fopen(name, "w")) != NULL) {
17451 int i;
17452
17453 new->funcblocksize = (char *)funcblock - (char *)(new + 1);
17454 new->funcstringsize = (char *)new + size - funcstring_end;
17455
17456 /* perform some sanity checks on pointers */
17457 fprintf(fp, "forkshell %p %6d\n", new, (int)sizeof(*new));
17458 fprintf(fp, "funcblock %p %6d\n", new+1, new->funcblocksize);
17459 fprintf(fp, "funcstring %p %6d\n", funcstring_end,
17460 new->funcstringsize);
17461 if ((char *)funcblock != funcstring_end)
17462 fprintf(fp, " funcstring != end funcblock + 1 %p\n", funcblock);
17463 fprintf(fp, "relocate %p %6d\n\n", relocate, bitmapsize);
17464
17465 forkshell_print(fp, new, annot);
17466
17467 for (i = 0; i < relocatesize; ++i) {
17468 if (annot_free[i]) {
17469 free((void *)annot[i]);
17470 }
17471 }
17472 free(annot);
17473 free(annot_free);
17474 annot = NULL;
17475 fclose(fp);
17476 }
17477#endif
17478 return new;
17479}
17480
17481#undef trap_ptr
17482static void
17483forkshell_init(const char *idstr)
17484{
17485 struct forkshell *fs;
17486 void *map_handle;
17487 HANDLE h;
17488 int i;
17489 char **ptr;
17490 char *lrelocate;
17491 struct jmploc jmploc;
17492
17493 if (sscanf(idstr, "%p", &map_handle) != 1)
17494 return;
17495
17496 h = (HANDLE)map_handle;
17497 fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
17498 if (!fs)
17499 return;
17500
17501 /* this memory can't be freed */
17502 sticky_mem_start = fs;
17503 sticky_mem_end = (char *) fs + fs->size;
17504
17505 /* pointer fixup */
17506 lrelocate = (char *)fs + fs->size;
17507 for (i = 0; i < fs->relocatesize; i++) {
17508 if (lrelocate[i/8] & (1 << i % 8)) {
17509 ptr = (char **)((char *)fs + i * sizeof(char *));
17510 if (*ptr)
17511 *ptr = (char *)fs + (*ptr - fs->old_base);
17512 }
17513 }
17514
17515 if (fs->fpid == FS_OPENHERE)
17516 goto end;
17517
17518 /* Now fix up stuff that can't be transferred */
17519 for (i = 0; i < CMDTABLESIZE; i++) {
17520 struct tblentry *e = fs->cmdtable[i];
17521 while (e) {
17522 if (e->cmdtype == CMDBUILTIN)
17523 e->param.cmd = builtintab + e->param.index;
17524 e = e->next;
17525 }
17526 }
17527 fs->gmp->trap_ptr = fs->gmp->trap;
17528
17529 /* Set global variables */
17530 ASSIGN_CONST_PTR(&ash_ptr_to_globals_var, fs->gvp);
17531 ASSIGN_CONST_PTR(&ash_ptr_to_globals_misc, fs->gmp);
17532 cmdtable = fs->cmdtable;
17533#if ENABLE_ASH_ALIAS
17534 atab = fs->atab; /* will be NULL for FS_SHELLEXEC */
17535#endif
17536#if MAX_HISTORY
17537 if (fs->cnt_history) {
17538 line_input_state = new_line_input_t(FOR_SHELL);
17539 line_input_state->cnt_history = fs->cnt_history;
17540 for (i = 0; i < line_input_state->cnt_history; i++)
17541 line_input_state->history[i] = fs->history[i];
17542 }
17543#endif
17544#if JOBS_WIN32
17545 jobtab = fs->jobtab;
17546 njobs = fs->njobs;
17547 curjob = fs->curjob;
17548#endif
17549
17550 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
17551
17552 reinitvar();
17553
17554 if (setjmp(jmploc.loc)) {
17555 exitreset();
17556 exitshell();
17557 }
17558 exception_handler = &jmploc;
17559
17560 shlvl++;
17561 if (fs->mode == FORK_BG) {
17562 SetConsoleCtrlHandler(NULL, TRUE);
17563 if (fs->nprocs == 0) {
17564 close(0);
17565 if (open(bb_dev_null, O_RDONLY) != 0)
17566 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
17567 }
17568 }
17569 else {
17570 SetConsoleCtrlHandler(ctrl_handler, TRUE);
17571 }
17572
17573 if (fs->n && fs->n->type == NCMD /* is it single cmd? */
17574 /* && n->ncmd.args->type == NARG - always true? */
17575 && fs->n->ncmd.args && strcmp(fs->n->ncmd.args->narg.text, "trap") == 0
17576 && fs->n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
17577 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
17578 ) {
17579 TRACE(("Trap hack\n"));
17580 /* Save trap handler strings for trap builtin to print */
17581 fs->gmp->trap_ptr = xmemdup(fs->gmp->trap, sizeof(fs->gmp->trap));
17582 /* Fall through into clearing traps */
17583 }
17584 clear_traps();
17585#if JOBS_WIN32
17586 /* do job control only in root shell */
17587 doing_jobctl = 0;
17588
17589 if (fs->n && fs->n->type == NCMD && fs->n->ncmd.args &&
17590 strcmp(fs->n->ncmd.args->narg.text, "jobs") == 0) {
17591 TRACE(("Job hack\n"));
17592 if (!fs->jpnull)
17593 freejob(curjob);
17594 goto end;
17595 }
17596 for (struct job *jp = curjob; jp; jp = jp->prev_job)
17597 freejob(jp);
17598#endif
17599 end:
17600 forkshell_child(fs);
17601}
17602
17603#undef free
17604static void
17605sticky_free(void *base)
17606{
17607 if (base >= sticky_mem_start && base < sticky_mem_end)
17608 return;
17609 free(base);
17610}
17611#endif
14887 17612
14888/*- 17613/*-
14889 * Copyright (c) 1989, 1991, 1993, 1994 17614 * Copyright (c) 1989, 1991, 1993, 1994
diff --git a/shell/ash_test/ash-read/read_ifs2.right b/shell/ash_test/ash-read/read_ifs2.right
new file mode 100644
index 000000000..797137dae
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.right
@@ -0,0 +1,9 @@
1|X|Y:Z:|
2|X|Y:Z|
3|X|Y|
4|X|Y|
5|X||
6|X||
7|||
8Whitespace should be trimmed too:
9|X|Y|
diff --git a/shell/ash_test/ash-read/read_ifs2.tests b/shell/ash_test/ash-read/read_ifs2.tests
new file mode 100755
index 000000000..f01a68978
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.tests
@@ -0,0 +1,9 @@
1echo "X:Y:Z:" | (IFS=": " read x y; echo "|$x|$y|")
2echo "X:Y:Z" | (IFS=": " read x y; echo "|$x|$y|")
3echo "X:Y:" | (IFS=": " read x y; echo "|$x|$y|")
4echo "X:Y" | (IFS=": " read x y; echo "|$x|$y|")
5echo "X:" | (IFS=": " read x y; echo "|$x|$y|")
6echo "X" | (IFS=": " read x y; echo "|$x|$y|")
7echo "" | (IFS=": " read x y; echo "|$x|$y|")
8echo Whitespace should be trimmed too:
9echo "X:Y : " | (IFS=": " read x y; echo "|$x|$y|")
diff --git a/shell/ash_test/ash-read/read_t.right b/shell/ash_test/ash-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/ash_test/ash-read/read_t.right
+++ b/shell/ash_test/ash-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/ash_test/ash-read/read_t.tests b/shell/ash_test/ash-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/ash_test/ash-read/read_t.tests
+++ b/shell/ash_test/ash-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/ash_test/printenv.c b/shell/ash_test/printenv.c
index c86308d3b..f0f41984d 100644
--- a/shell/ash_test/printenv.c
+++ b/shell/ash_test/printenv.c
@@ -31,9 +31,7 @@
31extern char **environ; 31extern char **environ;
32 32
33int 33int
34main (argc, argv) 34main (int argc, char **argv)
35 int argc;
36 char **argv;
37{ 35{
38 register char **envp, *eval; 36 register char **envp, *eval;
39 int len; 37 int len;
diff --git a/shell/ash_test/recho.c b/shell/ash_test/recho.c
index 42a5feafd..7e96b14cc 100644
--- a/shell/ash_test/recho.c
+++ b/shell/ash_test/recho.c
@@ -27,7 +27,7 @@
27#include <stdio.h> 27#include <stdio.h>
28#include <stdlib.h> 28#include <stdlib.h>
29 29
30void strprint(); 30void strprint(char *);
31 31
32int main(int argc, char **argv) 32int main(int argc, char **argv)
33{ 33{
diff --git a/shell/hush.c b/shell/hush.c
index 4a97293cc..09ab6ebc0 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -392,6 +392,8 @@
392 392
393/* Build knobs */ 393/* Build knobs */
394#define LEAK_HUNTING 0 394#define LEAK_HUNTING 0
395#define LEAK_PRINTF(...) fdprintf(__VA_ARGS__)
396//#define LEAK_PRINTF(...) do { if (ptr_to_globals && G.root_pid == getpid()) fdprintf(__VA_ARGS__); } while (0)
395#define BUILD_AS_NOMMU 0 397#define BUILD_AS_NOMMU 0
396/* Enable/disable sanity checks. Ok to enable in production, 398/* Enable/disable sanity checks. Ok to enable in production,
397 * only adds a bit of bloat. Set to >1 to get non-production level verbosity. 399 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
@@ -930,6 +932,12 @@ struct globals {
930# define G_flag_return_in_progress 0 932# define G_flag_return_in_progress 0
931#endif 933#endif
932 smallint exiting; /* used to prevent EXIT trap recursion */ 934 smallint exiting; /* used to prevent EXIT trap recursion */
935#if !BB_MMU
936 smallint reexeced_on_NOMMU;
937# define G_reexeced_on_NOMMU (G.reexeced_on_NOMMU)
938#else
939# define G_reexeced_on_NOMMU 0
940#endif
933 /* These support $? */ 941 /* These support $? */
934 smalluint last_exitcode; 942 smalluint last_exitcode;
935 smalluint expand_exitcode; 943 smalluint expand_exitcode;
@@ -1352,30 +1360,67 @@ static void debug_print_strings(const char *prefix, char **vv)
1352static void *xxmalloc(int lineno, size_t size) 1360static void *xxmalloc(int lineno, size_t size)
1353{ 1361{
1354 void *ptr = xmalloc((size + 0xff) & ~0xff); 1362 void *ptr = xmalloc((size + 0xff) & ~0xff);
1355 fdprintf(2, "line %d: malloc %p\n", lineno, ptr); 1363 LEAK_PRINTF(2, "line %d: malloc %p\n", lineno, ptr);
1364 return ptr;
1365}
1366static void *xxzalloc(int lineno, size_t size)
1367{
1368 void *ptr = xzalloc((size + 0xff) & ~0xff);
1369 LEAK_PRINTF(2, "line %d: zalloc %p\n", lineno, ptr);
1356 return ptr; 1370 return ptr;
1357} 1371}
1358static void *xxrealloc(int lineno, void *ptr, size_t size) 1372static void *xxrealloc(int lineno, void *ptr, size_t size)
1359{ 1373{
1374 char *p = ptr;
1360 ptr = xrealloc(ptr, (size + 0xff) & ~0xff); 1375 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1361 fdprintf(2, "line %d: realloc %p\n", lineno, ptr); 1376 if (p != ptr)
1377 LEAK_PRINTF(2, "line %d: realloc %p\n", lineno, ptr);
1378 return ptr;
1379}
1380static void *xxrealloc_getcwd_or_warn(int lineno, char *ptr)
1381{
1382 char *p = ptr;
1383 ptr = xrealloc_getcwd_or_warn(ptr);
1384 if (p != ptr)
1385 LEAK_PRINTF(2, "line %d: xrealloc_getcwd_or_warn %p\n", lineno, ptr);
1362 return ptr; 1386 return ptr;
1363} 1387}
1364static char *xxstrdup(int lineno, const char *str) 1388static char *xxstrdup(int lineno, const char *str)
1365{ 1389{
1366 char *ptr = xstrdup(str); 1390 char *ptr = xstrdup(str);
1367 fdprintf(2, "line %d: strdup %p\n", lineno, ptr); 1391 LEAK_PRINTF(2, "line %d: strdup %p\n", lineno, ptr);
1392 return ptr;
1393}
1394static char *xxstrndup(int lineno, const char *str, size_t n)
1395{
1396 char *ptr = xstrndup(str, n);
1397 LEAK_PRINTF(2, "line %d: strndup %p\n", lineno, ptr);
1398 return ptr;
1399}
1400static char *xxasprintf(int lineno, const char *f, ...)
1401{
1402 char *ptr;
1403 va_list args;
1404 va_start(args, f);
1405 if (vasprintf(&ptr, f, args) < 0)
1406 bb_die_memory_exhausted();
1407 va_end(args);
1408 LEAK_PRINTF(2, "line %d: xasprintf %p\n", lineno, ptr);
1368 return ptr; 1409 return ptr;
1369} 1410}
1370static void xxfree(void *ptr) 1411static void xxfree(void *ptr)
1371{ 1412{
1372 fdprintf(2, "free %p\n", ptr); 1413 LEAK_PRINTF(2, "free %p\n", ptr);
1373 free(ptr); 1414 free(ptr);
1374} 1415}
1375# define xmalloc(s) xxmalloc(__LINE__, s) 1416# define xmalloc(s) xxmalloc(__LINE__, s)
1376# define xrealloc(p, s) xxrealloc(__LINE__, p, s) 1417# define xzalloc(s) xxzalloc(__LINE__, s)
1377# define xstrdup(s) xxstrdup(__LINE__, s) 1418# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1378# define free(p) xxfree(p) 1419# define xrealloc_getcwd_or_warn(p) xxrealloc_getcwd_or_warn(__LINE__, p)
1420# define xstrdup(s) xxstrdup(__LINE__, s)
1421# define xstrndup(s, n) xxstrndup(__LINE__, s, n)
1422# define xasprintf(f, ...) xxasprintf(__LINE__, f, __VA_ARGS__)
1423# define free(p) xxfree(p)
1379#endif 1424#endif
1380 1425
1381/* 1426/*
@@ -1929,7 +1974,7 @@ static void restore_G_args(save_arg_t *sv, char **argv)
1929 * "trap - SIGxxx": 1974 * "trap - SIGxxx":
1930 * if sig is in special_sig_mask, set handler back to: 1975 * if sig is in special_sig_mask, set handler back to:
1931 * record_pending_signo, or to IGN if it's a tty stop signal 1976 * record_pending_signo, or to IGN if it's a tty stop signal
1932 * if sig is in fatal_sig_mask, set handler back to sigexit. 1977 * if sig is in fatal_sig_mask, set handler back to restore_ttypgrp_and_killsig_or__exit.
1933 * else: set handler back to SIG_DFL 1978 * else: set handler back to SIG_DFL
1934 * "trap 'cmd' SIGxxx": 1979 * "trap 'cmd' SIGxxx":
1935 * set handler to record_pending_signo. 1980 * set handler to record_pending_signo.
@@ -1973,7 +2018,7 @@ static void record_pending_signo(int sig)
1973 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0]) 2018 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0])
1974 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */ 2019 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */
1975 ) { 2020 ) {
1976 bb_got_signal = sig; /* for read_line_input: "we got a signal" */ 2021 bb_got_signal = sig; /* for read_line_input / read builtin: "we got a signal" */
1977 } 2022 }
1978#endif 2023#endif
1979#if ENABLE_HUSH_FAST 2024#if ENABLE_HUSH_FAST
@@ -2002,19 +2047,6 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
2002 return old_sa.sa_handler; 2047 return old_sa.sa_handler;
2003} 2048}
2004 2049
2005static void hush_exit(int exitcode) NORETURN;
2006
2007static void restore_ttypgrp_and__exit(void) NORETURN;
2008static void restore_ttypgrp_and__exit(void)
2009{
2010 /* xfunc has failed! die die die */
2011 /* no EXIT traps, this is an escape hatch! */
2012 G.exiting = 1;
2013 hush_exit(xfunc_error_retval);
2014}
2015
2016#if ENABLE_HUSH_JOB
2017
2018/* Needed only on some libc: 2050/* Needed only on some libc:
2019 * It was observed that on exit(), fgetc'ed buffered data 2051 * It was observed that on exit(), fgetc'ed buffered data
2020 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR). 2052 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
@@ -2028,26 +2060,20 @@ static void restore_ttypgrp_and__exit(void)
2028 * and in `cmd` handling. 2060 * and in `cmd` handling.
2029 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): 2061 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2030 */ 2062 */
2031static void fflush_and__exit(void) NORETURN; 2063static NORETURN void fflush_and__exit(void)
2032static void fflush_and__exit(void)
2033{ 2064{
2034 fflush_all(); 2065 fflush_all();
2035 _exit(xfunc_error_retval); 2066 _exit(xfunc_error_retval);
2036} 2067}
2037 2068
2038/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ 2069#if ENABLE_HUSH_JOB
2039# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2040/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2041# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
2042
2043/* Restores tty foreground process group, and exits. 2070/* Restores tty foreground process group, and exits.
2044 * May be called as signal handler for fatal signal 2071 * May be called as signal handler for fatal signal
2045 * (will resend signal to itself, producing correct exit state) 2072 * (will resend signal to itself, producing correct exit state)
2046 * or called directly with -EXITCODE. 2073 * or called directly with -EXITCODE.
2047 * We also call it if xfunc is exiting. 2074 * We also call it if xfunc is exiting.
2048 */ 2075 */
2049static void sigexit(int sig) NORETURN; 2076static NORETURN void restore_ttypgrp_and_killsig_or__exit(int sig)
2050static void sigexit(int sig)
2051{ 2077{
2052 /* Careful: we can end up here after [v]fork. Do not restore 2078 /* Careful: we can end up here after [v]fork. Do not restore
2053 * tty pgrp then, only top-level shell process does that */ 2079 * tty pgrp then, only top-level shell process does that */
@@ -2065,6 +2091,19 @@ static void sigexit(int sig)
2065 2091
2066 kill_myself_with_sig(sig); /* does not return */ 2092 kill_myself_with_sig(sig); /* does not return */
2067} 2093}
2094
2095static NORETURN void fflush_restore_ttypgrp_and__exit(void)
2096{
2097 /* xfunc has failed! die die die */
2098 fflush_all();
2099 restore_ttypgrp_and_killsig_or__exit(- xfunc_error_retval);
2100}
2101
2102/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2103# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2104/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2105# define enable_restore_tty_pgrp_on_exit() (die_func = fflush_restore_ttypgrp_and__exit)
2106
2068#else 2107#else
2069 2108
2070# define disable_restore_tty_pgrp_on_exit() ((void)0) 2109# define disable_restore_tty_pgrp_on_exit() ((void)0)
@@ -2081,7 +2120,7 @@ static sighandler_t pick_sighandler(unsigned sig)
2081#if ENABLE_HUSH_JOB 2120#if ENABLE_HUSH_JOB
2082 /* is sig fatal? */ 2121 /* is sig fatal? */
2083 if (G_fatal_sig_mask & sigmask) 2122 if (G_fatal_sig_mask & sigmask)
2084 handler = sigexit; 2123 handler = restore_ttypgrp_and_killsig_or__exit;
2085 else 2124 else
2086#endif 2125#endif
2087 /* sig has special handling? */ 2126 /* sig has special handling? */
@@ -2099,11 +2138,33 @@ static sighandler_t pick_sighandler(unsigned sig)
2099 return handler; 2138 return handler;
2100} 2139}
2101 2140
2102/* Restores tty foreground process group, and exits. */ 2141static const char* FAST_FUNC get_local_var_value(const char *name);
2103static void hush_exit(int exitcode) 2142
2143/* Self-explanatory.
2144 * Restores tty foreground process group too.
2145 */
2146static NORETURN void save_history_run_exit_trap_and_exit(int exitcode)
2104{ 2147{
2105#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2148#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2106 save_history(G.line_input_state); /* may be NULL */ 2149 if (G.line_input_state
2150 && getpid() == G.root_pid /* exits in subshells do not save history */
2151 ) {
2152 const char *hp;
2153# if ENABLE_FEATURE_SH_HISTFILESIZE
2154// in bash:
2155// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
2156// "When this variable is assigned a value, the history file is truncated, if necessary"
2157// but we do it only at exit, not on every assignment:
2158 /* Use HISTFILESIZE to limit file size */
2159 hp = get_local_var_value("HISTFILESIZE");
2160 if (hp)
2161 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
2162# endif
2163 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
2164 hp = get_local_var_value("HISTFILE");
2165 G.line_input_state->hist_file = hp;
2166 save_history(G.line_input_state); /* no-op if hist_file is NULL or "" */
2167 }
2107#endif 2168#endif
2108 2169
2109 fflush_all(); 2170 fflush_all();
@@ -2137,7 +2198,7 @@ static void hush_exit(int exitcode)
2137 2198
2138 fflush_all(); 2199 fflush_all();
2139#if ENABLE_HUSH_JOB 2200#if ENABLE_HUSH_JOB
2140 sigexit(- (exitcode & 0xff)); 2201 restore_ttypgrp_and_killsig_or__exit(- (exitcode & 0xff));
2141#else 2202#else
2142 _exit(exitcode); 2203 _exit(exitcode);
2143#endif 2204#endif
@@ -2222,7 +2283,7 @@ static int check_and_run_traps(void)
2222 } 2283 }
2223 } 2284 }
2224 /* this restores tty pgrp, then kills us with SIGHUP */ 2285 /* this restores tty pgrp, then kills us with SIGHUP */
2225 sigexit(SIGHUP); 2286 restore_ttypgrp_and_killsig_or__exit(SIGHUP);
2226 } 2287 }
2227#endif 2288#endif
2228#if ENABLE_HUSH_FAST 2289#if ENABLE_HUSH_FAST
@@ -4607,6 +4668,11 @@ static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt,
4607 4668
4608 redir->rd_type = REDIRECT_HEREDOC2; 4669 redir->rd_type = REDIRECT_HEREDOC2;
4609 /* redir->rd_dup is (ab)used to indicate <<- */ 4670 /* redir->rd_dup is (ab)used to indicate <<- */
4671 if (!redir->rd_filename) {
4672 /* examples: "echo <<", "echo <<<", "echo <<>" */
4673 syntax_error("missing here document terminator");
4674 return -1;
4675 }
4610 p = fetch_till_str(as_string, input, 4676 p = fetch_till_str(as_string, input,
4611 redir->rd_filename, redir->rd_dup); 4677 redir->rd_filename, redir->rd_dup);
4612 if (!p) { 4678 if (!p) {
@@ -7366,11 +7432,6 @@ static void switch_off_special_sigs(unsigned mask)
7366} 7432}
7367 7433
7368#if BB_MMU 7434#if BB_MMU
7369/* never called */
7370void re_execute_shell(char ***to_free, const char *s,
7371 char *g_argv0, char **g_argv,
7372 char **builtin_argv) NORETURN;
7373
7374static void reset_traps_to_defaults(void) 7435static void reset_traps_to_defaults(void)
7375{ 7436{
7376 /* This function is always called in a child shell 7437 /* This function is always called in a child shell
@@ -7420,10 +7481,8 @@ static void reset_traps_to_defaults(void)
7420 7481
7421#else /* !BB_MMU */ 7482#else /* !BB_MMU */
7422 7483
7423static void re_execute_shell(char ***to_free, const char *s, 7484static NORETURN void re_execute_shell(
7424 char *g_argv0, char **g_argv, 7485 char * *volatile * to_free, const char *s,
7425 char **builtin_argv) NORETURN;
7426static void re_execute_shell(char ***to_free, const char *s,
7427 char *g_argv0, char **g_argv, 7486 char *g_argv0, char **g_argv,
7428 char **builtin_argv) 7487 char **builtin_argv)
7429{ 7488{
@@ -7653,7 +7712,13 @@ static int generate_stream_from_string(const char *s, pid_t *pid_p)
7653 pid_t pid; 7712 pid_t pid;
7654 int channel[2]; 7713 int channel[2];
7655# if !BB_MMU 7714# if !BB_MMU
7656 char **to_free = NULL; 7715 /* _volatile_ pointer to "char*".
7716 * Or else compiler can peek from inside re_execute_shell()
7717 * and see that this pointer is a local var (i.e. not globally visible),
7718 * and decide to optimize out the store to it. Yes,
7719 * it was seen in the wild.
7720 */
7721 char * *volatile to_free = NULL;
7657# endif 7722# endif
7658 7723
7659 xpipe(channel); 7724 xpipe(channel);
@@ -7800,7 +7865,7 @@ static void setup_heredoc(struct redir_struct *redir)
7800 const char *heredoc = redir->rd_filename; 7865 const char *heredoc = redir->rd_filename;
7801 char *expanded; 7866 char *expanded;
7802#if !BB_MMU 7867#if !BB_MMU
7803 char **to_free; 7868 char * *volatile to_free;
7804#endif 7869#endif
7805 7870
7806 expanded = NULL; 7871 expanded = NULL;
@@ -8275,7 +8340,7 @@ static const struct built_in_command *find_builtin(const char *name)
8275 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]); 8340 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8276} 8341}
8277 8342
8278#if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION 8343#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_TAB_COMPLETION
8279static const char * FAST_FUNC hush_command_name(int i) 8344static const char * FAST_FUNC hush_command_name(int i)
8280{ 8345{
8281 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) { 8346 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
@@ -8441,10 +8506,8 @@ static void unset_func(const char *name)
8441#define exec_function(to_free, funcp, argv) \ 8506#define exec_function(to_free, funcp, argv) \
8442 exec_function(funcp, argv) 8507 exec_function(funcp, argv)
8443# endif 8508# endif
8444static void exec_function(char ***to_free, 8509static NORETURN void exec_function(
8445 const struct function *funcp, 8510 char * *volatile *to_free,
8446 char **argv) NORETURN;
8447static void exec_function(char ***to_free,
8448 const struct function *funcp, 8511 const struct function *funcp,
8449 char **argv) 8512 char **argv)
8450{ 8513{
@@ -8540,10 +8603,8 @@ static int run_function(const struct function *funcp, char **argv)
8540#define exec_builtin(to_free, x, argv) \ 8603#define exec_builtin(to_free, x, argv) \
8541 exec_builtin(to_free, argv) 8604 exec_builtin(to_free, argv)
8542#endif 8605#endif
8543static void exec_builtin(char ***to_free, 8606static NORETURN void exec_builtin(
8544 const struct built_in_command *x, 8607 char * *volatile *to_free,
8545 char **argv) NORETURN;
8546static void exec_builtin(char ***to_free,
8547 const struct built_in_command *x, 8608 const struct built_in_command *x,
8548 char **argv) 8609 char **argv)
8549{ 8610{
@@ -8566,8 +8627,7 @@ static void exec_builtin(char ***to_free,
8566#endif 8627#endif
8567} 8628}
8568 8629
8569static void execvp_or_die(char **argv) NORETURN; 8630static NORETURN void execvp_or_die(char **argv)
8570static void execvp_or_die(char **argv)
8571{ 8631{
8572 int e; 8632 int e;
8573 debug_printf_exec("execing '%s'\n", argv[0]); 8633 debug_printf_exec("execing '%s'\n", argv[0]);
@@ -8688,10 +8748,8 @@ static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *exp
8688 * The at_exit handlers apparently confuse the calling process, 8748 * The at_exit handlers apparently confuse the calling process,
8689 * in particular stdin handling. Not sure why? -- because of vfork! (vda) 8749 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
8690 */ 8750 */
8691static void pseudo_exec_argv(nommu_save_t *nommu_save, 8751static NORETURN NOINLINE void pseudo_exec_argv(
8692 char **argv, int assignment_cnt, 8752 volatile nommu_save_t *nommu_save,
8693 char **argv_expanded) NORETURN;
8694static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8695 char **argv, int assignment_cnt, 8753 char **argv, int assignment_cnt,
8696 char **argv_expanded) 8754 char **argv_expanded)
8697{ 8755{
@@ -8717,7 +8775,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8717#if BB_MMU 8775#if BB_MMU
8718 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */ 8776 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
8719#else 8777#else
8720 G.shadowed_vars_pp = &nommu_save->old_vars; 8778 /* cast away volatility */
8779 G.shadowed_vars_pp = (struct variable **)&nommu_save->old_vars;
8721 G.var_nest_level++; 8780 G.var_nest_level++;
8722#endif 8781#endif
8723 set_vars_and_save_old(new_env); 8782 set_vars_and_save_old(new_env);
@@ -8844,10 +8903,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8844 8903
8845/* Called after [v]fork() in run_pipe 8904/* Called after [v]fork() in run_pipe
8846 */ 8905 */
8847static void pseudo_exec(nommu_save_t *nommu_save, 8906static NORETURN void pseudo_exec(
8848 struct command *command, 8907 volatile nommu_save_t *nommu_save,
8849 char **argv_expanded) NORETURN;
8850static void pseudo_exec(nommu_save_t *nommu_save,
8851 struct command *command, 8908 struct command *command,
8852 char **argv_expanded) 8909 char **argv_expanded)
8853{ 8910{
@@ -9732,8 +9789,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
9732 9789
9733 /* Stores to nommu_save list of env vars putenv'ed 9790 /* Stores to nommu_save list of env vars putenv'ed
9734 * (NOMMU, on MMU we don't need that) */ 9791 * (NOMMU, on MMU we don't need that) */
9735 /* cast away volatility... */ 9792 pseudo_exec(&nommu_save, command, argv_expanded);
9736 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
9737 /* pseudo_exec() does not return */ 9793 /* pseudo_exec() does not return */
9738 } 9794 }
9739 9795
@@ -10121,7 +10177,7 @@ static int run_list(struct pipe *pi)
10121 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) { 10177 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10122 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth); 10178 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10123 if (G.errexit_depth == 0) 10179 if (G.errexit_depth == 0)
10124 hush_exit(rcode); 10180 save_history_run_exit_trap_and_exit(rcode);
10125 } 10181 }
10126 G.errexit_depth = sv_errexit_depth; 10182 G.errexit_depth = sv_errexit_depth;
10127 10183
@@ -10195,6 +10251,53 @@ static int run_and_free_list(struct pipe *pi)
10195/* 10251/*
10196 * Initialization and main 10252 * Initialization and main
10197 */ 10253 */
10254#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING
10255static void init_line_editing(void)
10256{
10257 G.line_input_state = new_line_input_t(FOR_SHELL);
10258# if ENABLE_FEATURE_TAB_COMPLETION
10259 G.line_input_state->get_exe_name = hush_command_name;
10260# endif
10261# if EDITING_HAS_sh_get_var
10262 G.line_input_state->sh_get_var = get_local_var_value;
10263# endif
10264# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
10265 {
10266 const char *hp = get_local_var_value("HISTFILE");
10267 if (!hp) {
10268 hp = get_local_var_value("HOME");
10269 if (hp) {
10270 hp = concat_path_file(hp, ".hush_history");
10271 /* Make HISTFILE set on exit (else history won't be saved) */
10272 set_local_var_from_halves("HISTFILE", hp);
10273 }
10274 } else {
10275 hp = xstrdup(hp);
10276 }
10277 if (hp) {
10278 G.line_input_state->hist_file = hp;
10279 }
10280# if ENABLE_FEATURE_SH_HISTFILESIZE
10281 hp = get_local_var_value("HISTSIZE");
10282 /* Using HISTFILESIZE above to limit max_history would be WRONG:
10283 * users may set HISTFILESIZE=0 in their profile scripts
10284 * to prevent _saving_ of history files, but still want to have
10285 * non-zero history limit for in-memory list.
10286 */
10287// in bash, runtime history size is controlled by HISTSIZE (0=no history),
10288// HISTFILESIZE controls on-disk history file size (in lines, 0=no history):
10289 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10290// HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files."
10291// HISTSIZE: "The shell sets the default value to 500 after reading any startup files."
10292// (meaning: if the value wasn't set after startup files, the default value is set as described above)
10293# endif
10294 }
10295# endif
10296}
10297#else
10298# define init_line_editing() ((void)0)
10299#endif
10300
10198static void install_sighandlers(unsigned mask) 10301static void install_sighandlers(unsigned mask)
10199{ 10302{
10200 sighandler_t old_handler; 10303 sighandler_t old_handler;
@@ -10333,7 +10436,6 @@ static int set_mode(int state, char mode, const char *o_opt)
10333int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 10436int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10334int hush_main(int argc, char **argv) 10437int hush_main(int argc, char **argv)
10335{ 10438{
10336 pid_t cached_getpid;
10337 enum { 10439 enum {
10338 OPT_login = (1 << 0), 10440 OPT_login = (1 << 0),
10339 }; 10441 };
@@ -10346,6 +10448,11 @@ int hush_main(int argc, char **argv)
10346 struct variable *shell_ver; 10448 struct variable *shell_ver;
10347 10449
10348 INIT_G(); 10450 INIT_G();
10451#if ENABLE_HUSH_JOB
10452 die_func = fflush_restore_ttypgrp_and__exit;
10453#else
10454 die_func = fflush_and__exit;
10455#endif
10349 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ 10456 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10350 G.last_exitcode = EXIT_SUCCESS; 10457 G.last_exitcode = EXIT_SUCCESS;
10351#if !BB_MMU 10458#if !BB_MMU
@@ -10361,9 +10468,6 @@ int hush_main(int argc, char **argv)
10361 _exit(0); 10468 _exit(0);
10362 } 10469 }
10363 G.argv0_for_re_execing = argv[0]; 10470 G.argv0_for_re_execing = argv[0];
10364 if (G.argv0_for_re_execing[0] == '-')
10365 /* reexeced hush should never be a login shell */
10366 G.argv0_for_re_execing++;
10367#endif 10471#endif
10368#if ENABLE_HUSH_TRAP 10472#if ENABLE_HUSH_TRAP
10369# if ENABLE_HUSH_FUNCTIONS 10473# if ENABLE_HUSH_FUNCTIONS
@@ -10376,9 +10480,8 @@ int hush_main(int argc, char **argv)
10376 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ 10480 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10377#endif 10481#endif
10378 10482
10379 cached_getpid = getpid(); /* for tcsetpgrp() during init */ 10483 G.root_pid = getpid(); /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10380 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */ 10484 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10381 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10382 10485
10383 /* Deal with HUSH_VERSION */ 10486 /* Deal with HUSH_VERSION */
10384 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); 10487 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
@@ -10427,7 +10530,7 @@ int hush_main(int argc, char **argv)
10427 if (!get_local_var_value("PATH")) 10530 if (!get_local_var_value("PATH"))
10428 set_local_var_from_halves("PATH", bb_default_root_path); 10531 set_local_var_from_halves("PATH", bb_default_root_path);
10429 10532
10430 /* PS1/PS2 are set later, if we determine that we are interactive */ 10533 /* PS1/PS2/HISTFILE are set later, if we determine that we are interactive */
10431 10534
10432 /* bash also exports SHLVL and _, 10535 /* bash also exports SHLVL and _,
10433 * and sets (but doesn't export) the following variables: 10536 * and sets (but doesn't export) the following variables:
@@ -10449,7 +10552,6 @@ int hush_main(int argc, char **argv)
10449 * BASH_SOURCE=() 10552 * BASH_SOURCE=()
10450 * DIRSTACK=() 10553 * DIRSTACK=()
10451 * PIPESTATUS=([0]="0") 10554 * PIPESTATUS=([0]="0")
10452 * HISTFILE=/<xxx>/.bash_history
10453 * HISTFILESIZE=500 10555 * HISTFILESIZE=500
10454 * HISTSIZE=500 10556 * HISTSIZE=500
10455 * MAILCHECK=60 10557 * MAILCHECK=60
@@ -10462,27 +10564,24 @@ int hush_main(int argc, char **argv)
10462 * PS4='+ ' 10564 * PS4='+ '
10463 */ 10565 */
10464 10566
10567 /* Shell is non-interactive at first. We need to call
10568 * install_special_sighandlers() if we are going to execute "sh <script>",
10569 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10570 * If we later decide that we are interactive, we run
10571 * install_special_sighandlers() in order to intercept more signals.
10572 */
10573 install_special_sighandlers();
10574
10465#if NUM_SCRIPTS > 0 10575#if NUM_SCRIPTS > 0
10466 if (argc < 0) { 10576 if (argc < 0) {
10467 char *script = get_script_content(-argc - 1); 10577 char *script = get_script_content(-argc - 1);
10468 G.global_argv = argv; 10578 G.global_argv = argv;
10469 G.global_argc = string_array_len(argv); 10579 G.global_argc = string_array_len(argv);
10470 //install_special_sighandlers(); - needed?
10471 parse_and_run_string(script); 10580 parse_and_run_string(script);
10472 goto final_return; 10581 goto final_return;
10473 } 10582 }
10474#endif 10583#endif
10475 10584
10476 /* Initialize some more globals to non-zero values */
10477 die_func = restore_ttypgrp_and__exit;
10478
10479 /* Shell is non-interactive at first. We need to call
10480 * install_special_sighandlers() if we are going to execute "sh <script>",
10481 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10482 * If we later decide that we are interactive, we run install_special_sighandlers()
10483 * in order to intercept (more) signals.
10484 */
10485
10486 /* Parse options */ 10585 /* Parse options */
10487 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ 10586 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10488 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 10587 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
@@ -10546,6 +10645,7 @@ int hush_main(int argc, char **argv)
10546 case '$': { 10645 case '$': {
10547 unsigned long long empty_trap_mask; 10646 unsigned long long empty_trap_mask;
10548 10647
10648 G.reexeced_on_NOMMU = 1;
10549 G.root_pid = bb_strtou(optarg, &optarg, 16); 10649 G.root_pid = bb_strtou(optarg, &optarg, 16);
10550 optarg++; 10650 optarg++;
10551 G.root_ppid = bb_strtou(optarg, &optarg, 16); 10651 G.root_ppid = bb_strtou(optarg, &optarg, 16);
@@ -10559,7 +10659,6 @@ int hush_main(int argc, char **argv)
10559 empty_trap_mask = bb_strtoull(optarg, &optarg, 16); 10659 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
10560 if (empty_trap_mask != 0) { 10660 if (empty_trap_mask != 0) {
10561 IF_HUSH_TRAP(int sig;) 10661 IF_HUSH_TRAP(int sig;)
10562 install_special_sighandlers();
10563# if ENABLE_HUSH_TRAP 10662# if ENABLE_HUSH_TRAP
10564 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); 10663 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10565 for (sig = 1; sig < NSIG; sig++) { 10664 for (sig = 1; sig < NSIG; sig++) {
@@ -10625,7 +10724,9 @@ int hush_main(int argc, char **argv)
10625 G.global_argv[0] = argv[0]; 10724 G.global_argv[0] = argv[0];
10626 10725
10627 /* If we are login shell... */ 10726 /* If we are login shell... */
10628 if (flags & OPT_login) { 10727 if (!G_reexeced_on_NOMMU /* reexeced hush should never be a login shell */
10728 && (flags & OPT_login)
10729 ) {
10629 const char *hp = NULL; 10730 const char *hp = NULL;
10630 HFILE *input; 10731 HFILE *input;
10631 10732
@@ -10633,7 +10734,6 @@ int hush_main(int argc, char **argv)
10633 input = hfopen("/etc/profile"); 10734 input = hfopen("/etc/profile");
10634 run_profile: 10735 run_profile:
10635 if (input != NULL) { 10736 if (input != NULL) {
10636 install_special_sighandlers();
10637 parse_and_run_file(input); 10737 parse_and_run_file(input);
10638 hfclose(input); 10738 hfclose(input);
10639 } 10739 }
@@ -10670,8 +10770,6 @@ int hush_main(int argc, char **argv)
10670 */ 10770 */
10671 char *script; 10771 char *script;
10672 10772
10673 install_special_sighandlers();
10674
10675 G.global_argc--; 10773 G.global_argc--;
10676 G.global_argv++; 10774 G.global_argv++;
10677#if !BB_MMU 10775#if !BB_MMU
@@ -10722,7 +10820,6 @@ int hush_main(int argc, char **argv)
10722 bb_simple_perror_msg_and_die(G.global_argv[0]); 10820 bb_simple_perror_msg_and_die(G.global_argv[0]);
10723 } 10821 }
10724 xfunc_error_retval = 1; 10822 xfunc_error_retval = 1;
10725 install_special_sighandlers();
10726 parse_and_run_file(input); 10823 parse_and_run_file(input);
10727#if ENABLE_FEATURE_CLEAN_UP 10824#if ENABLE_FEATURE_CLEAN_UP
10728 hfclose(input); 10825 hfclose(input);
@@ -10738,126 +10835,86 @@ int hush_main(int argc, char **argv)
10738 10835
10739 /* A shell is interactive if the '-i' flag was given, 10836 /* A shell is interactive if the '-i' flag was given,
10740 * or if all of the following conditions are met: 10837 * or if all of the following conditions are met:
10741 * no -c command 10838 * not -c 'CMD'
10742 * no arguments remaining or the -s flag given 10839 * not running a script (no arguments remaining, or -s flag given)
10743 * standard input is a terminal 10840 * standard input is a terminal
10744 * standard output is a terminal 10841 * standard output is a terminal
10745 * Refer to Posix.2, the description of the 'sh' utility. 10842 * Refer to Posix.2, the description of the 'sh' utility.
10746 */ 10843 */
10747#if ENABLE_HUSH_JOB 10844#if ENABLE_HUSH_INTERACTIVE
10748 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10845 if (!G_reexeced_on_NOMMU
10749 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); 10846 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
10750 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp); 10847 ) {
10751 if (G_saved_tty_pgrp < 0) 10848 /* Try to dup stdin to high fd#, >= 255 */
10752 G_saved_tty_pgrp = 0;
10753
10754 /* try to dup stdin to high fd#, >= 255 */
10755 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10849 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
10756 if (G_interactive_fd < 0) { 10850 if (G_interactive_fd < 0) {
10757 /* try to dup to any fd */ 10851 /* Try to dup to any fd */
10758 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); 10852 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10759 if (G_interactive_fd < 0) { 10853 if (G_interactive_fd < 0)
10760 /* give up */ 10854 /* Give up */
10761 G_interactive_fd = 0; 10855 G_interactive_fd = 0;
10762 G_saved_tty_pgrp = 0;
10763 }
10764 } 10856 }
10765 } 10857 debug_printf("interactive_fd:%d\n", G_interactive_fd);
10766 debug_printf("interactive_fd:%d\n", G_interactive_fd); 10858 if (G_interactive_fd) {
10767 if (G_interactive_fd) { 10859// TODO? bash:
10768 if (G_saved_tty_pgrp) { 10860// if interactive but not a login shell, sources ~/.bashrc
10769 /* If we were run as 'hush &', sleep until we are 10861// (--norc turns this off, --rcfile <file> overrides)
10770 * in the foreground (tty pgrp == our pgrp). 10862# if ENABLE_HUSH_JOB
10771 * If we get started under a job aware app (like bash), 10863 /* Can we do job control? */
10772 * make sure we are now in charge so we don't fight over 10864 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10773 * who gets the foreground */ 10865 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
10774 while (1) { 10866 if (G_saved_tty_pgrp < 0)
10775 pid_t shell_pgrp = getpgrp(); 10867 G_saved_tty_pgrp = 0; /* no */
10776 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd); 10868 if (G_saved_tty_pgrp) {
10777 if (G_saved_tty_pgrp == shell_pgrp) 10869 /* If we were run as 'hush &', sleep until we are
10778 break; 10870 * in the foreground (tty pgrp == our pgrp).
10779 /* send TTIN to ourself (should stop us) */ 10871 * If we get started under a job aware app (like bash),
10780 kill(- shell_pgrp, SIGTTIN); 10872 * make sure we are now in charge so we don't fight over
10873 * who gets the foreground */
10874 while (1) {
10875 pid_t shell_pgrp = getpgrp();
10876 if (G_saved_tty_pgrp == shell_pgrp) {
10877/* Often both pgrps here are set to our pid - but not always!
10878 * Example: sh -c 'echo $$; hush; echo FIN'
10879 * Here, the parent shell is not interactive, so it does NOT set up
10880 * a separate process group for its children, and we (hush) initially
10881 * run in parent's process group (until we set up our own a few lines down).
10882 */
10883 //bb_error_msg("process groups tty:%d hush:%d", G_saved_tty_pgrp, shell_pgrp);
10884 break;
10885 }
10886 /* Send TTIN to ourself (should stop us) */
10887 kill(- shell_pgrp, SIGTTIN);
10888 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
10889 }
10781 } 10890 }
10782 }
10783
10784 /* Install more signal handlers */
10785 install_special_sighandlers();
10786
10787 if (G_saved_tty_pgrp) {
10788 /* Set other signals to restore saved_tty_pgrp */
10789 install_fatal_sighandlers();
10790 /* Put ourselves in our own process group
10791 * (bash, too, does this only if ctty is available) */
10792 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10793 /* Grab control of the terminal */
10794 tcsetpgrp(G_interactive_fd, cached_getpid);
10795 }
10796 enable_restore_tty_pgrp_on_exit();
10797
10798# if ENABLE_FEATURE_EDITING
10799 G.line_input_state = new_line_input_t(FOR_SHELL);
10800# if ENABLE_FEATURE_TAB_COMPLETION
10801 G.line_input_state->get_exe_name = hush_command_name;
10802# endif
10803# if EDITING_HAS_sh_get_var
10804 G.line_input_state->sh_get_var = get_local_var_value;
10805# endif
10806# endif 10891# endif
10807# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0 10892 /* Install more signal handlers */
10808 { 10893 install_special_sighandlers();
10809 const char *hp = get_local_var_value("HISTFILE"); 10894# if ENABLE_HUSH_JOB
10810 if (!hp) { 10895 if (G_saved_tty_pgrp) {
10811 hp = get_local_var_value("HOME"); 10896 /* Set fatal signals to restore saved_tty_pgrp */
10812 if (hp) 10897 install_fatal_sighandlers();
10813 hp = concat_path_file(hp, ".hush_history"); 10898 /* (The if() is an optimization: can avoid two redundant syscalls) */
10814 } else { 10899 if (G_saved_tty_pgrp != G.root_pid) {
10815 hp = xstrdup(hp); 10900 /* Put ourselves in our own process group
10816 } 10901 * (bash, too, does this only if ctty is available) */
10817 if (hp) { 10902 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
10818 G.line_input_state->hist_file = hp; 10903 /* Grab control of the terminal */
10819 //set_local_var(xasprintf("HISTFILE=%s", ...)); 10904 tcsetpgrp(G_interactive_fd, G.root_pid);
10905 }
10820 } 10906 }
10821# if ENABLE_FEATURE_SH_HISTFILESIZE
10822 hp = get_local_var_value("HISTFILESIZE");
10823 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10824# endif
10825 }
10826# endif 10907# endif
10827 } else { 10908# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
10828 install_special_sighandlers(); 10909 /* Set (but not export) PS1/2 unless already set */
10829 } 10910 if (!get_local_var_value("PS1"))
10830#elif ENABLE_HUSH_INTERACTIVE 10911 set_local_var_from_halves("PS1", "\\w \\$ ");
10831 /* No job control compiled in, only prompt/line editing */ 10912 if (!get_local_var_value("PS2"))
10832 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 10913 set_local_var_from_halves("PS2", "> ");
10833 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10914# endif
10834 if (G_interactive_fd < 0) { 10915 init_line_editing();
10835 /* try to dup to any fd */
10836 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10837 if (G_interactive_fd < 0)
10838 /* give up */
10839 G_interactive_fd = 0;
10840 }
10841 }
10842 install_special_sighandlers();
10843#else
10844 /* We have interactiveness code disabled */
10845 install_special_sighandlers();
10846#endif
10847 /* bash:
10848 * if interactive but not a login shell, sources ~/.bashrc
10849 * (--norc turns this off, --rcfile <file> overrides)
10850 */
10851 10916
10852 if (G_interactive_fd) { 10917# if !ENABLE_FEATURE_SH_EXTRA_QUIET
10853#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
10854 /* Set (but not export) PS1/2 unless already set */
10855 if (!get_local_var_value("PS1"))
10856 set_local_var_from_halves("PS1", "\\w \\$ ");
10857 if (!get_local_var_value("PS2"))
10858 set_local_var_from_halves("PS2", "> ");
10859#endif
10860 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
10861 /* note: ash and hush share this string */ 10918 /* note: ash and hush share this string */
10862 printf("\n\n%s %s\n" 10919 printf("\n\n%s %s\n"
10863 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") 10920 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
@@ -10865,13 +10922,15 @@ int hush_main(int argc, char **argv)
10865 bb_banner, 10922 bb_banner,
10866 "hush - the humble shell" 10923 "hush - the humble shell"
10867 ); 10924 );
10868 } 10925# endif
10869 } 10926 } /* if become interactive */
10927 } /* if on tty */
10928#endif /* if INTERACTIVE is allowed by build config */
10870 10929
10871 parse_and_run_file(hfopen(NULL)); /* stdin */ 10930 parse_and_run_file(hfopen(NULL)); /* stdin */
10872 10931
10873 final_return: 10932 final_return:
10874 hush_exit(G.last_exitcode); 10933 save_history_run_exit_trap_and_exit(G.last_exitcode);
10875} 10934}
10876 10935
10877/* 10936/*
@@ -11057,19 +11116,19 @@ static int FAST_FUNC builtin_exit(char **argv)
11057 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit" 11116 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11058 */ 11117 */
11059 11118
11060 /* note: EXIT trap is run by hush_exit */ 11119 /* note: EXIT trap is run by save_history_run_exit_trap_and_exit */
11061 argv = skip_dash_dash(argv); 11120 argv = skip_dash_dash(argv);
11062 if (argv[0] == NULL) { 11121 if (argv[0] == NULL) {
11063#if ENABLE_HUSH_TRAP 11122#if ENABLE_HUSH_TRAP
11064 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */ 11123 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11065 hush_exit(G.pre_trap_exitcode); 11124 save_history_run_exit_trap_and_exit(G.pre_trap_exitcode);
11066#endif 11125#endif
11067 hush_exit(G.last_exitcode); 11126 save_history_run_exit_trap_and_exit(G.last_exitcode);
11068 } 11127 }
11069 /* mimic bash: exit 123abc == exit 255 + error msg */ 11128 /* mimic bash: exit 123abc == exit 255 + error msg */
11070 xfunc_error_retval = 255; 11129 xfunc_error_retval = 255;
11071 /* bash: exit -2 == exit 254, no error msg */ 11130 /* bash: exit -2 == exit 254, no error msg */
11072 hush_exit(xatoi(argv[0]) & 0xff); 11131 save_history_run_exit_trap_and_exit(xatoi(argv[0]) & 0xff);
11073} 11132}
11074 11133
11075#if ENABLE_HUSH_TYPE 11134#if ENABLE_HUSH_TYPE
@@ -11175,6 +11234,11 @@ static int FAST_FUNC builtin_read(char **argv)
11175 goto again; 11234 goto again;
11176 } 11235 }
11177 11236
11237 if ((uintptr_t)r == 2) /* -t SEC timeout? */
11238 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
11239 /* The actual value observed with bash 5.2.15: */
11240 return 128 + SIGALRM;
11241
11178 if ((uintptr_t)r > 1) { 11242 if ((uintptr_t)r > 1) {
11179 bb_simple_error_msg(r); 11243 bb_simple_error_msg(r);
11180 r = (char*)(uintptr_t)1; 11244 r = (char*)(uintptr_t)1;
diff --git a/shell/hush_leaktool.sh b/shell/hush_leaktool.sh
index ca35ec144..3edd3df61 100755
--- a/shell/hush_leaktool.sh
+++ b/shell/hush_leaktool.sh
@@ -7,7 +7,7 @@ freelist=`grep 'free 0x' "$output" | cut -d' ' -f2 | sort | uniq | xargs`
7 7
8grep -v free "$output" >"$output.leaked" 8grep -v free "$output" >"$output.leaked"
9 9
10i=8 10i=16
11list= 11list=
12for freed in $freelist; do 12for freed in $freelist; do
13 list="$list -e $freed" 13 list="$list -e $freed"
@@ -15,7 +15,7 @@ for freed in $freelist; do
15 echo Dropping $list 15 echo Dropping $list
16 grep -F -v $list <"$output.leaked" >"$output.temp" 16 grep -F -v $list <"$output.leaked" >"$output.temp"
17 mv "$output.temp" "$output.leaked" 17 mv "$output.temp" "$output.leaked"
18 i=8 18 i=16
19 list= 19 list=
20done 20done
21if test "$list"; then 21if test "$list"; then
@@ -23,3 +23,17 @@ if test "$list"; then
23 grep -F -v $list <"$output.leaked" >"$output.temp" 23 grep -F -v $list <"$output.leaked" >"$output.temp"
24 mv "$output.temp" "$output.leaked" 24 mv "$output.temp" "$output.leaked"
25fi 25fi
26
27# All remaining allocations are on addresses which were never freed.
28# * Sort them by line, grouping together allocations which allocated the same address.
29# A leaky allocation will give many different addresses (because it's never freed,
30# the address can not be reused).
31# * Remove the address (field #4).
32# * Count the allocations per every unique source line and alloc type.
33# * Show largest counts on top.
34cat output.leaked \
35 | sort -u \
36 | cut -d' ' -f1-3 \
37 | uniq -c \
38 | sort -rn \
39>output.leaked.counted_uniq_alloc_address
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right
new file mode 100644
index 000000000..7af73557a
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.right
@@ -0,0 +1 @@
hush: syntax error: missing here document terminator
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests
new file mode 100755
index 000000000..33ccde26b
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF1.tests
@@ -0,0 +1,2 @@
1echo <<
2echo Done:
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right
new file mode 100644
index 000000000..7af73557a
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.right
@@ -0,0 +1 @@
hush: syntax error: missing here document terminator
diff --git a/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests
new file mode 100755
index 000000000..fcda11045
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_syntax_err_no_EOF2.tests
@@ -0,0 +1,2 @@
1echo << >
2echo Done:
diff --git a/shell/hush_test/hush-misc/sig_exitcode.tests b/shell/hush_test/hush-misc/sig_exitcode.tests
index 7879dc854..67b4500f4 100755
--- a/shell/hush_test/hush-misc/sig_exitcode.tests
+++ b/shell/hush_test/hush-misc/sig_exitcode.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1exec 2>&1 4exec 2>&1
2 5
3$THIS_SH -c 'kill -9 $$' 6$THIS_SH -c 'kill -9 $$'
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests
index f9cf6d48c..54120319b 100755
--- a/shell/hush_test/hush-misc/wait1.tests
+++ b/shell/hush_test/hush-misc/wait1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & sleep 1 & wait $! 4sleep 2 & sleep 1 & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait2.tests b/shell/hush_test/hush-misc/wait2.tests
index be20f95a5..60f382c9f 100755
--- a/shell/hush_test/hush-misc/wait2.tests
+++ b/shell/hush_test/hush-misc/wait2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 3 & sleep 2 & sleep 1 4sleep 3 & sleep 2 & sleep 1
2wait $! 5wait $!
3echo $? 6echo $?
diff --git a/shell/hush_test/hush-misc/wait3.tests b/shell/hush_test/hush-misc/wait3.tests
index ac541c3fc..aceed1126 100755
--- a/shell/hush_test/hush-misc/wait3.tests
+++ b/shell/hush_test/hush-misc/wait3.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 2 & (sleep 1;exit 3) & wait $! 4sleep 2 & (sleep 1;exit 3) & wait $!
2echo $? 5echo $?
3jobs 6jobs
diff --git a/shell/hush_test/hush-misc/wait4.tests b/shell/hush_test/hush-misc/wait4.tests
index cc34059ac..c979a38b6 100755
--- a/shell/hush_test/hush-misc/wait4.tests
+++ b/shell/hush_test/hush-misc/wait4.tests
@@ -1,2 +1,5 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 1 | (sleep 1;exit 3) & wait %1 4sleep 1 | (sleep 1;exit 3) & wait %1
2echo Three:$? 5echo Three:$?
diff --git a/shell/hush_test/hush-misc/wait5.tests b/shell/hush_test/hush-misc/wait5.tests
index 1b4762d89..e0ac8c251 100755
--- a/shell/hush_test/hush-misc/wait5.tests
+++ b/shell/hush_test/hush-misc/wait5.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1sleep 0 | (sleep 0;exit 3) & 4sleep 0 | (sleep 0;exit 3) &
2sleep 1 5sleep 1
3echo Zero:$? 6echo Zero:$?
diff --git a/shell/hush_test/hush-misc/wait6.tests b/shell/hush_test/hush-misc/wait6.tests
index c23713199..c09002ab0 100755
--- a/shell/hush_test/hush-misc/wait6.tests
+++ b/shell/hush_test/hush-misc/wait6.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# In bash, "wait $!" extracts correct exitcode even if bg task has already exited 4# In bash, "wait $!" extracts correct exitcode even if bg task has already exited
2# It prints 0, then 3: 5# It prints 0, then 3:
3(sleep 0; exit 3) & sleep 1 6(sleep 0; exit 3) & sleep 1
diff --git a/shell/hush_test/hush-read/read_t.right b/shell/hush_test/hush-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/hush_test/hush-read/read_t.right
+++ b/shell/hush_test/hush-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/hush_test/hush-read/read_t.tests b/shell/hush_test/hush-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/hush_test/hush-read/read_t.tests
+++ b/shell/hush_test/hush-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/hush_test/hush-signals/catch.tests b/shell/hush_test/hush-signals/catch.tests
index d2a21d17e..4b1a08e8f 100755
--- a/shell/hush_test/hush-signals/catch.tests
+++ b/shell/hush_test/hush-signals/catch.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("User defined signal 2" text not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1# avoid ugly warnings about signals not being caught 4# avoid ugly warnings about signals not being caught
2trap ":" USR1 USR2 5trap ":" USR1 USR2
3 6
diff --git a/shell/hush_test/hush-signals/signal1.tests b/shell/hush_test/hush-signals/signal1.tests
index 61943467a..c83fa1254 100755
--- a/shell/hush_test/hush-signals/signal1.tests
+++ b/shell/hush_test/hush-signals/signal1.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1trap "echo got signal" USR1 4trap "echo got signal" USR1
2 5
3for try in 1 2 3 4 5; do 6for try in 1 2 3 4 5; do
diff --git a/shell/hush_test/hush-signals/signal8.tests b/shell/hush_test/hush-signals/signal8.tests
index 731af7477..cd5790164 100755
--- a/shell/hush_test/hush-signals/signal8.tests
+++ b/shell/hush_test/hush-signals/signal8.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1"$THIS_SH" -c ' 4"$THIS_SH" -c '
2exit_func() { 5exit_func() {
3 echo "Removing traps" 6 echo "Removing traps"
diff --git a/shell/hush_test/hush-signals/signal_read2.tests b/shell/hush_test/hush-signals/signal_read2.tests
index eab5b9b5b..11accd5ab 100755
--- a/shell/hush_test/hush-signals/signal_read2.tests
+++ b/shell/hush_test/hush-signals/signal_read2.tests
@@ -1,3 +1,6 @@
1# If job control is disabled, skip the test ("Hangup" not emitted)
2test "`type jobs`" = "jobs is a shell builtin" || exit 77
3
1$THIS_SH -c ' 4$THIS_SH -c '
2(sleep 1; kill -HUP $$) & 5(sleep 1; kill -HUP $$) &
3while true; do 6while true; do
diff --git a/shell/hush_test/hush-signals/subshell.tests b/shell/hush_test/hush-signals/subshell.tests
index d877f2b82..856c922d3 100755
--- a/shell/hush_test/hush-signals/subshell.tests
+++ b/shell/hush_test/hush-signals/subshell.tests
@@ -1,5 +1,8 @@
1# Non-empty traps should be reset in subshell 1# Non-empty traps should be reset in subshell
2 2
3# If job control is disabled, skip the test ("Terminated" text not emitted)
4test "`type jobs`" = "jobs is a shell builtin" || exit 77
5
3# HUP is special in interactive shells 6# HUP is special in interactive shells
4trap '' HUP 7trap '' HUP
5# QUIT is always special 8# QUIT is always special
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 7345fee43..ff29ca0b1 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -67,9 +67,12 @@ do_test()
67# echo Running test: "$x" 67# echo Running test: "$x"
68 echo -n "$1/$x:" 68 echo -n "$1/$x:"
69 ( 69 (
70 "$THIS_SH" "./$x" 2>&1 | \ 70 "$THIS_SH" "./$x" >"$name.xx" 2>&1
71 grep -va "^hush: using fallback suid method$" >"$name.xx"
72 r=$? 71 r=$?
72 # filter !FEATURE_SUID_CONFIG_QUIET message
73 sed -i \
74 -e "/^hush: using fallback suid method$/d" \
75 "$name.xx"
73 # filter C library differences 76 # filter C library differences
74 sed -i \ 77 sed -i \
75 -e "/: invalid option /s:'::g" \ 78 -e "/: invalid option /s:'::g" \
diff --git a/shell/math.h b/shell/math.h
index 439031828..aeb3b93c3 100644
--- a/shell/math.h
+++ b/shell/math.h
@@ -47,7 +47,7 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
47 47
48#if ENABLE_FEATURE_SH_MATH_64 48#if ENABLE_FEATURE_SH_MATH_64
49typedef long long arith_t; 49typedef long long arith_t;
50# define ARITH_FMT "%lld" 50#define ARITH_FMT "%"LL_FMT"d"
51#else 51#else
52typedef long arith_t; 52typedef long arith_t;
53# define ARITH_FMT "%ld" 53# define ARITH_FMT "%ld"
diff --git a/shell/random.c b/shell/random.c
index 56c7c5a3c..ffe0cc937 100644
--- a/shell/random.c
+++ b/shell/random.c
@@ -19,6 +19,17 @@
19# include "random.h" 19# include "random.h"
20# define RAND_BASH_MASK 0x7fff 20# define RAND_BASH_MASK 0x7fff
21 21
22# if ENABLE_FEATURE_PRNG_SHELL
23uint32_t FAST_FUNC
24next_random(random_t *rnd)
25{
26 return full_random(rnd) & RAND_BASH_MASK;
27}
28# undef RAND_BASH_MASK
29# define RAND_BASH_MASK 0xffffffff
30# define next_random full_random
31# endif
32
22#else 33#else
23# include <stdint.h> 34# include <stdint.h>
24# include <unistd.h> 35# include <unistd.h>
diff --git a/shell/random.h b/shell/random.h
index c4eb44c13..75fe0f69f 100644
--- a/shell/random.h
+++ b/shell/random.h
@@ -35,6 +35,9 @@ typedef struct random_t {
35 ((rnd)->galois_LFSR = 0) 35 ((rnd)->galois_LFSR = 0)
36 36
37uint32_t next_random(random_t *rnd) FAST_FUNC; 37uint32_t next_random(random_t *rnd) FAST_FUNC;
38#if ENABLE_FEATURE_PRNG_SHELL
39uint32_t full_random(random_t *rnd) FAST_FUNC;
40#endif
38 41
39POP_SAVED_FUNCTION_VISIBILITY 42POP_SAVED_FUNCTION_VISIBILITY
40 43
diff --git a/shell/shell_common.c b/shell/shell_common.c
index e5c2cefb3..657f0df8f 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -55,14 +55,16 @@ const char* FAST_FUNC
55shell_builtin_read(struct builtin_read_params *params) 55shell_builtin_read(struct builtin_read_params *params)
56{ 56{
57 struct pollfd pfd[1]; 57 struct pollfd pfd[1];
58#define fd (pfd[0].fd) /* -u FD */ 58#define fd (pfd->fd) /* -u FD */
59 unsigned err; 59 unsigned err;
60 unsigned end_ms; /* -t TIMEOUT */ 60 unsigned end_ms; /* -t TIMEOUT */
61 int nchars; /* -n NUM */ 61 int nchars; /* -n NUM */
62 char **pp; 62 char **pp;
63 char *buffer; 63 char *buffer;
64 char delim; 64 char delim;
65#if !ENABLE_PLATFORM_MINGW32
65 struct termios tty, old_tty; 66 struct termios tty, old_tty;
67#endif
66 const char *retval; 68 const char *retval;
67 int bufpos; /* need to be able to hold -1 */ 69 int bufpos; /* need to be able to hold -1 */
68 int startword; 70 int startword;
@@ -142,7 +144,7 @@ shell_builtin_read(struct builtin_read_params *params)
142 * bash seems to ignore -p PROMPT for this use case. 144 * bash seems to ignore -p PROMPT for this use case.
143 */ 145 */
144 int r; 146 int r;
145 pfd[0].events = POLLIN; 147 pfd->events = POLLIN;
146 r = poll(pfd, 1, /*timeout:*/ 0); 148 r = poll(pfd, 1, /*timeout:*/ 0);
147 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */ 149 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
148 return (const char *)(uintptr_t)(r <= 0); 150 return (const char *)(uintptr_t)(r <= 0);
@@ -158,6 +160,7 @@ shell_builtin_read(struct builtin_read_params *params)
158 ifs = defifs; 160 ifs = defifs;
159 161
160 read_flags = params->read_flags; 162 read_flags = params->read_flags;
163#if !ENABLE_PLATFORM_MINGW32
161 if (nchars || (read_flags & BUILTIN_READ_SILENT)) { 164 if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
162 tcgetattr(fd, &tty); 165 tcgetattr(fd, &tty);
163 old_tty = tty; 166 old_tty = tty;
@@ -180,6 +183,7 @@ shell_builtin_read(struct builtin_read_params *params)
180 * Ignoring, it's harmless. */ 183 * Ignoring, it's harmless. */
181 tcsetattr(fd, TCSANOW, &tty); 184 tcsetattr(fd, TCSANOW, &tty);
182 } 185 }
186#endif
183 187
184 retval = (const char *)(uintptr_t)0; 188 retval = (const char *)(uintptr_t)0;
185 startword = 1; 189 startword = 1;
@@ -196,6 +200,7 @@ shell_builtin_read(struct builtin_read_params *params)
196 if ((bufpos & 0xff) == 0) 200 if ((bufpos & 0xff) == 0)
197 buffer = xrealloc(buffer, bufpos + 0x101); 201 buffer = xrealloc(buffer, bufpos + 0x101);
198 202
203 IF_PLATFORM_MINGW32(loop:)
199 timeout = -1; 204 timeout = -1;
200 if (params->opt_t) { 205 if (params->opt_t) {
201 timeout = end_ms - (unsigned)monotonic_ms(); 206 timeout = end_ms - (unsigned)monotonic_ms();
@@ -204,8 +209,8 @@ shell_builtin_read(struct builtin_read_params *params)
204 * 32-bit unix time wrapped (year 2038+). 209 * 32-bit unix time wrapped (year 2038+).
205 */ 210 */
206 if (timeout <= 0) { /* already late? */ 211 if (timeout <= 0) { /* already late? */
207 retval = (const char *)(uintptr_t)1; 212 retval = (const char *)(uintptr_t)2;
208 goto ret; 213 break;
209 } 214 }
210 } 215 }
211 216
@@ -214,14 +219,61 @@ shell_builtin_read(struct builtin_read_params *params)
214 * regardless of SA_RESTART-ness of that signal! 219 * regardless of SA_RESTART-ness of that signal!
215 */ 220 */
216 errno = 0; 221 errno = 0;
217 pfd[0].events = POLLIN; 222 pfd->events = POLLIN;
218//TODO race with a signal arriving just before the poll! 223
219 if (poll(pfd, 1, timeout) <= 0) { 224#if ENABLE_PLATFORM_MINGW32
220 /* timed out, or EINTR */ 225 /* Don't poll if timeout is -1, it hurts performance. The
226 * caution above about interrupts isn't relevant on Windows
227 * where Ctrl-C causes an event, not a signal.
228 */
229 if (timeout >= 0)
230#endif
231 /* test bb_got_signal, then poll(), atomically wrt signals */
232 if (check_got_signal_and_poll(pfd, timeout) <= 0) {
233 /* timed out, or some error */
221 err = errno; 234 err = errno;
235 if (!err) { /* timed out */
236 retval = (const char *)(uintptr_t)2;
237 break;
238 }
222 retval = (const char *)(uintptr_t)1; 239 retval = (const char *)(uintptr_t)1;
223 goto ret; 240 goto ret;
224 } 241 }
242#if ENABLE_PLATFORM_MINGW32
243 if (isatty(fd)) {
244 int64_t key;
245
246 key = windows_read_key(fd, NULL, timeout);
247 if (key == 0x03) {
248 /* ^C pressed */
249 retval = (const char *)(uintptr_t)3;
250 goto ret;
251 }
252 else if (key == -1) {
253 /* timeout */
254 retval = (const char *)(uintptr_t)2;
255 break;
256 } else if (key == 0x1a && bufpos == 0) {
257 /* ^Z at start of buffer */
258 retval = (const char *)(uintptr_t)1;
259 break;
260 } else if (key == '\b') {
261 if (bufpos > 0) {
262 --bufpos;
263 ++nchars;
264 if (!(read_flags & BUILTIN_READ_SILENT)) {
265 console_write("\b \b", 3);
266 }
267 }
268 goto loop;
269 }
270 buffer[bufpos] = key == '\r' ? '\n' : key;
271 if (!(read_flags & BUILTIN_READ_SILENT)) {
272 /* echo input if not in silent mode */
273 console_write(buffer + bufpos, 1);
274 }
275 } else
276#endif
225 if (read(fd, &buffer[bufpos], 1) != 1) { 277 if (read(fd, &buffer[bufpos], 1) != 1) {
226 err = errno; 278 err = errno;
227 retval = (const char *)(uintptr_t)1; 279 retval = (const char *)(uintptr_t)1;
@@ -229,11 +281,37 @@ shell_builtin_read(struct builtin_read_params *params)
229 } 281 }
230 282
231 c = buffer[bufpos]; 283 c = buffer[bufpos];
284#if ENABLE_PLATFORM_MINGW32
285 if (c == '\n') {
286 if (backslash == 2 || (bufpos > 0 && buffer[bufpos - 1] == '\r')) {
287 /* We saw either:
288 * - BS CR LF: remove CR, fall through to ignore escaped LF
289 * and exit BS context.
290 * - CR LF not in BS context: replace CR with LF */
291 buffer[--bufpos] = c;
292 nchars += 1 + (backslash == 2);
293 }
294 } else if (backslash == 2) {
295 /* We saw BS CR ??, keep escaped CR, exit BS context,
296 * process ?? */
297 backslash = 0;
298 }
299#endif
232 if (!(read_flags & BUILTIN_READ_RAW)) { 300 if (!(read_flags & BUILTIN_READ_RAW)) {
233 if (backslash) { 301 if (backslash) {
302#if ENABLE_PLATFORM_MINGW32
303 if (c == '\r') {
304 /* We have BS CR, keep CR for now, might see LF next */
305 backslash = 2;
306 goto put;
307 }
308#endif
234 backslash = 0; 309 backslash = 0;
235 if (c != '\n') 310 if (c != '\n')
236 goto put; 311 goto put;
312#if ENABLE_PLATFORM_MINGW32
313 ++nchars;
314#endif
237 continue; 315 continue;
238 } 316 }
239 if (c == '\\') { 317 if (c == '\\') {
@@ -274,7 +352,7 @@ shell_builtin_read(struct builtin_read_params *params)
274 } 352 }
275 put: 353 put:
276 bufpos++; 354 bufpos++;
277 } while (--nchars); 355 } while (IF_PLATFORM_MINGW32(backslash ||) --nchars);
278 356
279 if (argv[0]) { 357 if (argv[0]) {
280 /* Remove trailing space $IFS chars */ 358 /* Remove trailing space $IFS chars */
@@ -329,8 +407,10 @@ shell_builtin_read(struct builtin_read_params *params)
329 407
330 ret: 408 ret:
331 free(buffer); 409 free(buffer);
410#if !ENABLE_PLATFORM_MINGW32
332 if (read_flags & BUILTIN_READ_SILENT) 411 if (read_flags & BUILTIN_READ_SILENT)
333 tcsetattr(fd, TCSANOW, &old_tty); 412 tcsetattr(fd, TCSANOW, &old_tty);
413#endif
334 414
335 errno = err; 415 errno = err;
336 return retval; 416 return retval;
@@ -339,6 +419,7 @@ shell_builtin_read(struct builtin_read_params *params)
339 419
340/* ulimit builtin */ 420/* ulimit builtin */
341 421
422#if !ENABLE_PLATFORM_MINGW32
342struct limits { 423struct limits {
343 uint8_t cmd; /* RLIMIT_xxx fit into it */ 424 uint8_t cmd; /* RLIMIT_xxx fit into it */
344 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ 425 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
@@ -707,3 +788,9 @@ shell_builtin_ulimit(char **argv)
707 788
708 return EXIT_SUCCESS; 789 return EXIT_SUCCESS;
709} 790}
791#else
792int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM)
793{
794 return 1;
795}
796#endif