diff options
Diffstat (limited to '')
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 | ||
173 | config FEATURE_SH_EMBEDDED_SCRIPTS | 174 | config 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:= | |||
9 | INSERT | 9 | INSERT |
10 | 10 | ||
11 | lib-$(CONFIG_FEATURE_SH_MATH) += math.o | 11 | lib-$(CONFIG_FEATURE_SH_MATH) += math.o |
12 | lib-$(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 | |||
383 | union node; | ||
384 | struct strlist; | ||
385 | struct job; | ||
386 | |||
387 | struct 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 | |||
432 | enum { | ||
433 | FS_OPENHERE, | ||
434 | FS_EVALBACKCMD, | ||
435 | FS_EVALSUBSHELL, | ||
436 | FS_EVALPIPE, | ||
437 | FS_SHELLEXEC | ||
438 | }; | ||
439 | |||
440 | static struct forkshell* forkshell_prepare(struct forkshell *fs); | ||
441 | static void forkshell_init(const char *idstr); | ||
442 | static void *sticky_mem_start, *sticky_mem_end; | ||
443 | static 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 | ||
448 | static void spawn_forkshell(struct forkshell *fs, struct job *jp, | ||
449 | union node *n, int mode); | ||
450 | # if FORKSHELL_DEBUG | ||
451 | static 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 */ |
333 | static const char *const optletters_optnames[] ALIGN_PTR = { | 465 | static 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 { | |||
406 | struct globals_misc { | 554 | struct 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, ...); | |||
547 | static void trace_vprintf(const char *fmt, va_list va); | 744 | static 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 | ||
640 | static struct parsefile basepf; /* top level input file */ | 845 | static struct parsefile basepf; /* top level input file */ |
641 | static struct parsefile *g_parsefile = &basepf; /* current input file */ | 846 | static struct parsefile *g_parsefile = &basepf; /* current input file */ |
847 | #if ENABLE_PLATFORM_POSIX | ||
642 | static char *commandname; /* currently executing command */ | 848 | static 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 | */ |
699 | static void raise_interrupt(void) NORETURN; | 906 | static void raise_interrupt(void) IF_NOT_PLATFORM_MINGW32(NORETURN); |
700 | static void | 907 | static void |
701 | raise_interrupt(void) | 908 | raise_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 | |||
1600 | struct globals_memstack { | 1825 | struct 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. */ | ||
2228 | static char * | ||
2229 | stack_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; | |||
2149 | static void change_realtime(const char *) FAST_FUNC; | 2387 | static void change_realtime(const char *) FAST_FUNC; |
2150 | #endif | 2388 | #endif |
2151 | 2389 | ||
2390 | #if ENABLE_PLATFORM_MINGW32 | ||
2391 | static void FAST_FUNC | ||
2392 | change_terminal_mode(const char *newval UNUSED_PARAM) | ||
2393 | { | ||
2394 | terminal_mode(TRUE); | ||
2395 | } | ||
2396 | |||
2397 | static void clearcmdentry(void); | ||
2398 | static void FAST_FUNC | ||
2399 | change_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 | |||
2152 | static const struct { | 2408 | static 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 | ||
2191 | struct redirtab; | 2453 | struct 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 | ||
2675 | static char * | ||
2676 | fix_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 */ | ||
2714 | static int | ||
2715 | is_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 * | |||
2420 | setvareq(char *s, int flags) | 2741 | setvareq(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 */ | ||
2949 | static void | ||
2950 | setwinxp(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 | */ | ||
2983 | static 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 | |||
2996 | static 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 */ |
2599 | static const char * | 3008 | static const char * |
@@ -2637,7 +3046,7 @@ static const char *pathopt; /* set by padvance */ | |||
2637 | static int | 3046 | static int |
2638 | padvance_magic(const char **path, const char *name, int magic) | 3047 | padvance_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 | ||
2774 | static int | 3188 | static int |
2775 | cdopt(void) | 3189 | cdopt(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) | |||
2795 | static const char * | 3216 | static const char * |
2796 | updatepwd(const char *dir) | 3217 | updatepwd(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 | ||
3538 | static void | ||
3539 | print_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 | |||
2991 | static int FAST_FUNC | 3557 | static int FAST_FUNC |
2992 | pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | 3558 | pwdcmd(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 | |||
3418 | static struct alias **atab; // [ATABSIZE]; | 3988 | static 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 | |||
3424 | static struct alias ** | 3993 | static 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) | |||
3623 | struct procstat { | 4191 | struct 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 | ||
3629 | struct job { | 4202 | struct 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 | ||
3652 | static struct job *makejob(/*union node *,*/ int); | 4227 | static struct job *makejob(/*union node *,*/ int); |
4228 | #if !ENABLE_PLATFORM_MINGW32 | ||
3653 | static int forkshell(struct job *, union node *, int); | 4229 | static int forkshell(struct job *, union node *, int); |
4230 | #endif | ||
3654 | static int waitforjob(struct job *); | 4231 | static int waitforjob(struct job *); |
3655 | 4232 | ||
3656 | #if !JOBS | 4233 | #if !JOBS && !JOBS_WIN32 |
3657 | enum { doing_jobctl = 0 }; | 4234 | enum { doing_jobctl = 0 }; |
3658 | #define setjobctl(on) do {} while (0) | 4235 | #define setjobctl(on) do {} while (0) |
3659 | #else | 4236 | #elif JOBS_WIN32 |
4237 | static smallint doing_jobctl; //references:8 | ||
4238 | #define setjobctl(on) do { if (rootshell) doing_jobctl = on; } while (0) | ||
4239 | #else /* JOBS */ | ||
3660 | static smallint doing_jobctl; //references:8 | 4240 | static smallint doing_jobctl; //references:8 |
3661 | static void setjobctl(int); | 4241 | static 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 |
3946 | static int | 4531 | static int |
3947 | jobno(const struct job *jp) | 4532 | jobno(const struct job *jp) |
3948 | { | 4533 | { |
@@ -3960,7 +4545,9 @@ static struct job * | |||
3960 | getjob(const char *name, int getctl) | 4545 | getjob(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) | |||
4039 | static void | 4630 | static void |
4040 | freejob(struct job *jp) | 4631 | freejob(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 | ||
4143 | static int FAST_FUNC | 4740 | static int FAST_FUNC |
4144 | killcmd(int argc, char **argv) | 4741 | killcmd(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 | ||
4197 | static void | 4802 | static void |
4198 | showpipe(struct job *jp /*, FILE *out*/) | 4803 | showpipe(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 | ||
4907 | static 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 | */ | ||
4925 | static pid_t | ||
4926 | waitpid_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) | |||
4308 | static int | 4972 | static int |
4309 | waitproc(int block, int *status) | 4973 | waitproc(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 | ||
4343 | static int | 5013 | static 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) | |||
4456 | static int | 5130 | static int |
4457 | dowait(int block, struct job *jp) | 5131 | dowait(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 |
4483 | static void | 5166 | static void |
4484 | showjob(struct job *jp, int mode) | 5167 | showjob(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 */ |
5172 | static void closescript(void); | 5862 | static 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 |
5302 | static void | 5993 | static void |
5994 | #if !ENABLE_PLATFORM_MINGW32 | ||
5303 | forkparent(struct job *jp, union node *n, int mode, pid_t pid) | 5995 | forkparent(struct job *jp, union node *n, int mode, pid_t pid) |
5996 | #else | ||
5997 | forkparent(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 */ |
5337 | static int | 6045 | static int |
5338 | forkshell(struct job *jp, union node *n, int mode) | 6046 | forkshell(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 | ||
5446 | static int | 6160 | static int |
5447 | stoppedjobs(void) | 6161 | stoppedjobs(void) |
5448 | { | 6162 | { |
@@ -5461,7 +6175,17 @@ stoppedjobs(void) | |||
5461 | out: | 6175 | out: |
5462 | return retval; | 6176 | return retval; |
5463 | } | 6177 | } |
5464 | 6178 | #else | |
6179 | static int | ||
6180 | stoppedjobs(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 | ||
8662 | static int FAST_FUNC | ||
8663 | ash_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 | ||
8004 | static struct strlist * | 8829 | static struct strlist * |
@@ -8271,14 +9096,40 @@ static int builtinloc = -1; /* index in path of %builtin, or -1 */ | |||
8271 | 9096 | ||
8272 | 9097 | ||
8273 | static void | 9098 | static void |
9099 | #if ENABLE_PLATFORM_MINGW32 | ||
9100 | tryexec(IF_FEATURE_SH_STANDALONE(int applet_no, const char *path, int noexec,) | ||
9101 | const char *cmd, char **argv, char **envp) | ||
9102 | #else | ||
8274 | tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp) | 9103 | tryexec(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 | */ |
8334 | static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; | 9228 | static struct builtincmd *find_builtin(const char *name); |
8335 | static void shellexec(char *prog, char **argv, const char *path, int idx) | 9229 | static void shellexec(char *prog, char **argv, const char *path, int idx, |
9230 | int noexec) NORETURN; | ||
9231 | static 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 */ |
8941 | static void *funcblock; /* block to allocate function from */ | 9901 | static void *funcblock; /* block to allocate function from */ |
8942 | static char *funcstring_end; /* end of block to allocate strings from */ | 9902 | static char *funcstring_end; /* end of block to allocate strings from */ |
9903 | #if ENABLE_PLATFORM_MINGW32 | ||
9904 | static int fs_size; | ||
9905 | static void *fs_start; | ||
9906 | # if FORKSHELL_DEBUG | ||
9907 | static void *fs_funcstring; | ||
9908 | static const char **annot; | ||
9909 | static char *annot_free; | ||
9910 | # endif | ||
9911 | #endif | ||
8943 | 9912 | ||
8944 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { | 9913 | static 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 | ||
9074 | static char * | 10043 | static char * |
9075 | nodeckstrdup(char *s) | 10044 | nodeckstrdup(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 | ||
9081 | static union node *copynode(union node *); | 10054 | static 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 | ||
10067 | static void forkshell_mark_ptr(void *dst, const char *note, int flag) | ||
10068 | #else | ||
10069 | static 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 | |||
9083 | static struct nodelist * | 10117 | static struct nodelist * |
9084 | copynodelist(struct nodelist *lp) | 10118 | copynodelist(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) |
9240 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ | 10311 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ |
9241 | static int skipcount; /* number of levels to skip */ | 10312 | static int skipcount; /* number of levels to skip */ |
10313 | #if ENABLE_PLATFORM_POSIX | ||
9242 | static int loopnest; /* current loop nesting level */ | 10314 | static int loopnest; /* current loop nesting level */ |
10315 | #endif | ||
9243 | static int funcline; /* starting line number of current function, or 0 if not in a function */ | 10316 | static 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 */ |
9246 | static int evalstring(char *s, int flags); | 10319 | static 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 | ||
10381 | static void | ||
10382 | dotrap(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... */ |
9308 | static int evalloop(union node *, int); | 10421 | static int evalloop(union node *, int); |
@@ -9588,19 +10701,36 @@ evalcase(union node *n, int flags) | |||
9588 | static int | 10701 | static int |
9589 | evalsubshell(union node *n, int flags) | 10702 | evalsubshell(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) | |||
9692 | static int | 10823 | static int |
9693 | evalpipe(union node *n, int flags) | 10824 | evalpipe(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 | ||
9824 | struct localvar_list { | 10976 | struct 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; | |||
10155 | static int setcmd(int, char **) FAST_FUNC; | 11321 | static int setcmd(int, char **) FAST_FUNC; |
10156 | static int shiftcmd(int, char **) FAST_FUNC; | 11322 | static int shiftcmd(int, char **) FAST_FUNC; |
10157 | static int timescmd(int, char **) FAST_FUNC; | 11323 | static int timescmd(int, char **) FAST_FUNC; |
11324 | #if ENABLE_PLATFORM_MINGW32 | ||
11325 | static int titlecmd(int, char **) FAST_FUNC; | ||
11326 | #endif | ||
10158 | static int trapcmd(int, char **) FAST_FUNC; | 11327 | static int trapcmd(int, char **) FAST_FUNC; |
10159 | static int umaskcmd(int, char **) FAST_FUNC; | 11328 | static int umaskcmd(int, char **) FAST_FUNC; |
10160 | static int unsetcmd(int, char **) FAST_FUNC; | 11329 | static 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 | |||
10356 | static int | 11529 | static int |
10357 | evalcommand(union node *cmd, int flags) | 11530 | evalcommand(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 | */ | ||
12051 | static inline ssize_t | ||
12052 | nonblock_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 | |||
10834 | static int | 12094 | static int |
10835 | preadfd(void) | 12095 | preadfd(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 | |||
13452 | static const char * | 14766 | static const char * |
13453 | expandstr(const char *ps, int syntax_type) | 14767 | expandstr(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 | ||
15698 | static int FAST_FUNC | ||
15699 | titlecmd(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(¶ms); | 15801 | r = shell_builtin_read(¶ms); |
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 | ||
16024 | static 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 */ |
14552 | static NOINLINE void | 16032 | static NOINLINE void |
14553 | init(void) | 16033 | init(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 | ||
16518 | static void | ||
16519 | forkshell_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 | |||
16540 | static void | ||
16541 | forkshell_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 | |||
16571 | static void | ||
16572 | forkshell_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 | |||
16588 | static void | ||
16589 | forkshell_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 | |||
16612 | static void | ||
16613 | forkshell_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 | |||
16623 | static void | ||
16624 | forkshell_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 | */ | ||
16654 | static void | ||
16655 | reinitvar(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 | |||
16678 | static void | ||
16679 | spawn_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 | |||
16729 | static int align_len(const char *s) | ||
16730 | { | ||
16731 | return s ? SHELL_ALIGN(strlen(s)+1) : 0; | ||
16732 | } | ||
16733 | |||
16734 | struct datasize { | ||
16735 | int funcblocksize; | ||
16736 | int funcstringsize; | ||
16737 | }; | ||
16738 | |||
16739 | #define SLIST_SIZE_BEGIN(name,type) \ | ||
16740 | static struct datasize \ | ||
16741 | name(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) \ | ||
16753 | static type * \ | ||
16754 | name(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 | */ | ||
16775 | SLIST_SIZE_BEGIN(var_size,struct var) | ||
16776 | ds.funcstringsize += align_len(p->var_text); | ||
16777 | SLIST_SIZE_END() | ||
16778 | |||
16779 | SLIST_COPY_BEGIN(var_copy,struct var) | ||
16780 | (*vpp)->var_text = nodeckstrdup(vp->var_text); | ||
16781 | (*vpp)->flags = vp->flags; | ||
16782 | (*vpp)->var_func = NULL; | ||
16783 | SAVE_PTR((*vpp)->var_text, xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"), FREE); | ||
16784 | SLIST_COPY_END() | ||
16785 | |||
16786 | /* | ||
16787 | * struct tblentry | ||
16788 | */ | ||
16789 | static struct datasize | ||
16790 | tblentry_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 | |||
16804 | static struct tblentry * | ||
16805 | tblentry_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 | |||
16840 | static struct datasize | ||
16841 | cmdtable_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 | |||
16850 | static struct tblentry ** | ||
16851 | cmdtable_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 | */ | ||
16868 | SLIST_SIZE_BEGIN(alias_size,struct alias) | ||
16869 | ds.funcstringsize += align_len(p->name); | ||
16870 | ds.funcstringsize += align_len(p->val); | ||
16871 | SLIST_SIZE_END() | ||
16872 | |||
16873 | SLIST_COPY_BEGIN(alias_copy,struct alias) | ||
16874 | (*vpp)->name = nodeckstrdup(vp->name); | ||
16875 | (*vpp)->val = nodeckstrdup(vp->val); | ||
16876 | (*vpp)->flag = vp->flag; | ||
16877 | SAVE_PTR((*vpp)->name, xasprintf("(*vpp)->name '%s'", vp->name ?: "NULL"), FREE); | ||
16878 | SAVE_PTR((*vpp)->val, xasprintf("(*vpp)->val '%s'", vp->val ?: "NULL"), FREE); | ||
16879 | SLIST_COPY_END() | ||
16880 | |||
16881 | static struct datasize | ||
16882 | atab_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 | |||
16891 | static struct alias ** | ||
16892 | atab_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 | */ | ||
16909 | static struct datasize | ||
16910 | argv_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 | |||
16924 | static char ** | ||
16925 | argv_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 | ||
16951 | static struct datasize | ||
16952 | history_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 | |||
16964 | static char ** | ||
16965 | history_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 | */ | ||
16985 | static struct datasize | ||
16986 | procstat_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 | |||
16999 | static struct procstat * | ||
17000 | procstat_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 | */ | ||
17020 | static struct datasize | ||
17021 | jobtab_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 | |||
17031 | static struct job * | ||
17032 | jobtab_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 | */ | ||
17070 | static int | ||
17071 | redirtab_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 | |||
17080 | static struct redirtab * | ||
17081 | redirtab_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 | |||
17100 | static struct datasize | ||
17101 | globals_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 | ||
17118 | static struct globals_var * | ||
17119 | globals_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 | |||
17148 | static struct datasize | ||
17149 | globals_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 | ||
17171 | static struct globals_misc * | ||
17172 | globals_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 | |||
17200 | static struct datasize | ||
17201 | forkshell_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 | |||
17232 | static void | ||
17233 | forkshell_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 | ||
17283 | enum {GVP, GMP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, JOBTAB, FUNCSTRING}; | ||
17284 | |||
17285 | /* fp0 and notes can each be NULL */ | ||
17286 | static void | ||
17287 | forkshell_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 | |||
17396 | static struct forkshell * | ||
17397 | forkshell_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 | ||
17482 | static void | ||
17483 | forkshell_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 | ||
17604 | static void | ||
17605 | sticky_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 | ||| | ||
8 | Whitespace 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 @@ | |||
1 | echo "X:Y:Z:" | (IFS=": " read x y; echo "|$x|$y|") | ||
2 | echo "X:Y:Z" | (IFS=": " read x y; echo "|$x|$y|") | ||
3 | echo "X:Y:" | (IFS=": " read x y; echo "|$x|$y|") | ||
4 | echo "X:Y" | (IFS=": " read x y; echo "|$x|$y|") | ||
5 | echo "X:" | (IFS=": " read x y; echo "|$x|$y|") | ||
6 | echo "X" | (IFS=": " read x y; echo "|$x|$y|") | ||
7 | echo "" | (IFS=": " read x y; echo "|$x|$y|") | ||
8 | echo Whitespace should be trimmed too: | ||
9 | echo "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 @@ | |||
31 | extern char **environ; | 31 | extern char **environ; |
32 | 32 | ||
33 | int | 33 | int |
34 | main (argc, argv) | 34 | main (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 | ||
30 | void strprint(); | 30 | void strprint(char *); |
31 | 31 | ||
32 | int main(int argc, char **argv) | 32 | int 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) | |||
1352 | static void *xxmalloc(int lineno, size_t size) | 1360 | static 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 | } | ||
1366 | static 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 | } |
1358 | static void *xxrealloc(int lineno, void *ptr, size_t size) | 1372 | static 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 | } | ||
1380 | static 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 | } |
1364 | static char *xxstrdup(int lineno, const char *str) | 1388 | static 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 | } | ||
1394 | static 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 | } | ||
1400 | static 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 | } |
1370 | static void xxfree(void *ptr) | 1411 | static 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 | ||
2005 | static void hush_exit(int exitcode) NORETURN; | ||
2006 | |||
2007 | static void restore_ttypgrp_and__exit(void) NORETURN; | ||
2008 | static 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 | */ |
2031 | static void fflush_and__exit(void) NORETURN; | 2063 | static NORETURN void fflush_and__exit(void) |
2032 | static 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 | */ |
2049 | static void sigexit(int sig) NORETURN; | 2076 | static NORETURN void restore_ttypgrp_and_killsig_or__exit(int sig) |
2050 | static 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 | |||
2095 | static 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. */ | 2141 | static const char* FAST_FUNC get_local_var_value(const char *name); |
2103 | static void hush_exit(int exitcode) | 2142 | |
2143 | /* Self-explanatory. | ||
2144 | * Restores tty foreground process group too. | ||
2145 | */ | ||
2146 | static 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 */ | ||
7370 | void re_execute_shell(char ***to_free, const char *s, | ||
7371 | char *g_argv0, char **g_argv, | ||
7372 | char **builtin_argv) NORETURN; | ||
7373 | |||
7374 | static void reset_traps_to_defaults(void) | 7435 | static 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 | ||
7423 | static void re_execute_shell(char ***to_free, const char *s, | 7484 | static 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; | ||
7426 | static 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 |
8279 | static const char * FAST_FUNC hush_command_name(int i) | 8344 | static 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 |
8444 | static void exec_function(char ***to_free, | 8509 | static NORETURN void exec_function( |
8445 | const struct function *funcp, | 8510 | char * *volatile *to_free, |
8446 | char **argv) NORETURN; | ||
8447 | static 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 |
8543 | static void exec_builtin(char ***to_free, | 8606 | static NORETURN void exec_builtin( |
8544 | const struct built_in_command *x, | 8607 | char * *volatile *to_free, |
8545 | char **argv) NORETURN; | ||
8546 | static 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 | ||
8569 | static void execvp_or_die(char **argv) NORETURN; | 8630 | static NORETURN void execvp_or_die(char **argv) |
8570 | static 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 | */ |
8691 | static void pseudo_exec_argv(nommu_save_t *nommu_save, | 8751 | static NORETURN NOINLINE void pseudo_exec_argv( |
8692 | char **argv, int assignment_cnt, | 8752 | volatile nommu_save_t *nommu_save, |
8693 | char **argv_expanded) NORETURN; | ||
8694 | static 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 | */ |
8847 | static void pseudo_exec(nommu_save_t *nommu_save, | 8906 | static NORETURN void pseudo_exec( |
8848 | struct command *command, | 8907 | volatile nommu_save_t *nommu_save, |
8849 | char **argv_expanded) NORETURN; | ||
8850 | static 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 | ||
10255 | static 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 | |||
10198 | static void install_sighandlers(unsigned mask) | 10301 | static 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) | |||
10333 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 10436 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
10334 | int hush_main(int argc, char **argv) | 10437 | int 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 | ||
8 | grep -v free "$output" >"$output.leaked" | 8 | grep -v free "$output" >"$output.leaked" |
9 | 9 | ||
10 | i=8 | 10 | i=16 |
11 | list= | 11 | list= |
12 | for freed in $freelist; do | 12 | for 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= |
20 | done | 20 | done |
21 | if test "$list"; then | 21 | if 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" |
25 | fi | 25 | fi |
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. | ||
34 | cat 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 @@ | |||
1 | echo << | ||
2 | echo 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 @@ | |||
1 | echo << > | ||
2 | echo 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | exec 2>&1 | 4 | exec 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 2 & sleep 1 & wait $! | 4 | sleep 2 & sleep 1 & wait $! |
2 | echo $? | 5 | echo $? |
3 | jobs | 6 | jobs |
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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 3 & sleep 2 & sleep 1 | 4 | sleep 3 & sleep 2 & sleep 1 |
2 | wait $! | 5 | wait $! |
3 | echo $? | 6 | echo $? |
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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 2 & (sleep 1;exit 3) & wait $! | 4 | sleep 2 & (sleep 1;exit 3) & wait $! |
2 | echo $? | 5 | echo $? |
3 | jobs | 6 | jobs |
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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 1 | (sleep 1;exit 3) & wait %1 | 4 | sleep 1 | (sleep 1;exit 3) & wait %1 |
2 | echo Three:$? | 5 | echo 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | sleep 0 | (sleep 0;exit 3) & | 4 | sleep 0 | (sleep 0;exit 3) & |
2 | sleep 1 | 5 | sleep 1 |
3 | echo Zero:$? | 6 | echo 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 | ||
2 | test "`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) | ||
2 | test "`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 |
2 | trap ":" USR1 USR2 | 5 | trap ":" 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | trap "echo got signal" USR1 | 4 | trap "echo got signal" USR1 |
2 | 5 | ||
3 | for try in 1 2 3 4 5; do | 6 | for 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 | ||
2 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
3 | |||
1 | "$THIS_SH" -c ' | 4 | "$THIS_SH" -c ' |
2 | exit_func() { | 5 | exit_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) | ||
2 | test "`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 $$) & |
3 | while true; do | 6 | while 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) | ||
4 | test "`type jobs`" = "jobs is a shell builtin" || exit 77 | ||
5 | |||
3 | # HUP is special in interactive shells | 6 | # HUP is special in interactive shells |
4 | trap '' HUP | 7 | trap '' 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 |
49 | typedef long long arith_t; | 49 | typedef long long arith_t; |
50 | # define ARITH_FMT "%lld" | 50 | #define ARITH_FMT "%"LL_FMT"d" |
51 | #else | 51 | #else |
52 | typedef long arith_t; | 52 | typedef 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 | ||
23 | uint32_t FAST_FUNC | ||
24 | next_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 | ||
37 | uint32_t next_random(random_t *rnd) FAST_FUNC; | 37 | uint32_t next_random(random_t *rnd) FAST_FUNC; |
38 | #if ENABLE_FEATURE_PRNG_SHELL | ||
39 | uint32_t full_random(random_t *rnd) FAST_FUNC; | ||
40 | #endif | ||
38 | 41 | ||
39 | POP_SAVED_FUNCTION_VISIBILITY | 42 | POP_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 | |||
55 | shell_builtin_read(struct builtin_read_params *params) | 55 | shell_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 | ||
342 | struct limits { | 423 | struct 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 | ||
792 | int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM) | ||
793 | { | ||
794 | return 1; | ||
795 | } | ||
796 | #endif | ||