summaryrefslogtreecommitdiff
path: root/shell/ash.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/ash.c')
-rw-r--r--shell/ash.c1701
1 files changed, 1683 insertions, 18 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 924e17f32..b82c51029 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -15,6 +15,21 @@
15 * 15 *
16 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 16 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
17 */ 17 */
18
19/*
20 * MinGW notes
21 *
22 * - Environment variables from Windows will all be turned to uppercase.
23 * - PATH accepts both ; and : as separator, but can't be mixed
24 * - command without ".exe" extension is still understood as executable
25 * - shell scripts on the path are detected by the presence of '#!';
26 * the path to the interpreter is ignored, PATH is searched to find it
27 * - both / and \ are supported in PATH. Usually you must use /
28 * - trap/job does not work
29 * - /dev/null is supported for redirection
30 * - fake $PPID
31 */
32
18//config:config ASH 33//config:config ASH
19//config: bool "ash (78 kb)" 34//config: bool "ash (78 kb)"
20//config: default y 35//config: default y
@@ -148,6 +163,25 @@
148//config: you to run the specified command or builtin, 163//config: you to run the specified command or builtin,
149//config: even when there is a function with the same name. 164//config: even when there is a function with the same name.
150//config: 165//config:
166//config:
167//config:config ASH_NOCONSOLE
168//config: bool "'noconsole' option"
169//config: default y
170//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
171//config: help
172//config: Enable support for the 'noconsole' option, which attempts to
173//config: hide the console normally associated with a command line
174//config: application. This may be useful when running a shell script
175//config: from a GUI application.
176//config:
177//config:config ASH_NOCASEGLOB
178//config: bool "'nocaseglob' option"
179//config: default y
180//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
181//config: help
182//config: Enable support for the 'nocaseglob' option, which allows
183//config: case-insensitive filename globbing.
184//config:
151//config:endif # ash options 185//config:endif # ash options
152 186
153//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 187//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
@@ -284,6 +318,10 @@ typedef long arith_t;
284# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 318# define PIPE_BUF 4096 /* amount of buffering in a pipe */
285#endif 319#endif
286 320
321#if !ENABLE_PLATFORM_MINGW32
322# define is_absolute_path(path) ((path)[0] == '/')
323#endif
324
287#if !BB_MMU 325#if !BB_MMU
288# error "Do not even bother, ash will not run on NOMMU machine" 326# error "Do not even bother, ash will not run on NOMMU machine"
289#endif 327#endif
@@ -301,6 +339,69 @@ typedef long arith_t;
301# define BB_GLOBAL_CONST const 339# define BB_GLOBAL_CONST const
302#endif 340#endif
303 341
342#define FORKSHELL_DEBUG 0
343#if ENABLE_PLATFORM_MINGW32
344union node;
345struct strlist;
346struct job;
347
348struct forkshell {
349 /* filled by forkshell_copy() */
350 struct globals_var *gvp;
351 struct globals_misc *gmp;
352 struct tblentry **cmdtable;
353 /* struct alias **atab; */
354 /* struct parsefile *g_parsefile; */
355 HANDLE hMapFile;
356 char *old_base;
357# if FORKSHELL_DEBUG
358 int nodeptrcount;
359 int funcblocksize;
360 int funcstringsize;
361# endif
362 int size;
363
364 /* type of forkshell */
365 int fpid;
366
367 /* generic data, used by forkshell_child */
368 int mode;
369 int nprocs;
370
371 /* optional data, used by forkshell_child */
372 int flags;
373 int fd[3];
374 union node *n;
375 char **argv;
376 char *path;
377 struct strlist *varlist;
378
379 /* start of data block */
380 char **nodeptr[1];
381};
382
383enum {
384 FS_OPENHERE,
385 FS_EVALBACKCMD,
386 FS_EVALSUBSHELL,
387 FS_EVALPIPE,
388 FS_SHELLEXEC
389};
390
391static struct forkshell* forkshell_prepare(struct forkshell *fs);
392static void forkshell_init(const char *idstr);
393static void forkshell_child(struct forkshell *fs);
394static void sticky_free(void *p);
395# define free(p) sticky_free(p)
396#if !JOBS
397#define spawn_forkshell(fs, jp, n, mode) spawn_forkshell(fs, jp, mode)
398#endif
399static void spawn_forkshell(struct forkshell *fs, struct job *jp,
400 union node *n, int mode);
401# if FORKSHELL_DEBUG
402static void forkshell_print(FILE *fp0, struct forkshell *fs, char **notes);
403# endif
404#endif
304 405
305/* ============ Hash table sizes. Configurable. */ 406/* ============ Hash table sizes. Configurable. */
306 407
@@ -333,6 +434,15 @@ static const char *const optletters_optnames[] = {
333 ,"\0" "nolog" 434 ,"\0" "nolog"
334 ,"\0" "debug" 435 ,"\0" "debug"
335#endif 436#endif
437#if ENABLE_PLATFORM_MINGW32
438 ,"X" "winxp"
439#endif
440#if ENABLE_ASH_NOCONSOLE
441 ,"\0" "noconsole"
442#endif
443#if ENABLE_ASH_NOCASEGLOB
444 ,"\0" "nocaseglob"
445#endif
336}; 446};
337 447
338#define optletters(n) optletters_optnames[n][0] 448#define optletters(n) optletters_optnames[n][0]
@@ -365,15 +475,25 @@ struct globals_misc {
365 int rootpid; /* pid of main shell */ 475 int rootpid; /* pid of main shell */
366 /* shell level: 0 for the main shell, 1 for its children, and so on */ 476 /* shell level: 0 for the main shell, 1 for its children, and so on */
367 int shlvl; 477 int shlvl;
478#if ENABLE_PLATFORM_MINGW32
479 int loopnest; /* current loop nesting level */
480#endif
368#define rootshell (!shlvl) 481#define rootshell (!shlvl)
369 int errlinno; 482 int errlinno;
370 483
371 char *minusc; /* argument to -c option */ 484 char *minusc; /* argument to -c option */
485#if ENABLE_PLATFORM_MINGW32
486 char *dirarg; /* argument to -d option */
487 char *title; /* argument to -t option */
488#endif
372 489
373 char *curdir; // = nullstr; /* current working directory */ 490 char *curdir; // = nullstr; /* current working directory */
374 char *physdir; // = nullstr; /* physical working directory */ 491 char *physdir; // = nullstr; /* physical working directory */
375 492
376 char *arg0; /* value of $0 */ 493 char *arg0; /* value of $0 */
494#if ENABLE_PLATFORM_MINGW32
495 char *commandname;
496#endif
377 497
378 struct jmploc *exception_handler; 498 struct jmploc *exception_handler;
379 499
@@ -413,6 +533,15 @@ struct globals_misc {
413# define nolog optlist[14 + BASH_PIPEFAIL] 533# define nolog optlist[14 + BASH_PIPEFAIL]
414# define debug optlist[15 + BASH_PIPEFAIL] 534# define debug optlist[15 + BASH_PIPEFAIL]
415#endif 535#endif
536#if ENABLE_PLATFORM_MINGW32
537# define winxp optlist[14 + BASH_PIPEFAIL + 2*DEBUG]
538#endif
539#if ENABLE_ASH_NOCONSOLE
540# define noconsole optlist[15 + BASH_PIPEFAIL + 2*DEBUG]
541#endif
542#if ENABLE_ASH_NOCASEGLOB
543# define nocaseglob optlist[15 + BASH_PIPEFAIL + 2*DEBUG + ENABLE_ASH_NOCONSOLE]
544#endif
416 545
417 /* trap handler commands */ 546 /* trap handler commands */
418 /* 547 /*
@@ -446,10 +575,20 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
446#define rootpid (G_misc.rootpid ) 575#define rootpid (G_misc.rootpid )
447#define shlvl (G_misc.shlvl ) 576#define shlvl (G_misc.shlvl )
448#define errlinno (G_misc.errlinno ) 577#define errlinno (G_misc.errlinno )
578#if ENABLE_PLATFORM_MINGW32
579#define loopnest (G_misc.loopnest )
580#endif
449#define minusc (G_misc.minusc ) 581#define minusc (G_misc.minusc )
582#if ENABLE_PLATFORM_MINGW32
583#define dirarg (G_misc.dirarg )
584#define title (G_misc.title )
585#endif
450#define curdir (G_misc.curdir ) 586#define curdir (G_misc.curdir )
451#define physdir (G_misc.physdir ) 587#define physdir (G_misc.physdir )
452#define arg0 (G_misc.arg0 ) 588#define arg0 (G_misc.arg0 )
589#if ENABLE_PLATFORM_MINGW32
590#define commandname (G_misc.commandname)
591#endif
453#define exception_handler (G_misc.exception_handler) 592#define exception_handler (G_misc.exception_handler)
454#define exception_type (G_misc.exception_type ) 593#define exception_type (G_misc.exception_type )
455#define suppress_int (G_misc.suppress_int ) 594#define suppress_int (G_misc.suppress_int )
@@ -1322,7 +1461,9 @@ struct parsefile {
1322 1461
1323static struct parsefile basepf; /* top level input file */ 1462static struct parsefile basepf; /* top level input file */
1324static struct parsefile *g_parsefile = &basepf; /* current input file */ 1463static struct parsefile *g_parsefile = &basepf; /* current input file */
1464#if ENABLE_PLATFORM_POSIX
1325static char *commandname; /* currently executing command */ 1465static char *commandname; /* currently executing command */
1466#endif
1326 1467
1327 1468
1328/* ============ Message printing */ 1469/* ============ Message printing */
@@ -2327,6 +2468,42 @@ bltinlookup(const char *name)
2327 return lookupvar(name); 2468 return lookupvar(name);
2328} 2469}
2329 2470
2471#if ENABLE_PLATFORM_MINGW32
2472static char *
2473fix_pathvar(const char *path, int len)
2474{
2475 char *newpath = xstrdup(path);
2476 char *p;
2477 int modified = FALSE;
2478
2479 p = newpath + len;
2480 while (*p) {
2481 if (*p != ':' && *p != ';') {
2482 /* skip drive */
2483 if (isalpha(*p) && p[1] == ':')
2484 p += 2;
2485 /* skip through path component */
2486 for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2487 continue;
2488 }
2489 /* *p is ':', ';' or '\0' here */
2490 if (*p == ':') {
2491 *p++ = ';';
2492 modified = TRUE;
2493 }
2494 else if (*p == ';') {
2495 ++p;
2496 }
2497 }
2498
2499 if (!modified) {
2500 free(newpath);
2501 newpath = NULL;
2502 }
2503 return newpath;
2504}
2505#endif
2506
2330/* 2507/*
2331 * Same as setvar except that the variable and value are passed in 2508 * Same as setvar except that the variable and value are passed in
2332 * the first argument as name=value. Since the first argument will 2509 * the first argument as name=value. Since the first argument will
@@ -2339,6 +2516,27 @@ setvareq(char *s, int flags)
2339{ 2516{
2340 struct var *vp, **vpp; 2517 struct var *vp, **vpp;
2341 2518
2519#if ENABLE_PLATFORM_MINGW32
2520 const char *paths = "PATH=\0""CDPATH=\0""MANPATH=\0";
2521 const char *p;
2522 int len;
2523
2524 for (p = paths; *p; p += len + 1) {
2525 len = strlen(p);
2526 if (strncmp(s, p, len) == 0) {
2527 char *newpath = fix_pathvar(s, len);
2528 if (newpath) {
2529 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2530 free(s);
2531 flags |= VNOSAVE;
2532 flags &= ~(VTEXTFIXED|VSTACK);
2533 s = newpath;
2534 }
2535 break;
2536 }
2537 }
2538#endif
2539
2342 vpp = hashvar(s); 2540 vpp = hashvar(s);
2343 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2541 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2344 vpp = findvar(vpp, s); 2542 vpp = findvar(vpp, s);
@@ -2555,10 +2753,12 @@ path_advance(const char **path, const char *name)
2555 if (*path == NULL) 2753 if (*path == NULL)
2556 return NULL; 2754 return NULL;
2557 start = *path; 2755 start = *path;
2558 for (p = start; *p && *p != ':' && *p != '%'; p++) 2756 for (p = start; *p && *p != PATH_SEP && *p != '%'; p++)
2559 continue; 2757 continue;
2560 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 2758 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2561 while (stackblocksize() < len) 2759
2760 /* reserve space for suffix on WIN32 */
2761 while (stackblocksize() < (ENABLE_PLATFORM_MINGW32 ? len+4 : len))
2562 growstackblock(); 2762 growstackblock();
2563 q = stackblock(); 2763 q = stackblock();
2564 if (p != start) { 2764 if (p != start) {
@@ -2569,10 +2769,10 @@ path_advance(const char **path, const char *name)
2569 pathopt = NULL; 2769 pathopt = NULL;
2570 if (*p == '%') { 2770 if (*p == '%') {
2571 pathopt = ++p; 2771 pathopt = ++p;
2572 while (*p && *p != ':') 2772 while (*p && *p != PATH_SEP)
2573 p++; 2773 p++;
2574 } 2774 }
2575 if (*p == ':') 2775 if (*p == PATH_SEP)
2576 *path = p + 1; 2776 *path = p + 1;
2577 else 2777 else
2578 *path = NULL; 2778 *path = NULL;
@@ -2656,6 +2856,7 @@ setprompt_if(smallint do_set, int whichprompt)
2656 2856
2657#define CD_PHYSICAL 1 2857#define CD_PHYSICAL 1
2658#define CD_PRINT 2 2858#define CD_PRINT 2
2859#define CD_PRINT_ALL 4
2659 2860
2660static int 2861static int
2661cdopt(void) 2862cdopt(void)
@@ -2664,7 +2865,14 @@ cdopt(void)
2664 int i, j; 2865 int i, j;
2665 2866
2666 j = 'L'; 2867 j = 'L';
2868#if ENABLE_PLATFORM_MINGW32
2869 while ((i = nextopt("LPa")) != '\0') {
2870 if (i == 'a')
2871 flags |= CD_PRINT_ALL;
2872 else
2873#else
2667 while ((i = nextopt("LP")) != '\0') { 2874 while ((i = nextopt("LP")) != '\0') {
2875#endif
2668 if (i != j) { 2876 if (i != j) {
2669 flags ^= CD_PHYSICAL; 2877 flags ^= CD_PHYSICAL;
2670 j = i; 2878 j = i;
@@ -2681,6 +2889,114 @@ cdopt(void)
2681static const char * 2889static const char *
2682updatepwd(const char *dir) 2890updatepwd(const char *dir)
2683{ 2891{
2892#if ENABLE_PLATFORM_MINGW32
2893# define is_path_sep(x) ((x) == '/' || (x) == '\\')
2894# define is_root(x) (is_path_sep(x[0]) && x[1] == '\0')
2895 /*
2896 * Due to Windows drive notion, getting pwd is a completely
2897 * different thing. Handle it in a separate routine
2898 */
2899
2900 char *new;
2901 char *p;
2902 char *cdcomppath;
2903 const char *lim;
2904 int len = 0;
2905 /*
2906 * There are five cases that make some kind of sense
2907 *
2908 * Absolute paths:
2909 * c:/path
2910 * //host/share
2911 *
2912 * Relative to current drive:
2913 * /path
2914 *
2915 * Relative to current working directory of current drive
2916 * path
2917 *
2918 * Relative to current working directory of other drive
2919 * c:path
2920 */
2921 int absdrive = has_dos_drive_prefix(dir);
2922 int curr_relpath = !absdrive && !is_path_sep(*dir);
2923 int other_relpath = absdrive && !is_path_sep(dir[2]);
2924 int relpath = curr_relpath || other_relpath;
2925
2926 cdcomppath = sstrdup(dir);
2927 STARTSTACKSTR(new);
2928
2929 /* prefix new path with current directory, if required */
2930 if (other_relpath) {
2931 /* c:path */
2932 char buffer[PATH_MAX];
2933
2934 if (get_drive_cwd(dir, buffer, PATH_MAX) == NULL)
2935 return 0;
2936 new = stack_putstr(buffer, new);
2937 }
2938 else if (curr_relpath || (is_root(dir) && unc_root_len(curdir))) {
2939 /* relative path on current drive or explicit root of UNC curdir */
2940 if (curdir == nullstr)
2941 return 0;
2942 new = stack_putstr(curdir, new);
2943 }
2944
2945 new = makestrspace(strlen(dir) + 2, new);
2946
2947 if ( (len=unc_root_len(dir)) || ((len=unc_root_len(curdir)) &&
2948 (is_root(dir) || curr_relpath)) ) {
2949 /* //host/share or path relative to //host/share */
2950 lim = (char *)stackblock() + len;
2951 }
2952 else {
2953 if (absdrive) {
2954 if (!relpath)
2955 new = stack_nputstr(dir, 2, new);
2956 cdcomppath += 2;
2957 dir += 2;
2958 }
2959 lim = (char *)stackblock() + 3;
2960 }
2961
2962 if (relpath) {
2963 if (!is_path_sep(new[-1]))
2964 USTPUTC('/', new);
2965 } else {
2966 USTPUTC('/', new);
2967 cdcomppath ++;
2968 if (is_path_sep(dir[1]) && !is_path_sep(dir[2])) {
2969 USTPUTC('/', new);
2970 cdcomppath++;
2971 }
2972 }
2973 p = strtok(cdcomppath, "/\\");
2974 while (p) {
2975 switch (*p) {
2976 case '.':
2977 if (p[1] == '.' && p[2] == '\0') {
2978 while (new > lim) {
2979 STUNPUTC(new);
2980 if (is_path_sep(new[-1]))
2981 break;
2982 }
2983 break;
2984 }
2985 if (p[1] == '\0')
2986 break;
2987 /* fall through */
2988 default:
2989 new = stack_putstr(p, new);
2990 USTPUTC('/', new);
2991 }
2992 p = strtok(0, "/\\");
2993 }
2994 if (new > lim)
2995 STUNPUTC(new);
2996 *new = 0;
2997 fix_path_case((char *)stackblock());
2998 return stackblock();
2999#else
2684 char *new; 3000 char *new;
2685 char *p; 3001 char *p;
2686 char *cdcomppath; 3002 char *cdcomppath;
@@ -2734,6 +3050,7 @@ updatepwd(const char *dir)
2734 STUNPUTC(new); 3050 STUNPUTC(new);
2735 *new = 0; 3051 *new = 0;
2736 return stackblock(); 3052 return stackblock();
3053#endif
2737} 3054}
2738 3055
2739/* 3056/*
@@ -2828,7 +3145,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2828 } 3145 }
2829 if (!dest) 3146 if (!dest)
2830 dest = nullstr; 3147 dest = nullstr;
2831 if (*dest == '/') 3148 if (is_absolute_path(dest))
2832 goto step6; 3149 goto step6;
2833 if (*dest == '.') { 3150 if (*dest == '.') {
2834 c = dest[1]; 3151 c = dest[1];
@@ -2850,7 +3167,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2850 c = *path; 3167 c = *path;
2851 p = path_advance(&path, dest); 3168 p = path_advance(&path, dest);
2852 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 3169 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2853 if (c && c != ':') 3170 if (c && c != PATH_SEP)
2854 flags |= CD_PRINT; 3171 flags |= CD_PRINT;
2855 docd: 3172 docd:
2856 if (!docd(p, flags)) 3173 if (!docd(p, flags))
@@ -2872,6 +3189,26 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2872 return 0; 3189 return 0;
2873} 3190}
2874 3191
3192#if ENABLE_PLATFORM_MINGW32
3193static void
3194print_all_cwd(void)
3195{
3196 FILE *mnt;
3197 struct mntent *entry;
3198 char buffer[PATH_MAX];
3199
3200 mnt = setmntent(bb_path_mtab_file, "r");
3201 if (mnt) {
3202 while ((entry=getmntent(mnt)) != NULL) {
3203 entry->mnt_dir[2] = '\0';
3204 if (get_drive_cwd(entry->mnt_dir, buffer, PATH_MAX) != NULL)
3205 out1fmt("%s\n", buffer);
3206 }
3207 endmntent(mnt);
3208 }
3209}
3210#endif
3211
2875static int FAST_FUNC 3212static int FAST_FUNC
2876pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 3213pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2877{ 3214{
@@ -2879,6 +3216,12 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2879 const char *dir = curdir; 3216 const char *dir = curdir;
2880 3217
2881 flags = cdopt(); 3218 flags = cdopt();
3219#if ENABLE_PLATFORM_MINGW32
3220 if (flags & CD_PRINT_ALL) {
3221 print_all_cwd();
3222 return 0;
3223 }
3224#endif
2882 if (flags) { 3225 if (flags) {
2883 if (physdir == nullstr) 3226 if (physdir == nullstr)
2884 setpwd(dir, 0); 3227 setpwd(dir, 0);
@@ -3530,6 +3873,9 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3530 */ 3873 */
3531struct procstat { 3874struct procstat {
3532 pid_t ps_pid; /* process id */ 3875 pid_t ps_pid; /* process id */
3876#if ENABLE_PLATFORM_MINGW32
3877 HANDLE ps_proc;
3878#endif
3533 int ps_status; /* last process status from wait() */ 3879 int ps_status; /* last process status from wait() */
3534 char *ps_cmd; /* text of command being run */ 3880 char *ps_cmd; /* text of command being run */
3535}; 3881};
@@ -3558,7 +3904,9 @@ struct job {
3558}; 3904};
3559 3905
3560static struct job *makejob(/*union node *,*/ int); 3906static struct job *makejob(/*union node *,*/ int);
3907#if !ENABLE_PLATFORM_MINGW32
3561static int forkshell(struct job *, union node *, int); 3908static int forkshell(struct job *, union node *, int);
3909#endif
3562static int waitforjob(struct job *); 3910static int waitforjob(struct job *);
3563 3911
3564#if !JOBS 3912#if !JOBS
@@ -3569,6 +3917,7 @@ static smallint doing_jobctl; //references:8
3569static void setjobctl(int); 3917static void setjobctl(int);
3570#endif 3918#endif
3571 3919
3920#if !ENABLE_PLATFORM_MINGW32
3572/* 3921/*
3573 * Ignore a signal. 3922 * Ignore a signal.
3574 */ 3923 */
@@ -3715,6 +4064,10 @@ setsignal(int signo)
3715 4064
3716 sigaction_set(signo, &act); 4065 sigaction_set(signo, &act);
3717} 4066}
4067#else
4068#define setsignal(s)
4069#define ignoresig(s)
4070#endif
3718 4071
3719/* mode flags for set_curjob */ 4072/* mode flags for set_curjob */
3720#define CUR_DELETE 2 4073#define CUR_DELETE 2
@@ -4213,6 +4566,122 @@ sprint_status48(char *s, int status, int sigonly)
4213 return col; 4566 return col;
4214} 4567}
4215 4568
4569#if ENABLE_PLATFORM_MINGW32
4570
4571HANDLE hSIGINT; /* Ctrl-C is pressed */
4572
4573static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
4574{
4575 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
4576 pending_int = 1;
4577 SetEvent(hSIGINT);
4578 return TRUE;
4579 }
4580 return FALSE;
4581}
4582
4583/*
4584 * Windows does not know about parent-child relationship
4585 * They don't support waitpid(-1)
4586 */
4587static pid_t
4588waitpid_child(int *status, int wait_flags)
4589{
4590 struct job *jb;
4591 struct procstat *ps;
4592 int pid_nr = 0;
4593 pid_t *pidlist;
4594 HANDLE *proclist;
4595 pid_t pid = -1;
4596 DWORD win_status, idx;
4597 int i, delay;
4598
4599 for (jb = curjob; jb; jb = jb->prev_job) {
4600 if (jb->state != JOBDONE)
4601 pid_nr += jb->nprocs;
4602 }
4603 if ( pid_nr++ == 0 )
4604 return -1;
4605
4606 pidlist = ckmalloc(sizeof(*pidlist)*pid_nr);
4607 proclist = ckmalloc(sizeof(*proclist)*pid_nr);
4608
4609 pidlist[0] = -1;
4610 proclist[0] = hSIGINT;
4611 pid_nr = 1;
4612 for (jb = curjob; jb; jb = jb->prev_job) {
4613 if (jb->state == JOBDONE)
4614 continue;
4615 ps = jb->ps;
4616 for (i = 0; i < jb->nprocs; ++i) {
4617 if (ps[i].ps_proc) {
4618 pidlist[pid_nr] = ps[i].ps_pid;
4619 proclist[pid_nr++] = ps[i].ps_proc;
4620 }
4621 }
4622 }
4623
4624 if (pid_nr == 1)
4625 goto done;
4626
4627 idx = WaitForMultipleObjects(pid_nr, proclist, FALSE,
4628 wait_flags&WNOHANG ? 1 : INFINITE);
4629 if (idx < pid_nr) {
4630 if (idx == 0) { /* hSIGINT */
4631 ResetEvent(hSIGINT);
4632
4633 ps = curjob->ps;
4634 for (i = 0; i < curjob->nprocs; ++i) {
4635 if (ps[i].ps_proc) {
4636 kill_SIGTERM_by_handle(ps[i].ps_proc, 128+SIGINT);
4637 pid = ps[i].ps_pid; /* remember last valid pid */
4638 }
4639 }
4640
4641 Sleep(200);
4642 delay = FALSE;
4643 for (i = 0; i < curjob->nprocs; ++i) {
4644 DWORD code;
4645 if (ps[i].ps_proc &&
4646 GetExitCodeProcess(ps[i].ps_proc, &code) &&
4647 code == STILL_ACTIVE) {
4648 TerminateProcess(ps[i].ps_proc, 128+SIGINT);
4649 delay = TRUE;
4650 }
4651 }
4652
4653 if (delay)
4654 Sleep(200);
4655 for (i = 0; i < curjob->nprocs; ++i) {
4656 /* mark all pids dead except the one we'll return */
4657 if (ps[i].ps_pid != pid) {
4658 ps[i].ps_status = 128 + SIGINT;
4659 ps[i].ps_pid = -1;
4660 CloseHandle(ps[i].ps_proc);
4661 ps[i].ps_proc = NULL;
4662 }
4663 }
4664
4665 *status = 128 + SIGINT; /* terminated by a signal */
4666 if (iflag)
4667 write(STDOUT_FILENO, "^C", 2);
4668 }
4669 else { /* valid pid index */
4670 GetExitCodeProcess(proclist[idx], &win_status);
4671 *status = (int)win_status << 8;
4672 pid = pidlist[idx];
4673 }
4674 }
4675 done:
4676 free(pidlist);
4677 free(proclist);
4678 return pid;
4679}
4680#define waitpid(p, s, f) waitpid_child(s, f)
4681#define wait_block_or_sig(s) waitpid_child(s, 0)
4682
4683#else
4684
4216static int 4685static int
4217wait_block_or_sig(int *status) 4686wait_block_or_sig(int *status)
4218{ 4687{
@@ -4245,6 +4714,7 @@ wait_block_or_sig(int *status)
4245 4714
4246 return pid; 4715 return pid;
4247} 4716}
4717#endif
4248 4718
4249#define DOWAIT_NONBLOCK 0 4719#define DOWAIT_NONBLOCK 0
4250#define DOWAIT_BLOCK 1 4720#define DOWAIT_BLOCK 1
@@ -4320,6 +4790,11 @@ dowait(int block, struct job *job)
4320 jobno(jp), pid, ps->ps_status, status)); 4790 jobno(jp), pid, ps->ps_status, status));
4321 ps->ps_status = status; 4791 ps->ps_status = status;
4322 thisjob = jp; 4792 thisjob = jp;
4793#if ENABLE_PLATFORM_MINGW32
4794 ps->ps_pid = -1;
4795 CloseHandle(ps->ps_proc);
4796 ps->ps_proc = NULL;
4797#endif
4323 } 4798 }
4324 if (ps->ps_status == -1) 4799 if (ps->ps_status == -1)
4325 jobstate = JOBRUNNING; 4800 jobstate = JOBRUNNING;
@@ -5036,6 +5511,7 @@ commandtext(union node *n)
5036 * 5511 *
5037 * Called with interrupts off. 5512 * Called with interrupts off.
5038 */ 5513 */
5514#if !ENABLE_PLATFORM_MINGW32
5039/* 5515/*
5040 * Clear traps on a fork. 5516 * Clear traps on a fork.
5041 */ 5517 */
@@ -5185,16 +5661,24 @@ forkchild(struct job *jp, union node *n, int mode)
5185 freejob(jp); 5661 freejob(jp);
5186 jobless = 0; 5662 jobless = 0;
5187} 5663}
5664#endif
5188 5665
5189/* Called after fork(), in parent */ 5666/* Called after fork(), in parent */
5190#if !JOBS 5667#if !JOBS
5191#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) 5668#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5192#endif 5669#endif
5193static void 5670static void
5671#if !ENABLE_PLATFORM_MINGW32
5194forkparent(struct job *jp, union node *n, int mode, pid_t pid) 5672forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5673#else
5674forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
5675#endif
5195{ 5676{
5677#if ENABLE_PLATFORM_MINGW32
5678 pid_t pid = GetProcessId(proc);
5679#endif
5196 TRACE(("In parent shell: child = %d\n", pid)); 5680 TRACE(("In parent shell: child = %d\n", pid));
5197 if (!jp) { 5681 if (!jp && !ENABLE_PLATFORM_MINGW32) { /* FIXME not quite understand this */
5198 /* jp is NULL when called by openhere() for heredoc support */ 5682 /* jp is NULL when called by openhere() for heredoc support */
5199 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) 5683 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
5200 continue; 5684 continue;
@@ -5220,6 +5704,9 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5220 if (jp) { 5704 if (jp) {
5221 struct procstat *ps = &jp->ps[jp->nprocs++]; 5705 struct procstat *ps = &jp->ps[jp->nprocs++];
5222 ps->ps_pid = pid; 5706 ps->ps_pid = pid;
5707#if ENABLE_PLATFORM_MINGW32
5708 ps->ps_proc = proc;
5709#endif
5223 ps->ps_status = -1; 5710 ps->ps_status = -1;
5224 ps->ps_cmd = nullstr; 5711 ps->ps_cmd = nullstr;
5225#if JOBS 5712#if JOBS
@@ -5229,6 +5716,7 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5229 } 5716 }
5230} 5717}
5231 5718
5719#if !ENABLE_PLATFORM_MINGW32
5232/* jp and n are NULL when called by openhere() for heredoc support */ 5720/* jp and n are NULL when called by openhere() for heredoc support */
5233static int 5721static int
5234forkshell(struct job *jp, union node *n, int mode) 5722forkshell(struct job *jp, union node *n, int mode)
@@ -5251,6 +5739,7 @@ forkshell(struct job *jp, union node *n, int mode)
5251 } 5739 }
5252 return pid; 5740 return pid;
5253} 5741}
5742#endif
5254 5743
5255/* 5744/*
5256 * Wait for job to finish. 5745 * Wait for job to finish.
@@ -5382,6 +5871,7 @@ openhere(union node *redir)
5382{ 5871{
5383 int pip[2]; 5872 int pip[2];
5384 size_t len = 0; 5873 size_t len = 0;
5874 IF_PLATFORM_MINGW32(struct forkshell fs);
5385 5875
5386 if (pipe(pip) < 0) 5876 if (pipe(pip) < 0)
5387 ash_msg_and_raise_perror("can't create pipe"); 5877 ash_msg_and_raise_perror("can't create pipe");
@@ -5392,6 +5882,14 @@ openhere(union node *redir)
5392 goto out; 5882 goto out;
5393 } 5883 }
5394 } 5884 }
5885#if ENABLE_PLATFORM_MINGW32
5886 memset(&fs, 0, sizeof(fs));
5887 fs.fpid = FS_OPENHERE;
5888 fs.n = redir;
5889 fs.fd[0] = pip[0];
5890 fs.fd[1] = pip[1];
5891 spawn_forkshell(&fs, NULL, NULL, FORK_NOJOB);
5892#else
5395 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { 5893 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
5396 /* child */ 5894 /* child */
5397 close(pip[0]); 5895 close(pip[0]);
@@ -5406,6 +5904,7 @@ openhere(union node *redir)
5406 expandhere(redir->nhere.doc, pip[1]); 5904 expandhere(redir->nhere.doc, pip[1]);
5407 _exit(EXIT_SUCCESS); 5905 _exit(EXIT_SUCCESS);
5408 } 5906 }
5907#endif
5409 out: 5908 out:
5410 close(pip[1]); 5909 close(pip[1]);
5411 return pip[0]; 5910 return pip[0];
@@ -5483,6 +5982,9 @@ openredirect(union node *redir)
5483 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); 5982 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5484 if (f < 0) 5983 if (f < 0)
5485 goto ecreate; 5984 goto ecreate;
5985#if ENABLE_PLATFORM_MINGW32
5986 lseek(f, 0, SEEK_END);
5987#endif
5486 break; 5988 break;
5487 } 5989 }
5488 5990
@@ -6462,6 +6964,7 @@ evalbackcmd(union node *n, struct backcmd *result)
6462{ 6964{
6463 int pip[2]; 6965 int pip[2];
6464 struct job *jp; 6966 struct job *jp;
6967 IF_PLATFORM_MINGW32(struct forkshell fs);
6465 6968
6466 result->fd = -1; 6969 result->fd = -1;
6467 result->buf = NULL; 6970 result->buf = NULL;
@@ -6474,6 +6977,14 @@ evalbackcmd(union node *n, struct backcmd *result)
6474 if (pipe(pip) < 0) 6977 if (pipe(pip) < 0)
6475 ash_msg_and_raise_perror("can't create pipe"); 6978 ash_msg_and_raise_perror("can't create pipe");
6476 jp = makejob(/*n,*/ 1); 6979 jp = makejob(/*n,*/ 1);
6980#if ENABLE_PLATFORM_MINGW32
6981 memset(&fs, 0, sizeof(fs));
6982 fs.fpid = FS_EVALBACKCMD;
6983 fs.n = n;
6984 fs.fd[0] = pip[0];
6985 fs.fd[1] = pip[1];
6986 spawn_forkshell(&fs, jp, n, FORK_NOJOB);
6987#else
6477 if (forkshell(jp, n, FORK_NOJOB) == 0) { 6988 if (forkshell(jp, n, FORK_NOJOB) == 0) {
6478 /* child */ 6989 /* child */
6479 FORCE_INT_ON; 6990 FORCE_INT_ON;
@@ -6496,6 +7007,7 @@ evalbackcmd(union node *n, struct backcmd *result)
6496 evaltreenr(n, EV_EXIT); 7007 evaltreenr(n, EV_EXIT);
6497 /* NOTREACHED */ 7008 /* NOTREACHED */
6498 } 7009 }
7010#endif
6499 /* parent */ 7011 /* parent */
6500 close(pip[1]); 7012 close(pip[1]);
6501 result->fd = pip[0]; 7013 result->fd = pip[0];
@@ -6552,7 +7064,8 @@ expbackq(union node *cmd, int flag)
6552 7064
6553 /* Eat all trailing newlines */ 7065 /* Eat all trailing newlines */
6554 dest = expdest; 7066 dest = expdest;
6555 for (; dest > (char *)stackblock() && dest[-1] == '\n';) 7067 for (; dest > (char *)stackblock() && (dest[-1] == '\n' ||
7068 (ENABLE_PLATFORM_MINGW32 && dest[-1] == '\r'));)
6556 STUNPUTC(dest); 7069 STUNPUTC(dest);
6557 expdest = dest; 7070 expdest = dest;
6558 7071
@@ -7752,6 +8265,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7752 while (!pending_int && (dp = readdir(dirp)) != NULL) { 8265 while (!pending_int && (dp = readdir(dirp)) != NULL) {
7753 if (dp->d_name[0] == '.' && !matchdot) 8266 if (dp->d_name[0] == '.' && !matchdot)
7754 continue; 8267 continue;
8268#if ENABLE_ASH_NOCASEGLOB
8269# undef pmatch
8270# define pmatch(a, b) !fnmatch((a), (b), nocaseglob ? FNM_CASEFOLD : 0)
8271#endif
7755 if (pmatch(start, dp->d_name)) { 8272 if (pmatch(start, dp->d_name)) {
7756 if (atend) { 8273 if (atend) {
7757 strcpy(enddir, dp->d_name); 8274 strcpy(enddir, dp->d_name);
@@ -7781,6 +8298,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7781 endname[-esc - 1] = esc ? '\\' : '/'; 8298 endname[-esc - 1] = esc ? '\\' : '/';
7782#undef expdir 8299#undef expdir
7783#undef expdir_max 8300#undef expdir_max
8301#if ENABLE_ASH_NOCASEGLOB
8302# undef pmatch
8303# define pmatch(a, b) !fnmatch((a), (b), 0)
8304#endif
7784} 8305}
7785 8306
7786static struct strlist * 8307static struct strlist *
@@ -8075,6 +8596,11 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8075 } 8596 }
8076#endif 8597#endif
8077 8598
8599#if ENABLE_PLATFORM_MINGW32
8600 cmd = auto_win32_extension(cmd) ?: cmd;
8601 execve(cmd, argv, envp);
8602 /* skip POSIX-mandated retry on ENOEXEC */
8603#else
8078 repeat: 8604 repeat:
8079#ifdef SYSV 8605#ifdef SYSV
8080 do { 8606 do {
@@ -8110,6 +8636,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8110 argv[0] = (char*) "ash"; 8636 argv[0] = (char*) "ash";
8111 goto repeat; 8637 goto repeat;
8112 } 8638 }
8639#endif /* ENABLE_PLATFORM_MINGW32 */
8113} 8640}
8114 8641
8115/* 8642/*
@@ -8127,7 +8654,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8127 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ 8654 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
8128 8655
8129 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 8656 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
8130 if (strchr(prog, '/') != NULL 8657 if ((strchr(prog, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(prog, '\\')))
8131#if ENABLE_FEATURE_SH_STANDALONE 8658#if ENABLE_FEATURE_SH_STANDALONE
8132 || (applet_no = find_applet_by_name(prog)) >= 0 8659 || (applet_no = find_applet_by_name(prog)) >= 0
8133#endif 8660#endif
@@ -8186,6 +8713,9 @@ printentry(struct tblentry *cmdp)
8186 name = path_advance(&path, cmdp->cmdname); 8713 name = path_advance(&path, cmdp->cmdname);
8187 stunalloc(name); 8714 stunalloc(name);
8188 } while (--idx >= 0); 8715 } while (--idx >= 0);
8716#if ENABLE_PLATFORM_MINGW32
8717 add_win32_extension(name);
8718#endif
8189 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); 8719 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8190} 8720}
8191 8721
@@ -8382,8 +8912,8 @@ changepath(const char *new)
8382 for (;;) { 8912 for (;;) {
8383 if (*old != *new) { 8913 if (*old != *new) {
8384 firstchange = idx; 8914 firstchange = idx;
8385 if ((*old == '\0' && *new == ':') 8915 if ((*old == '\0' && *new == PATH_SEP)
8386 || (*old == ':' && *new == '\0') 8916 || (*old == PATH_SEP && *new == '\0')
8387 ) { 8917 ) {
8388 firstchange++; 8918 firstchange++;
8389 } 8919 }
@@ -8393,7 +8923,7 @@ changepath(const char *new)
8393 break; 8923 break;
8394 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) 8924 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
8395 idx_bltin = idx; 8925 idx_bltin = idx;
8396 if (*new == ':') 8926 if (*new == PATH_SEP)
8397 idx++; 8927 idx++;
8398 new++; 8928 new++;
8399 old++; 8929 old++;
@@ -8582,6 +9112,9 @@ describe_command(char *command, const char *path, int describe_command_verbose)
8582 stunalloc(p); 9112 stunalloc(p);
8583 } while (--j >= 0); 9113 } while (--j >= 0);
8584 } 9114 }
9115#if ENABLE_PLATFORM_MINGW32
9116 add_win32_extension(p);
9117#endif
8585 if (describe_command_verbose) { 9118 if (describe_command_verbose) {
8586 out1fmt(" is %s", p); 9119 out1fmt(" is %s", p);
8587 } else { 9120 } else {
@@ -8716,6 +9249,14 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8716/*static int funcstringsize; // size of strings in node */ 9249/*static int funcstringsize; // size of strings in node */
8717static void *funcblock; /* block to allocate function from */ 9250static void *funcblock; /* block to allocate function from */
8718static char *funcstring_end; /* end of block to allocate strings from */ 9251static char *funcstring_end; /* end of block to allocate strings from */
9252#if ENABLE_PLATFORM_MINGW32
9253static int nodeptrcount;
9254static char ***nodeptr;
9255# if FORKSHELL_DEBUG
9256static int annot_count;
9257static char **annot;
9258# endif
9259#endif
8719 9260
8720static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 9261static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8721 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 9262 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -8756,6 +9297,7 @@ sizenodelist(int funcblocksize, struct nodelist *lp)
8756{ 9297{
8757 while (lp) { 9298 while (lp) {
8758 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); 9299 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
9300 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8759 funcblocksize = calcsize(funcblocksize, lp->n); 9301 funcblocksize = calcsize(funcblocksize, lp->n);
8760 lp = lp->next; 9302 lp = lp->next;
8761 } 9303 }
@@ -8773,15 +9315,18 @@ calcsize(int funcblocksize, union node *n)
8773 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect); 9315 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect);
8774 funcblocksize = calcsize(funcblocksize, n->ncmd.args); 9316 funcblocksize = calcsize(funcblocksize, n->ncmd.args);
8775 funcblocksize = calcsize(funcblocksize, n->ncmd.assign); 9317 funcblocksize = calcsize(funcblocksize, n->ncmd.assign);
9318 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8776 break; 9319 break;
8777 case NPIPE: 9320 case NPIPE:
8778 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist); 9321 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist);
9322 IF_PLATFORM_MINGW32(nodeptrcount++);
8779 break; 9323 break;
8780 case NREDIR: 9324 case NREDIR:
8781 case NBACKGND: 9325 case NBACKGND:
8782 case NSUBSHELL: 9326 case NSUBSHELL:
8783 funcblocksize = calcsize(funcblocksize, n->nredir.redirect); 9327 funcblocksize = calcsize(funcblocksize, n->nredir.redirect);
8784 funcblocksize = calcsize(funcblocksize, n->nredir.n); 9328 funcblocksize = calcsize(funcblocksize, n->nredir.n);
9329 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8785 break; 9330 break;
8786 case NAND: 9331 case NAND:
8787 case NOR: 9332 case NOR:
@@ -8790,34 +9335,41 @@ calcsize(int funcblocksize, union node *n)
8790 case NUNTIL: 9335 case NUNTIL:
8791 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2); 9336 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2);
8792 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1); 9337 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1);
9338 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8793 break; 9339 break;
8794 case NIF: 9340 case NIF:
8795 funcblocksize = calcsize(funcblocksize, n->nif.elsepart); 9341 funcblocksize = calcsize(funcblocksize, n->nif.elsepart);
8796 funcblocksize = calcsize(funcblocksize, n->nif.ifpart); 9342 funcblocksize = calcsize(funcblocksize, n->nif.ifpart);
8797 funcblocksize = calcsize(funcblocksize, n->nif.test); 9343 funcblocksize = calcsize(funcblocksize, n->nif.test);
9344 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8798 break; 9345 break;
8799 case NFOR: 9346 case NFOR:
8800 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */ 9347 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */
8801 funcblocksize = calcsize(funcblocksize, n->nfor.body); 9348 funcblocksize = calcsize(funcblocksize, n->nfor.body);
8802 funcblocksize = calcsize(funcblocksize, n->nfor.args); 9349 funcblocksize = calcsize(funcblocksize, n->nfor.args);
9350 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8803 break; 9351 break;
8804 case NCASE: 9352 case NCASE:
8805 funcblocksize = calcsize(funcblocksize, n->ncase.cases); 9353 funcblocksize = calcsize(funcblocksize, n->ncase.cases);
8806 funcblocksize = calcsize(funcblocksize, n->ncase.expr); 9354 funcblocksize = calcsize(funcblocksize, n->ncase.expr);
9355 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8807 break; 9356 break;
8808 case NCLIST: 9357 case NCLIST:
8809 funcblocksize = calcsize(funcblocksize, n->nclist.body); 9358 funcblocksize = calcsize(funcblocksize, n->nclist.body);
8810 funcblocksize = calcsize(funcblocksize, n->nclist.pattern); 9359 funcblocksize = calcsize(funcblocksize, n->nclist.pattern);
8811 funcblocksize = calcsize(funcblocksize, n->nclist.next); 9360 funcblocksize = calcsize(funcblocksize, n->nclist.next);
9361 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8812 break; 9362 break;
8813 case NDEFUN: 9363 case NDEFUN:
8814 funcblocksize = calcsize(funcblocksize, n->ndefun.body); 9364 funcblocksize = calcsize(funcblocksize, n->ndefun.body);
8815 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1); 9365 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
9366 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8816 break; 9367 break;
8817 case NARG: 9368 case NARG:
8818 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote); 9369 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
8819 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */ 9370 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
8820 funcblocksize = calcsize(funcblocksize, n->narg.next); 9371 funcblocksize = calcsize(funcblocksize, n->narg.next);
9372 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8821 break; 9373 break;
8822 case NTO: 9374 case NTO:
8823#if BASH_REDIR_OUTPUT 9375#if BASH_REDIR_OUTPUT
@@ -8829,33 +9381,65 @@ calcsize(int funcblocksize, union node *n)
8829 case NAPPEND: 9381 case NAPPEND:
8830 funcblocksize = calcsize(funcblocksize, n->nfile.fname); 9382 funcblocksize = calcsize(funcblocksize, n->nfile.fname);
8831 funcblocksize = calcsize(funcblocksize, n->nfile.next); 9383 funcblocksize = calcsize(funcblocksize, n->nfile.next);
9384 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8832 break; 9385 break;
8833 case NTOFD: 9386 case NTOFD:
8834 case NFROMFD: 9387 case NFROMFD:
8835 funcblocksize = calcsize(funcblocksize, n->ndup.vname); 9388 funcblocksize = calcsize(funcblocksize, n->ndup.vname);
8836 funcblocksize = calcsize(funcblocksize, n->ndup.next); 9389 funcblocksize = calcsize(funcblocksize, n->ndup.next);
9390 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8837 break; 9391 break;
8838 case NHERE: 9392 case NHERE:
8839 case NXHERE: 9393 case NXHERE:
8840 funcblocksize = calcsize(funcblocksize, n->nhere.doc); 9394 funcblocksize = calcsize(funcblocksize, n->nhere.doc);
8841 funcblocksize = calcsize(funcblocksize, n->nhere.next); 9395 funcblocksize = calcsize(funcblocksize, n->nhere.next);
9396 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8842 break; 9397 break;
8843 case NNOT: 9398 case NNOT:
8844 funcblocksize = calcsize(funcblocksize, n->nnot.com); 9399 funcblocksize = calcsize(funcblocksize, n->nnot.com);
9400 IF_PLATFORM_MINGW32(nodeptrcount++);
8845 break; 9401 break;
8846 }; 9402 };
8847 return funcblocksize; 9403 return funcblocksize;
8848} 9404}
8849 9405
8850static char * 9406static char *
8851nodeckstrdup(char *s) 9407nodeckstrdup(const char *s)
8852{ 9408{
9409#if ENABLE_PLATFORM_MINGW32
9410 if(!s)
9411 return NULL;
9412#endif
8853 funcstring_end -= SHELL_ALIGN(strlen(s) + 1); 9413 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
8854 return strcpy(funcstring_end, s); 9414 return strcpy(funcstring_end, s);
8855} 9415}
8856 9416
8857static union node *copynode(union node *); 9417static union node *copynode(union node *);
8858 9418
9419#if ENABLE_PLATFORM_MINGW32
9420# define SAVE_PTR(dst) {if (nodeptr) *nodeptr++ = (char **)&(dst);}
9421# define SAVE_PTR2(dst1,dst2) {if (nodeptr) { *nodeptr++ = (char **)&(dst1);*nodeptr++ = (char **)&(dst2);}}
9422# define SAVE_PTR3(dst1,dst2,dst3) {if (nodeptr) { *nodeptr++ = (char **)&(dst1);*nodeptr++ = (char **)&(dst2);*nodeptr++ = (char **)&(dst3);}}
9423#else
9424# define SAVE_PTR(dst)
9425# define SAVE_PTR2(dst,dst2)
9426# define SAVE_PTR3(dst,dst2,dst3)
9427#endif
9428
9429#if ENABLE_PLATFORM_MINGW32 && FORKSHELL_DEBUG
9430# define ANNOT_NO_DUP(note) {if (annot) annot[annot_count++] = note;}
9431# define ANNOT(note) {ANNOT_NO_DUP(xstrdup(note));}
9432# define ANNOT2(n1,n2) {ANNOT(n1); ANNOT(n2)}
9433# define ANNOT3(n1,n2,n3) {ANNOT2(n1,n2); ANNOT(n3);}
9434# define ANNOT4(n1,n2,n3,n4) {ANNOT3(n1,n2,n3); ANNOT(n4);}
9435#else
9436# define ANNOT_NO_DUP(note)
9437# define ANNOT(note)
9438# define ANNOT2(n1,n2)
9439# define ANNOT3(n1,n2,n3)
9440# define ANNOT4(n1,n2,n3,n4)
9441#endif
9442
8859static struct nodelist * 9443static struct nodelist *
8860copynodelist(struct nodelist *lp) 9444copynodelist(struct nodelist *lp)
8861{ 9445{
@@ -8867,6 +9451,8 @@ copynodelist(struct nodelist *lp)
8867 *lpp = funcblock; 9451 *lpp = funcblock;
8868 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); 9452 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8869 (*lpp)->n = copynode(lp->n); 9453 (*lpp)->n = copynode(lp->n);
9454 SAVE_PTR2((*lpp)->n, (*lpp)->next);
9455 ANNOT2("(*lpp)->n","(*lpp)->next");
8870 lp = lp->next; 9456 lp = lp->next;
8871 lpp = &(*lpp)->next; 9457 lpp = &(*lpp)->next;
8872 } 9458 }
@@ -8890,10 +9476,14 @@ copynode(union node *n)
8890 new->ncmd.args = copynode(n->ncmd.args); 9476 new->ncmd.args = copynode(n->ncmd.args);
8891 new->ncmd.assign = copynode(n->ncmd.assign); 9477 new->ncmd.assign = copynode(n->ncmd.assign);
8892 new->ncmd.linno = n->ncmd.linno; 9478 new->ncmd.linno = n->ncmd.linno;
9479 SAVE_PTR3(new->ncmd.redirect,new->ncmd.args, new->ncmd.assign);
9480 ANNOT3("ncmd.redirect","ncmd.args","ncmd.assign");
8893 break; 9481 break;
8894 case NPIPE: 9482 case NPIPE:
8895 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); 9483 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8896 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; 9484 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
9485 SAVE_PTR(new->npipe.cmdlist);
9486 ANNOT("npipe.cmdlist");
8897 break; 9487 break;
8898 case NREDIR: 9488 case NREDIR:
8899 case NBACKGND: 9489 case NBACKGND:
@@ -8901,6 +9491,8 @@ copynode(union node *n)
8901 new->nredir.redirect = copynode(n->nredir.redirect); 9491 new->nredir.redirect = copynode(n->nredir.redirect);
8902 new->nredir.n = copynode(n->nredir.n); 9492 new->nredir.n = copynode(n->nredir.n);
8903 new->nredir.linno = n->nredir.linno; 9493 new->nredir.linno = n->nredir.linno;
9494 SAVE_PTR2(new->nredir.redirect,new->nredir.n);
9495 ANNOT2("nredir.redirect","nredir.n");
8904 break; 9496 break;
8905 case NAND: 9497 case NAND:
8906 case NOR: 9498 case NOR:
@@ -8909,37 +9501,54 @@ copynode(union node *n)
8909 case NUNTIL: 9501 case NUNTIL:
8910 new->nbinary.ch2 = copynode(n->nbinary.ch2); 9502 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8911 new->nbinary.ch1 = copynode(n->nbinary.ch1); 9503 new->nbinary.ch1 = copynode(n->nbinary.ch1);
9504 SAVE_PTR2(new->nbinary.ch1,new->nbinary.ch2);
9505 ANNOT2("nbinary.ch1","nbinary.ch2");
8912 break; 9506 break;
8913 case NIF: 9507 case NIF:
8914 new->nif.elsepart = copynode(n->nif.elsepart); 9508 new->nif.elsepart = copynode(n->nif.elsepart);
8915 new->nif.ifpart = copynode(n->nif.ifpart); 9509 new->nif.ifpart = copynode(n->nif.ifpart);
8916 new->nif.test = copynode(n->nif.test); 9510 new->nif.test = copynode(n->nif.test);
9511 SAVE_PTR3(new->nif.elsepart,new->nif.ifpart,new->nif.test);
9512 ANNOT3("nif.elsepart","nif.ifpart","nif.test");
8917 break; 9513 break;
8918 case NFOR: 9514 case NFOR:
8919 new->nfor.var = nodeckstrdup(n->nfor.var); 9515 new->nfor.var = nodeckstrdup(n->nfor.var);
8920 new->nfor.body = copynode(n->nfor.body); 9516 new->nfor.body = copynode(n->nfor.body);
8921 new->nfor.args = copynode(n->nfor.args); 9517 new->nfor.args = copynode(n->nfor.args);
8922 new->nfor.linno = n->nfor.linno; 9518 new->nfor.linno = n->nfor.linno;
9519 SAVE_PTR3(new->nfor.var,new->nfor.body,new->nfor.args);
9520 ANNOT_NO_DUP(xasprintf("nfor.var '%s'", n->nfor.var ?: "NULL"));
9521 ANNOT2("nfor.body","nfor.args");
8923 break; 9522 break;
8924 case NCASE: 9523 case NCASE:
8925 new->ncase.cases = copynode(n->ncase.cases); 9524 new->ncase.cases = copynode(n->ncase.cases);
8926 new->ncase.expr = copynode(n->ncase.expr); 9525 new->ncase.expr = copynode(n->ncase.expr);
8927 new->ncase.linno = n->ncase.linno; 9526 new->ncase.linno = n->ncase.linno;
9527 SAVE_PTR2(new->ncase.cases,new->ncase.expr);
9528 ANNOT2("ncase.cases","ncase.expr");
8928 break; 9529 break;
8929 case NCLIST: 9530 case NCLIST:
8930 new->nclist.body = copynode(n->nclist.body); 9531 new->nclist.body = copynode(n->nclist.body);
8931 new->nclist.pattern = copynode(n->nclist.pattern); 9532 new->nclist.pattern = copynode(n->nclist.pattern);
8932 new->nclist.next = copynode(n->nclist.next); 9533 new->nclist.next = copynode(n->nclist.next);
9534 SAVE_PTR3(new->nclist.body,new->nclist.pattern,new->nclist.next);
9535 ANNOT3("nclist.body","nclist.pattern","nclist.next");
8933 break; 9536 break;
8934 case NDEFUN: 9537 case NDEFUN:
8935 new->ndefun.body = copynode(n->ndefun.body); 9538 new->ndefun.body = copynode(n->ndefun.body);
8936 new->ndefun.text = nodeckstrdup(n->ndefun.text); 9539 new->ndefun.text = nodeckstrdup(n->ndefun.text);
8937 new->ndefun.linno = n->ndefun.linno; 9540 new->ndefun.linno = n->ndefun.linno;
9541 SAVE_PTR2(new->ndefun.body,new->ndefun.text);
9542 ANNOT("ndefun.body");
9543 ANNOT_NO_DUP(xasprintf("ndefun.text '%s'", n->ndefun.text ?: "NULL"));
8938 break; 9544 break;
8939 case NARG: 9545 case NARG:
8940 new->narg.backquote = copynodelist(n->narg.backquote); 9546 new->narg.backquote = copynodelist(n->narg.backquote);
8941 new->narg.text = nodeckstrdup(n->narg.text); 9547 new->narg.text = nodeckstrdup(n->narg.text);
8942 new->narg.next = copynode(n->narg.next); 9548 new->narg.next = copynode(n->narg.next);
9549 SAVE_PTR3(new->narg.backquote,new->narg.text,new->narg.next);
9550 ANNOT2("narg.backquote","narg.next");
9551 ANNOT_NO_DUP(xasprintf("narg.text '%s'", n->narg.text ?: "NULL"));
8943 break; 9552 break;
8944 case NTO: 9553 case NTO:
8945#if BASH_REDIR_OUTPUT 9554#if BASH_REDIR_OUTPUT
@@ -8952,6 +9561,8 @@ copynode(union node *n)
8952 new->nfile.fname = copynode(n->nfile.fname); 9561 new->nfile.fname = copynode(n->nfile.fname);
8953 new->nfile.fd = n->nfile.fd; 9562 new->nfile.fd = n->nfile.fd;
8954 new->nfile.next = copynode(n->nfile.next); 9563 new->nfile.next = copynode(n->nfile.next);
9564 SAVE_PTR2(new->nfile.fname,new->nfile.next);
9565 ANNOT2("nfile.fname","nfile.next");
8955 break; 9566 break;
8956 case NTOFD: 9567 case NTOFD:
8957 case NFROMFD: 9568 case NFROMFD:
@@ -8959,15 +9570,21 @@ copynode(union node *n)
8959 new->ndup.dupfd = n->ndup.dupfd; 9570 new->ndup.dupfd = n->ndup.dupfd;
8960 new->ndup.fd = n->ndup.fd; 9571 new->ndup.fd = n->ndup.fd;
8961 new->ndup.next = copynode(n->ndup.next); 9572 new->ndup.next = copynode(n->ndup.next);
9573 SAVE_PTR2(new->ndup.vname,new->ndup.next);
9574 ANNOT2("ndup.vname","ndup.next");
8962 break; 9575 break;
8963 case NHERE: 9576 case NHERE:
8964 case NXHERE: 9577 case NXHERE:
8965 new->nhere.doc = copynode(n->nhere.doc); 9578 new->nhere.doc = copynode(n->nhere.doc);
8966 new->nhere.fd = n->nhere.fd; 9579 new->nhere.fd = n->nhere.fd;
8967 new->nhere.next = copynode(n->nhere.next); 9580 new->nhere.next = copynode(n->nhere.next);
9581 SAVE_PTR2(new->nhere.doc,new->nhere.next);
9582 ANNOT2("nhere.doc","nhere.next");
8968 break; 9583 break;
8969 case NNOT: 9584 case NNOT:
8970 new->nnot.com = copynode(n->nnot.com); 9585 new->nnot.com = copynode(n->nnot.com);
9586 SAVE_PTR(new->nnot.com);
9587 ANNOT("nnot.com");
8971 break; 9588 break;
8972 }; 9589 };
8973 new->type = n->type; 9590 new->type = n->type;
@@ -8988,6 +9605,7 @@ copyfunc(union node *n)
8988 f = ckzalloc(blocksize /* + funcstringsize */); 9605 f = ckzalloc(blocksize /* + funcstringsize */);
8989 funcblock = (char *) f + offsetof(struct funcnode, n); 9606 funcblock = (char *) f + offsetof(struct funcnode, n);
8990 funcstring_end = (char *) f + blocksize; 9607 funcstring_end = (char *) f + blocksize;
9608 IF_PLATFORM_MINGW32(nodeptr = NULL);
8991 copynode(n); 9609 copynode(n);
8992 /* f->count = 0; - ckzalloc did it */ 9610 /* f->count = 0; - ckzalloc did it */
8993 return f; 9611 return f;
@@ -9014,7 +9632,9 @@ defun(union node *func)
9014#define SKIPFUNC (1 << 2) 9632#define SKIPFUNC (1 << 2)
9015static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 9633static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
9016static int skipcount; /* number of levels to skip */ 9634static int skipcount; /* number of levels to skip */
9635#if ENABLE_PLATFORM_POSIX
9017static int loopnest; /* current loop nesting level */ 9636static int loopnest; /* current loop nesting level */
9637#endif
9018static int funcline; /* starting line number of current function, or 0 if not in a function */ 9638static int funcline; /* starting line number of current function, or 0 if not in a function */
9019 9639
9020/* Forward decl way out to parsing code - dotrap needs it */ 9640/* Forward decl way out to parsing code - dotrap needs it */
@@ -9333,6 +9953,7 @@ evalcase(union node *n, int flags)
9333static int 9953static int
9334evalsubshell(union node *n, int flags) 9954evalsubshell(union node *n, int flags)
9335{ 9955{
9956 IF_PLATFORM_MINGW32(struct forkshell fs;)
9336 struct job *jp; 9957 struct job *jp;
9337 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ 9958 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9338 int status; 9959 int status;
@@ -9348,12 +9969,21 @@ evalsubshell(union node *n, int flags)
9348 if (backgnd == FORK_FG) 9969 if (backgnd == FORK_FG)
9349 get_tty_state(); 9970 get_tty_state();
9350 jp = makejob(/*n,*/ 1); 9971 jp = makejob(/*n,*/ 1);
9972#if ENABLE_PLATFORM_MINGW32
9973 memset(&fs, 0, sizeof(fs));
9974 fs.fpid = FS_EVALSUBSHELL;
9975 fs.n = n;
9976 fs.flags = flags;
9977 spawn_forkshell(&fs, jp, n, backgnd);
9978 if ( 0 ) {
9979#else
9351 if (forkshell(jp, n, backgnd) == 0) { 9980 if (forkshell(jp, n, backgnd) == 0) {
9352 /* child */ 9981 /* child */
9353 INT_ON; 9982 INT_ON;
9354 flags |= EV_EXIT; 9983 flags |= EV_EXIT;
9355 if (backgnd) 9984 if (backgnd)
9356 flags &= ~EV_TESTED; 9985 flags &= ~EV_TESTED;
9986#endif
9357 nofork: 9987 nofork:
9358 redirect(n->nredir.redirect, 0); 9988 redirect(n->nredir.redirect, 0);
9359 evaltreenr(n->nredir.n, flags); 9989 evaltreenr(n->nredir.n, flags);
@@ -9440,6 +10070,7 @@ expredir(union node *n)
9440static int 10070static int
9441evalpipe(union node *n, int flags) 10071evalpipe(union node *n, int flags)
9442{ 10072{
10073 IF_PLATFORM_MINGW32(struct forkshell fs;)
9443 struct job *jp; 10074 struct job *jp;
9444 struct nodelist *lp; 10075 struct nodelist *lp;
9445 int pipelen; 10076 int pipelen;
@@ -9466,6 +10097,16 @@ evalpipe(union node *n, int flags)
9466 ash_msg_and_raise_perror("can't create pipe"); 10097 ash_msg_and_raise_perror("can't create pipe");
9467 } 10098 }
9468 } 10099 }
10100#if ENABLE_PLATFORM_MINGW32
10101 memset(&fs, 0, sizeof(fs));
10102 fs.fpid = FS_EVALPIPE;
10103 fs.flags = flags;
10104 fs.n = lp->n;
10105 fs.fd[0] = pip[0];
10106 fs.fd[1] = pip[1];
10107 fs.fd[2] = prevfd;
10108 spawn_forkshell(&fs, jp, lp->n, n->npipe.pipe_backgnd);
10109#else
9469 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 10110 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
9470 /* child */ 10111 /* child */
9471 INT_ON; 10112 INT_ON;
@@ -9483,6 +10124,7 @@ evalpipe(union node *n, int flags)
9483 evaltreenr(lp->n, flags); 10124 evaltreenr(lp->n, flags);
9484 /* never returns */ 10125 /* never returns */
9485 } 10126 }
10127#endif
9486 /* parent */ 10128 /* parent */
9487 if (prevfd >= 0) 10129 if (prevfd >= 0)
9488 close(prevfd); 10130 close(prevfd);
@@ -10013,6 +10655,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10013 * as POSIX mandates */ 10655 * as POSIX mandates */
10014 return back_exitstatus; 10656 return back_exitstatus;
10015} 10657}
10658
10016static int 10659static int
10017evalcommand(union node *cmd, int flags) 10660evalcommand(union node *cmd, int flags)
10018{ 10661{
@@ -10036,6 +10679,9 @@ evalcommand(union node *cmd, int flags)
10036 int status; 10679 int status;
10037 char **nargv; 10680 char **nargv;
10038 smallint cmd_is_exec; 10681 smallint cmd_is_exec;
10682#if ENABLE_PLATFORM_MINGW32
10683 int cmdpath;
10684#endif
10039 10685
10040 errlinno = lineno = cmd->ncmd.linno; 10686 errlinno = lineno = cmd->ncmd.linno;
10041 if (funcline) 10687 if (funcline)
@@ -10103,6 +10749,7 @@ evalcommand(union node *cmd, int flags)
10103 } 10749 }
10104 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); 10750 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
10105 10751
10752#if !ENABLE_PLATFORM_MINGW32
10106 path = vpath.var_text; 10753 path = vpath.var_text;
10107 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { 10754 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10108 struct strlist **spp; 10755 struct strlist **spp;
@@ -10121,6 +10768,15 @@ evalcommand(union node *cmd, int flags)
10121 if (varcmp(p, path) == 0) 10768 if (varcmp(p, path) == 0)
10122 path = p; 10769 path = p;
10123 } 10770 }
10771#else
10772 /* Set path after any local PATH= has been processed. */
10773 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10774 struct strlist **spp = varlist.lastp;
10775 expandarg(argp, &varlist, EXP_VARTILDE);
10776 mklocal((*spp)->text);
10777 }
10778 path = vpath.var_text;
10779#endif
10124 10780
10125 /* Print the command if xflag is set. */ 10781 /* Print the command if xflag is set. */
10126 if (xflag) { 10782 if (xflag) {
@@ -10259,6 +10915,33 @@ evalcommand(union node *cmd, int flags)
10259 * in a script or a subshell does not need forking, 10915 * in a script or a subshell does not need forking,
10260 * we can just exec it. 10916 * we can just exec it.
10261 */ 10917 */
10918#if ENABLE_PLATFORM_MINGW32
10919 if (!(flags & EV_EXIT) || may_have_traps) {
10920 /* No, forking off a child is necessary */
10921 struct forkshell fs;
10922
10923 INT_OFF;
10924 memset(&fs, 0, sizeof(fs));
10925 fs.fpid = FS_SHELLEXEC;
10926 fs.argv = argv;
10927 fs.path = (char*)path;
10928 fs.fd[0] = cmdentry.u.index;
10929 fs.varlist = varlist.list;
10930 jp = makejob(/*cmd,*/ 1);
10931 spawn_forkshell(&fs, jp, cmd, FORK_FG);
10932 status = waitforjob(jp);
10933 INT_ON;
10934 TRACE(("forked child exited with %d\n", status));
10935 break;
10936 }
10937 /* If we're running 'command -p' we need to use the value stored
10938 * in path by parse_command_args(). If PATH is a local variable
10939 * listsetvar() will free the value currently in path so we need
10940 * to fetch the updated version. */
10941 cmdpath = (path != pathval());
10942 listsetvar(varlist.list, VEXPORT|VSTACK);
10943 shellexec(argv[0], argv, cmdpath ? path : pathval(), cmdentry.u.index);
10944#else
10262 if (!(flags & EV_EXIT) || may_have_traps) { 10945 if (!(flags & EV_EXIT) || may_have_traps) {
10263 /* No, forking off a child is necessary */ 10946 /* No, forking off a child is necessary */
10264 INT_OFF; 10947 INT_OFF;
@@ -10277,6 +10960,7 @@ evalcommand(union node *cmd, int flags)
10277 } 10960 }
10278 listsetvar(varlist.list, VEXPORT|VSTACK); 10961 listsetvar(varlist.list, VEXPORT|VSTACK);
10279 shellexec(argv[0], argv, path, cmdentry.u.index); 10962 shellexec(argv[0], argv, path, cmdentry.u.index);
10963#endif
10280 /* NOTREACHED */ 10964 /* NOTREACHED */
10281 } /* default */ 10965 } /* default */
10282 case CMDBUILTIN: 10966 case CMDBUILTIN:
@@ -10658,7 +11342,7 @@ preadbuffer(void)
10658 more--; 11342 more--;
10659 11343
10660 c = *q; 11344 c = *q;
10661 if (c == '\0') { 11345 if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) {
10662 memmove(q, q + 1, more); 11346 memmove(q, q + 1, more);
10663 } else { 11347 } else {
10664 q++; 11348 q++;
@@ -10854,6 +11538,7 @@ popallfiles(void)
10854 unwindfiles(&basepf); 11538 unwindfiles(&basepf);
10855} 11539}
10856 11540
11541#if !ENABLE_PLATFORM_MINGW32
10857/* 11542/*
10858 * Close the file(s) that the shell is reading commands from. Called 11543 * Close the file(s) that the shell is reading commands from. Called
10859 * after a fork is done. 11544 * after a fork is done.
@@ -10867,6 +11552,7 @@ closescript(void)
10867 g_parsefile->pf_fd = 0; 11552 g_parsefile->pf_fd = 0;
10868 } 11553 }
10869} 11554}
11555#endif
10870 11556
10871/* 11557/*
10872 * Like setinputfile, but takes an open file descriptor. Call this with 11558 * Like setinputfile, but takes an open file descriptor. Call this with
@@ -11093,8 +11779,13 @@ options(int cmdline, int *login_sh)
11093 int val; 11779 int val;
11094 int c; 11780 int c;
11095 11781
11096 if (cmdline) 11782 if (cmdline) {
11097 minusc = NULL; 11783 minusc = NULL;
11784#if ENABLE_PLATFORM_MINGW32
11785 dirarg = NULL;
11786 title = NULL;
11787#endif
11788 }
11098 while ((p = *argptr) != NULL) { 11789 while ((p = *argptr) != NULL) {
11099 c = *p++; 11790 c = *p++;
11100 if (c != '-' && c != '+') 11791 if (c != '-' && c != '+')
@@ -11120,6 +11811,20 @@ options(int cmdline, int *login_sh)
11120 /* bash 3.2 indeed handles -c CMD and +c CMD the same */ 11811 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
11121 if (c == 'c' && cmdline) { 11812 if (c == 'c' && cmdline) {
11122 minusc = p; /* command is after shell args */ 11813 minusc = p; /* command is after shell args */
11814#if ENABLE_PLATFORM_MINGW32
11815 /* Undocumented flags;
11816 * -d force current directory
11817 * -t title to display in console window
11818 * Must appear before -s or -c. */
11819 } else if (c == 'd' && cmdline && val == 1) {
11820 if (*argptr == NULL)
11821 ash_msg_and_raise_error(bb_msg_requires_arg, "-d");
11822 dirarg = *argptr++;
11823 } else if (c == 't' && cmdline && val == 1) {
11824 if (*argptr == NULL)
11825 ash_msg_and_raise_error(bb_msg_requires_arg, "-t");
11826 title = *argptr++;
11827#endif
11123 } else if (c == 'o') { 11828 } else if (c == 'o') {
11124 if (plus_minus_o(*argptr, val)) { 11829 if (plus_minus_o(*argptr, val)) {
11125 /* it already printed err message */ 11830 /* it already printed err message */
@@ -13110,6 +13815,9 @@ evalstring(char *s, int flags)
13110 int status; 13815 int status;
13111 13816
13112 s = sstrdup(s); 13817 s = sstrdup(s);
13818#if ENABLE_PLATFORM_MINGW32
13819 remove_cr(s, strlen(s)+1);
13820#endif
13113 setinputstring(s); 13821 setinputstring(s);
13114 setstackmark(&smark); 13822 setstackmark(&smark);
13115 13823
@@ -13253,7 +13961,7 @@ find_dot_file(char *name)
13253 struct stat statb; 13961 struct stat statb;
13254 13962
13255 /* don't try this for absolute or relative paths */ 13963 /* don't try this for absolute or relative paths */
13256 if (strchr(name, '/')) 13964 if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\')))
13257 return name; 13965 return name;
13258 13966
13259 while ((fullname = path_advance(&path, name)) != NULL) { 13967 while ((fullname = path_advance(&path, name)) != NULL) {
@@ -13375,14 +14083,18 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13375 struct builtincmd *bcmd; 14083 struct builtincmd *bcmd;
13376 14084
13377 /* If name contains a slash, don't use PATH or hash table */ 14085 /* If name contains a slash, don't use PATH or hash table */
13378 if (strchr(name, '/') != NULL) { 14086 if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\'))) {
13379 entry->u.index = -1; 14087 entry->u.index = -1;
13380 if (act & DO_ABS) { 14088 if (act & DO_ABS) {
14089#if ENABLE_PLATFORM_MINGW32
14090 if (auto_win32_extension(name) == NULL && stat(name, &statb) < 0) {
14091#else
13381 while (stat(name, &statb) < 0) { 14092 while (stat(name, &statb) < 0) {
13382#ifdef SYSV 14093#ifdef SYSV
13383 if (errno == EINTR) 14094 if (errno == EINTR)
13384 continue; 14095 continue;
13385#endif 14096#endif
14097#endif
13386 entry->cmdtype = CMDUNKNOWN; 14098 entry->cmdtype = CMDUNKNOWN;
13387 return; 14099 return;
13388 } 14100 }
@@ -13482,12 +14194,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13482 } 14194 }
13483 } 14195 }
13484 /* if rehash, don't redo absolute path names */ 14196 /* if rehash, don't redo absolute path names */
13485 if (fullname[0] == '/' && idx <= prev) { 14197 if (is_absolute_path(fullname) && idx <= prev) {
13486 if (idx < prev) 14198 if (idx < prev)
13487 continue; 14199 continue;
13488 TRACE(("searchexec \"%s\": no change\n", name)); 14200 TRACE(("searchexec \"%s\": no change\n", name));
13489 goto success; 14201 goto success;
13490 } 14202 }
14203#if ENABLE_PLATFORM_MINGW32
14204 add_win32_extension(fullname);
14205#endif
13491 while (stat(fullname, &statb) < 0) { 14206 while (stat(fullname, &statb) < 0) {
13492#ifdef SYSV 14207#ifdef SYSV
13493 if (errno == EINTR) 14208 if (errno == EINTR)
@@ -13978,6 +14693,9 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13978 if (!isdigit(modestr[0])) 14693 if (!isdigit(modestr[0]))
13979 mask ^= 0777; 14694 mask ^= 0777;
13980 umask(mask); 14695 umask(mask);
14696#if ENABLE_PLATFORM_MINGW32
14697 setvareq(xasprintf("BB_UMASK=0%o", mask), VEXPORT|VNOSAVE);
14698#endif
13981 } 14699 }
13982 return 0; 14700 return 0;
13983} 14701}
@@ -14036,6 +14754,7 @@ init(void)
14036 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); 14754 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
14037 basepf.linno = 1; 14755 basepf.linno = 1;
14038 14756
14757#if !ENABLE_PLATFORM_MINGW32
14039 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 14758 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
14040 setsignal(SIGCHLD); 14759 setsignal(SIGCHLD);
14041 14760
@@ -14043,12 +14762,89 @@ init(void)
14043 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" 14762 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
14044 */ 14763 */
14045 signal(SIGHUP, SIG_DFL); 14764 signal(SIGHUP, SIG_DFL);
14765#endif
14046 14766
14047 { 14767 {
14048 char **envp; 14768 char **envp;
14049 const char *p; 14769 const char *p;
14050 14770
14051 initvar(); 14771 initvar();
14772
14773#if ENABLE_PLATFORM_MINGW32
14774 /*
14775 * case insensitive env names from Windows world
14776 *
14777 * Some standard env names such as PATH is named Path and so on
14778 * ash itself is case sensitive, so "Path" will confuse it, as
14779 * MSVC getenv() is case insensitive.
14780 *
14781 * We may end up having both Path and PATH. Then Path will be chosen
14782 * because it appears first.
14783 */
14784 for (envp = environ; envp && *envp; envp++) {
14785 if (strncasecmp(*envp, "PATH=", 5) == 0 &&
14786 strncmp(*envp, "PATH=", 5) != 0) {
14787 break;
14788 }
14789 }
14790
14791 if (envp && *envp) {
14792 /*
14793 * If we get here it's because the environment contains a path
14794 * variable called something other than PATH. This suggests we
14795 * haven't been invoked from an earlier instance of BusyBox.
14796 */
14797 char *start, *end;
14798 struct passwd *pw;
14799
14800 for (envp = environ; envp && *envp; envp++) {
14801 if (!(end=strchr(*envp, '=')))
14802 continue;
14803
14804 /* make all variable names uppercase */
14805 for (start = *envp;start < end;start++)
14806 *start = toupper(*start);
14807
14808 /* Convert backslashes to forward slashes in value but
14809 * not if we're on Windows XP or for variables known to
14810 * cause problems */
14811 if ( !winxp && strncmp(*envp, "SYSTEMROOT=", 11) != 0 &&
14812 strncmp(*envp, "COMSPEC=", 8) != 0 ) {
14813 bs_to_slash(end+1);
14814 }
14815
14816 /* check for invalid characters in name */
14817 for (start = *envp;start < end;start++) {
14818 if (!isdigit(*start) && !isalpha(*start) && *start != '_') {
14819 break;
14820 }
14821 }
14822
14823 if (start != end) {
14824 /*
14825 * Make a copy of the variable, replacing invalid
14826 * characters in the name with underscores.
14827 */
14828 char *var = xstrdup(*envp);
14829
14830 for (start = var;*start != '=';start++) {
14831 if (!isdigit(*start) && !isalpha(*start)) {
14832 *start = '_';
14833 }
14834 }
14835 setvareq(var, VEXPORT|VNOSAVE);
14836 }
14837 }
14838
14839 /* Initialise some variables normally set at login, but
14840 * only if someone hasn't already set them. */
14841 pw = xgetpwuid(getuid());
14842 if (!getenv("USER")) xsetenv("USER", pw->pw_name);
14843 if (!getenv("LOGNAME")) xsetenv("LOGNAME", pw->pw_name);
14844 if (!getenv("HOME")) xsetenv("HOME", pw->pw_dir);
14845 if (!getenv("SHELL")) xsetenv("SHELL", DEFAULT_SHELL);
14846 }
14847#endif
14052 for (envp = environ; envp && *envp; envp++) { 14848 for (envp = environ; envp && *envp; envp++) {
14053/* Used to have 14849/* Used to have
14054 * p = endofname(*envp); 14850 * p = endofname(*envp);
@@ -14062,7 +14858,11 @@ init(void)
14062 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this 14858 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14063 */ 14859 */
14064 if (strchr(*envp, '=')) { 14860 if (strchr(*envp, '=')) {
14861#if !ENABLE_PLATFORM_MINGW32
14065 setvareq(*envp, VEXPORT|VTEXTFIXED); 14862 setvareq(*envp, VEXPORT|VTEXTFIXED);
14863#else
14864 setvareq(*envp, VEXPORT);
14865#endif
14066 } 14866 }
14067 } 14867 }
14068 14868
@@ -14151,6 +14951,9 @@ procargs(char **argv)
14151 goto setarg0; 14951 goto setarg0;
14152 } else if (!sflag) { 14952 } else if (!sflag) {
14153 setinputfile(*xargv, 0); 14953 setinputfile(*xargv, 0);
14954#if ENABLE_PLATFORM_MINGW32
14955 bs_to_slash(*xargv);
14956#endif
14154 setarg0: 14957 setarg0:
14155 arg0 = *xargv++; 14958 arg0 = *xargv++;
14156 commandname = arg0; 14959 commandname = arg0;
@@ -14235,6 +15038,9 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14235 struct jmploc jmploc; 15038 struct jmploc jmploc;
14236 struct stackmark smark; 15039 struct stackmark smark;
14237 int login_sh; 15040 int login_sh;
15041#if ENABLE_PLATFORM_MINGW32
15042 char *sd;
15043#endif
14238 15044
14239 /* Initialize global data */ 15045 /* Initialize global data */
14240 INIT_G_misc(); 15046 INIT_G_misc();
@@ -14281,9 +15087,24 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14281 exception_handler = &jmploc; 15087 exception_handler = &jmploc;
14282 rootpid = getpid(); 15088 rootpid = getpid();
14283 15089
15090#if ENABLE_PLATFORM_MINGW32
15091 winxp = (argv[1] != NULL && strcmp(argv[1], "-X") == 0);
15092#endif
14284 init(); 15093 init();
14285 setstackmark(&smark); 15094 setstackmark(&smark);
14286 15095
15096#if ENABLE_PLATFORM_MINGW32
15097 hSIGINT = CreateEvent(NULL, TRUE, FALSE, NULL);
15098 SetConsoleCtrlHandler(ctrl_handler, TRUE);
15099
15100 if (argc == 3 && !strcmp(argv[1], "--fs")) {
15101 forkshell_init(argv[2]);
15102
15103 /* NOTREACHED */
15104 bb_error_msg_and_die("forkshell failed");
15105 }
15106#endif
15107
14287#if NUM_SCRIPTS > 0 15108#if NUM_SCRIPTS > 0
14288 if (argc < 0) 15109 if (argc < 0)
14289 /* Non-NULL minusc tells procargs that an embedded script is being run */ 15110 /* Non-NULL minusc tells procargs that an embedded script is being run */
@@ -14295,10 +15116,49 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14295 trace_puts_args(argv); 15116 trace_puts_args(argv);
14296#endif 15117#endif
14297 15118
15119#if ENABLE_ASH_NOCONSOLE
15120 if (noconsole)
15121 hide_console();
15122#endif
15123
15124#if ENABLE_PLATFORM_MINGW32
15125 if (dirarg) {
15126 chdir(dirarg);
15127 setpwd(NULL, 0);
15128 }
15129 else if (!login_sh && iflag) {
15130 char *cwd = getcwd(NULL, 0);
15131 if (cwd) {
15132 docd(cwd, 0);
15133 free(cwd);
15134 }
15135 }
15136
15137 if (title)
15138 set_title(title);
15139#endif
15140
14298 if (login_sh) { 15141 if (login_sh) {
14299 const char *hp; 15142 const char *hp;
14300 15143
15144#if ENABLE_PLATFORM_MINGW32
15145 if (!dirarg) {
15146 chdir(xgetpwuid(getuid())->pw_dir);
15147 setpwd(NULL, 0);
15148 }
15149#endif
15150
14301 state = 1; 15151 state = 1;
15152#if ENABLE_PLATFORM_MINGW32
15153 sd = get_system_drive();
15154 if (sd) {
15155 char *path = xasprintf("%s/etc/profile", sd);
15156 read_profile(path);
15157 free(sd);
15158 free(path);
15159 }
15160 else
15161#endif
14302 read_profile("/etc/profile"); 15162 read_profile("/etc/profile");
14303 state1: 15163 state1:
14304 state = 2; 15164 state = 2;
@@ -14309,9 +15169,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14309 state2: 15169 state2:
14310 state = 3; 15170 state = 3;
14311 if ( 15171 if (
15172#if ENABLE_PLATFORM_POSIX
14312#ifndef linux 15173#ifndef linux
14313 getuid() == geteuid() && getgid() == getegid() && 15174 getuid() == geteuid() && getgid() == getegid() &&
14314#endif 15175#endif
15176#endif
14315 iflag 15177 iflag
14316 ) { 15178 ) {
14317 const char *shinit = lookupvar("ENV"); 15179 const char *shinit = lookupvar("ENV");
@@ -14372,6 +15234,809 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14372 /* NOTREACHED */ 15234 /* NOTREACHED */
14373} 15235}
14374 15236
15237#if ENABLE_PLATFORM_MINGW32
15238static void
15239forkshell_openhere(struct forkshell *fs)
15240{
15241 union node *redir = fs->n;
15242 int pip[2];
15243
15244 pip[0] = fs->fd[0];
15245 pip[1] = fs->fd[1];
15246
15247 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15248
15249 close(pip[0]);
15250 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
15251 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
15252 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
15253 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
15254 signal(SIGPIPE, SIG_DFL);
15255 if (redir->type == NHERE) {
15256 size_t len = strlen(redir->nhere.doc->narg.text);
15257 full_write(pip[1], redir->nhere.doc->narg.text, len);
15258 } else /* NXHERE */
15259 expandhere(redir->nhere.doc, pip[1]);
15260 _exit(EXIT_SUCCESS);
15261}
15262
15263static void
15264forkshell_evalbackcmd(struct forkshell *fs)
15265{
15266 union node *n = fs->n;
15267 int pip[2] = {fs->fd[0], fs->fd[1]};
15268
15269 FORCE_INT_ON;
15270 close(pip[0]);
15271 if (pip[1] != 1) {
15272 /*close(1);*/
15273 dup2_or_raise(pip[1], 1);
15274 close(pip[1]);
15275 }
15276 eflag = 0;
15277 ifsfree();
15278 evaltreenr(n, EV_EXIT);
15279 /* NOTREACHED */
15280}
15281
15282static void
15283forkshell_evalsubshell(struct forkshell *fs)
15284{
15285 union node *n = fs->n;
15286 int flags = fs->flags;
15287
15288 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15289 INT_ON;
15290 flags |= EV_EXIT;
15291 expredir(n->nredir.redirect);
15292 redirect(n->nredir.redirect, 0);
15293 evaltreenr(n->nredir.n, flags);
15294 /* never returns */
15295}
15296
15297static void
15298forkshell_evalpipe(struct forkshell *fs)
15299{
15300 union node *n = fs->n;
15301 int flags = fs->flags;
15302 int prevfd = fs->fd[2];
15303 int pip[2] = {fs->fd[0], fs->fd[1]};
15304
15305 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15306 INT_ON;
15307 if (pip[1] >= 0) {
15308 close(pip[0]);
15309 }
15310 if (prevfd > 0) {
15311 dup2(prevfd, 0);
15312 close(prevfd);
15313 }
15314 if (pip[1] > 1) {
15315 dup2(pip[1], 1);
15316 close(pip[1]);
15317 }
15318 evaltreenr(n, flags);
15319}
15320
15321static void
15322forkshell_shellexec(struct forkshell *fs)
15323{
15324 int idx = fs->fd[0];
15325 struct strlist *varlist = fs->varlist;
15326 char **argv = fs->argv;
15327 char *path = fs->path;
15328
15329 FORCE_INT_ON;
15330 listsetvar(varlist, VEXPORT|VSTACK);
15331 shellexec(argv[0], argv, path, idx);
15332}
15333
15334static void
15335forkshell_child(struct forkshell *fs)
15336{
15337 switch ( fs->fpid ) {
15338 case FS_OPENHERE:
15339 forkshell_openhere(fs);
15340 break;
15341 case FS_EVALBACKCMD:
15342 forkshell_evalbackcmd(fs);
15343 break;
15344 case FS_EVALSUBSHELL:
15345 forkshell_evalsubshell(fs);
15346 break;
15347 case FS_EVALPIPE:
15348 forkshell_evalpipe(fs);
15349 break;
15350 case FS_SHELLEXEC:
15351 forkshell_shellexec(fs);
15352 break;
15353 }
15354}
15355
15356/*
15357 * Reinitialise the builtin environment variables in varinit. Their
15358 * current settings have been copied from the parent in vartab. Look
15359 * these up using the names from varinit_data, copy the details from
15360 * vartab to varinit and replace the old copy in vartab with the new
15361 * one in varinit.
15362 *
15363 * Also reinitialise the function pointers and line number variable.
15364 */
15365static void
15366reinitvar(void)
15367{
15368 int i;
15369 const char *name;
15370 struct var **vpp, **old;
15371
15372 for (i=0; i<ARRAY_SIZE(varinit); ++i) {
15373 name = varinit_data[i].var_text ? varinit_data[i].var_text : "LINENO=";
15374 vpp = hashvar(name);
15375 if ( (old=findvar(vpp, name)) != NULL ) {
15376 varinit[i] = **old;
15377 *old = varinit+i;
15378 }
15379 varinit[i].var_func = varinit_data[i].var_func;
15380 }
15381 vlineno.var_text = linenovar;
15382}
15383
15384static void
15385spawn_forkshell(struct forkshell *fs, struct job *jp, union node *n, int mode)
15386{
15387 struct forkshell *new;
15388 char buf[32];
15389 const char *argv[] = { "sh", "--fs", NULL, NULL };
15390 intptr_t ret;
15391
15392 new = forkshell_prepare(fs);
15393 new->mode = mode;
15394 new->nprocs = jp == NULL ? 0 : jp->nprocs;
15395 sprintf(buf, "%p", new->hMapFile);
15396 argv[2] = buf;
15397 ret = mingw_spawn_proc(argv);
15398 CloseHandle(new->hMapFile);
15399 UnmapViewOfFile(new);
15400 if (ret == -1) {
15401 if (jp)
15402 freejob(jp);
15403 ash_msg_and_raise_error("unable to spawn shell");
15404 }
15405 forkparent(jp, n, mode, (HANDLE)ret);
15406}
15407
15408/*
15409 * forkshell_prepare() and friends
15410 *
15411 * The sequence is as follows:
15412 * - funcblocksize, nodeptrcount are initialized
15413 * - forkshell_size(fs) is called to calculate the exact memory needed
15414 * - a new struct is allocated
15415 * - funcblock, funcstring, nodeptr are initialized from the new block
15416 * - forkshell_copy(fs) is called to copy recursively everything over
15417 * it will record all pointers along the way, to nodeptr
15418 *
15419 * When this memory is mapped elsewhere, pointer fixup will be needed
15420 */
15421
15422/* redefine without test that nodeptr is non-NULL */
15423#undef SAVE_PTR
15424#undef SAVE_PTR2
15425#undef SAVE_PTR3
15426#define SAVE_PTR(dst) {*nodeptr++ = (char **)&(dst);}
15427#define SAVE_PTR2(dst1,dst2) {SAVE_PTR(dst1); SAVE_PTR(dst2);}
15428#define SAVE_PTR3(dst1,dst2,dst3) {SAVE_PTR2(dst1,dst2); SAVE_PTR(dst3);}
15429#define SAVE_PTR4(dst1,dst2,dst3,dst4) {SAVE_PTR2(dst1,dst2); SAVE_PTR2(dst3,dst4);}
15430
15431static int align_len(const char *s)
15432{
15433 return s ? SHELL_ALIGN(strlen(s)+1) : 0;
15434}
15435
15436#define SLIST_SIZE_BEGIN(name,type) \
15437static int \
15438name(int funcblocksize, type *p) \
15439{ \
15440 while (p) { \
15441 funcblocksize += sizeof(type);
15442 /* do something here with p */
15443#define SLIST_SIZE_END() \
15444 nodeptrcount++; \
15445 p = p->next; \
15446 } \
15447 return funcblocksize; \
15448}
15449
15450#define SLIST_COPY_BEGIN(name,type) \
15451static type * \
15452name(type *vp) \
15453{ \
15454 type *start; \
15455 type **vpp; \
15456 vpp = &start; \
15457 while (vp) { \
15458 *vpp = funcblock; \
15459 funcblock = (char *) funcblock + sizeof(type);
15460 /* do something here with vpp and vp */
15461#define SLIST_COPY_END() \
15462 SAVE_PTR((*vpp)->next); \
15463 ANNOT("(*vpp)->next"); \
15464 vp = vp->next; \
15465 vpp = &(*vpp)->next; \
15466 } \
15467 *vpp = NULL; \
15468 return start; \
15469}
15470
15471/*
15472 * struct var
15473 */
15474SLIST_SIZE_BEGIN(var_size,struct var)
15475funcblocksize += align_len(p->var_text);
15476nodeptrcount++; /* p->text */
15477SLIST_SIZE_END()
15478
15479SLIST_COPY_BEGIN(var_copy,struct var)
15480(*vpp)->var_text = nodeckstrdup(vp->var_text);
15481(*vpp)->flags = vp->flags;
15482(*vpp)->var_func = NULL;
15483SAVE_PTR((*vpp)->var_text);
15484ANNOT_NO_DUP(xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"));
15485SLIST_COPY_END()
15486
15487/*
15488 * struct strlist
15489 */
15490SLIST_SIZE_BEGIN(strlist_size,struct strlist)
15491funcblocksize += align_len(p->text);
15492nodeptrcount++; /* p->text */
15493SLIST_SIZE_END()
15494
15495SLIST_COPY_BEGIN(strlist_copy,struct strlist)
15496(*vpp)->text = nodeckstrdup(vp->text);
15497SAVE_PTR((*vpp)->text);
15498ANNOT_NO_DUP(xasprintf("(*vpp)->text '%s'", vp->text ?: "NULL"));
15499SLIST_COPY_END()
15500
15501/*
15502 * struct tblentry
15503 */
15504static int
15505tblentry_size(int funcblocksize, struct tblentry *tep)
15506{
15507 while (tep) {
15508 funcblocksize += sizeof(struct tblentry) + strlen(tep->cmdname);
15509 /* CMDBUILTIN, e->param.cmd needs no pointer relocation */
15510 if (tep->cmdtype == CMDFUNCTION) {
15511 funcblocksize += offsetof(struct funcnode, n);
15512 funcblocksize = calcsize(funcblocksize, &tep->param.func->n);
15513 nodeptrcount++; /* tep->param.func */
15514 }
15515 nodeptrcount++; /* tep->next */
15516 tep = tep->next;
15517 }
15518 return funcblocksize;
15519}
15520
15521static struct tblentry *
15522tblentry_copy(struct tblentry *tep)
15523{
15524 struct tblentry *start;
15525 struct tblentry **newp;
15526 int size;
15527
15528 newp = &start;
15529 while (tep) {
15530 *newp = funcblock;
15531 size = sizeof(struct tblentry) + strlen(tep->cmdname);
15532
15533 funcblock = (char *) funcblock + size;
15534 memcpy(*newp, tep, size);
15535 switch (tep->cmdtype) {
15536 case CMDBUILTIN:
15537 /* Save index of builtin, not pointer; fixed by forkshell_init() */
15538 (*newp)->param.index = tep->param.cmd - builtintab;
15539 break;
15540 case CMDFUNCTION:
15541 (*newp)->param.func = funcblock;
15542 funcblock = (char *) funcblock + offsetof(struct funcnode, n);
15543 copynode(&tep->param.func->n);
15544 SAVE_PTR((*newp)->param.func);
15545 ANNOT("param.func");
15546 break;
15547 default:
15548 break;
15549 }
15550 SAVE_PTR((*newp)->next);
15551 ANNOT_NO_DUP(xasprintf("cmdname '%s'", tep->cmdname));
15552 tep = tep->next;
15553 newp = &(*newp)->next;
15554 }
15555 *newp = NULL;
15556 return start;
15557}
15558
15559static int
15560cmdtable_size(int funcblocksize, struct tblentry **cmdtablep)
15561{
15562 int i;
15563 nodeptrcount += CMDTABLESIZE;
15564 funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE;
15565 for (i = 0; i < CMDTABLESIZE; i++)
15566 funcblocksize = tblentry_size(funcblocksize, cmdtablep[i]);
15567 return funcblocksize;
15568}
15569
15570static struct tblentry **
15571cmdtable_copy(struct tblentry **cmdtablep)
15572{
15573 struct tblentry **new = funcblock;
15574 int i;
15575
15576 funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE;
15577 for (i = 0; i < CMDTABLESIZE; i++) {
15578 new[i] = tblentry_copy(cmdtablep[i]);
15579 SAVE_PTR(new[i]);
15580 ANNOT_NO_DUP(xasprintf("cmdtablep[%d]", i));
15581 }
15582 return new;
15583}
15584
15585/*
15586 * char **
15587 */
15588static int
15589argv_size(int funcblocksize, char **p)
15590{
15591 while (p && *p) {
15592 funcblocksize += sizeof(char *);
15593 funcblocksize += align_len(*p);
15594 nodeptrcount++;
15595 p++;
15596 }
15597 funcblocksize += sizeof(char *);
15598 return funcblocksize;
15599}
15600
15601static char **
15602argv_copy(char **p)
15603{
15604 char **new, **start = funcblock;
15605#if FORKSHELL_DEBUG
15606 int i = 0;
15607#endif
15608
15609 while (p && *p) {
15610 new = funcblock;
15611 funcblock = (char *) funcblock + sizeof(char *);
15612 *new = nodeckstrdup(*p);
15613 SAVE_PTR(*new);
15614 ANNOT_NO_DUP(xasprintf("argv[%d] '%s'", i++, *p));
15615 p++;
15616 new++;
15617 }
15618 new = funcblock;
15619 funcblock = (char *) funcblock + sizeof(char *);
15620 *new = NULL;
15621 return start;
15622}
15623
15624/*
15625 * struct redirtab
15626 */
15627static int
15628redirtab_size(int funcblocksize, struct redirtab *rdtp)
15629{
15630 while (rdtp) {
15631 funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
15632 rdtp = rdtp->next;
15633 nodeptrcount++; /* rdtp->next */
15634 }
15635 return funcblocksize;
15636}
15637
15638static struct redirtab *
15639redirtab_copy(struct redirtab *rdtp)
15640{
15641 struct redirtab *start;
15642 struct redirtab **vpp;
15643
15644 vpp = &start;
15645 while (rdtp) {
15646 int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
15647 *vpp = funcblock;
15648 funcblock = (char *) funcblock + size;
15649 memcpy(*vpp, rdtp, size);
15650 SAVE_PTR((*vpp)->next);
15651 ANNOT("(*vpp)->next");
15652 rdtp = rdtp->next;
15653 vpp = &(*vpp)->next;
15654 }
15655 *vpp = NULL;
15656 return start;
15657}
15658
15659#undef shellparam
15660#undef redirlist
15661#undef vartab
15662static int
15663globals_var_size(int funcblocksize, struct globals_var *gvp)
15664{
15665 int i;
15666
15667 funcblocksize += sizeof(struct globals_var);
15668 funcblocksize = argv_size(funcblocksize, gvp->shellparam.p);
15669 funcblocksize = redirtab_size(funcblocksize, gvp->redirlist);
15670 for (i = 0; i < VTABSIZE; i++)
15671 funcblocksize = var_size(funcblocksize, gvp->vartab[i]);
15672 /* gvp->redirlist, gvp->shellparam.p, vartab */
15673 nodeptrcount += 2 + VTABSIZE;
15674 return funcblocksize;
15675}
15676
15677static struct globals_var *
15678globals_var_copy(struct globals_var *gvp)
15679{
15680 int i;
15681 struct globals_var *new;
15682
15683 new = funcblock;
15684 funcblock = (char *) funcblock + sizeof(struct globals_var);
15685 memcpy(new, gvp, sizeof(struct globals_var));
15686
15687 /* shparam */
15688 new->shellparam.malloced = 0;
15689 new->shellparam.p = argv_copy(gvp->shellparam.p);
15690 SAVE_PTR(new->shellparam.p);
15691 ANNOT("shellparam.p");
15692
15693 new->redirlist = redirtab_copy(gvp->redirlist);
15694 SAVE_PTR(new->redirlist);
15695 ANNOT("redirlist");
15696
15697 for (i = 0; i < VTABSIZE; i++) {
15698 new->vartab[i] = var_copy(gvp->vartab[i]);
15699 SAVE_PTR(new->vartab[i]);
15700 ANNOT_NO_DUP(xasprintf("vartab[%d]", i));
15701 }
15702
15703 return new;
15704}
15705
15706#undef minusc
15707#undef curdir
15708#undef physdir
15709#undef arg0
15710#undef commandname
15711#undef nullstr
15712static int
15713globals_misc_size(int funcblocksize, struct globals_misc *p)
15714{
15715 funcblocksize += sizeof(struct globals_misc);
15716 funcblocksize += align_len(p->minusc);
15717 if (p->curdir != p->nullstr)
15718 funcblocksize += align_len(p->curdir);
15719 if (p->physdir != p->nullstr)
15720 funcblocksize += align_len(p->physdir);
15721 funcblocksize += align_len(p->arg0);
15722 funcblocksize += align_len(p->commandname);
15723 nodeptrcount += 5; /* minusc, curdir, physdir, arg0, commandname */
15724 return funcblocksize;
15725}
15726
15727static struct globals_misc *
15728globals_misc_copy(struct globals_misc *p)
15729{
15730 struct globals_misc *new = funcblock;
15731
15732 funcblock = (char *) funcblock + sizeof(struct globals_misc);
15733 memcpy(new, p, sizeof(struct globals_misc));
15734
15735 new->minusc = nodeckstrdup(p->minusc);
15736 new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr;
15737 new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr;
15738 new->arg0 = nodeckstrdup(p->arg0);
15739 new->commandname = nodeckstrdup(p->commandname);
15740 SAVE_PTR4(new->minusc, new->curdir, new->physdir, new->arg0);
15741 SAVE_PTR(new->commandname);
15742 ANNOT_NO_DUP(xasprintf("minusc '%s'", p->minusc ?: "NULL"));
15743 ANNOT_NO_DUP(xasprintf("curdir '%s'", new->curdir ?: "NULL"));
15744 ANNOT_NO_DUP(xasprintf("physdir '%s'", new->physdir ?: "NULL"));
15745 ANNOT_NO_DUP(xasprintf("arg0 '%s'", p->arg0 ?: "NULL");)
15746 ANNOT_NO_DUP(xasprintf("commandname '%s'", p->commandname ?: "NULL"));
15747 return new;
15748}
15749
15750static int
15751forkshell_size(int funcblocksize, struct forkshell *fs)
15752{
15753 funcblocksize = globals_var_size(funcblocksize, fs->gvp);
15754 funcblocksize = globals_misc_size(funcblocksize, fs->gmp);
15755 funcblocksize = cmdtable_size(funcblocksize, fs->cmdtable);
15756 /* optlist_transfer(sending, fd); */
15757 /* misc_transfer(sending, fd); */
15758
15759 funcblocksize = calcsize(funcblocksize, fs->n);
15760 funcblocksize = argv_size(funcblocksize, fs->argv);
15761 funcblocksize += align_len(fs->path);
15762 funcblocksize = strlist_size(funcblocksize, fs->varlist);
15763
15764 nodeptrcount += 7; /* gvp, gmp, cmdtable, n, argv, string, strlist */
15765 return funcblocksize;
15766}
15767
15768static void
15769forkshell_copy(struct forkshell *fs, struct forkshell *new)
15770{
15771 memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */
15772 new->gvp = globals_var_copy(fs->gvp);
15773 new->gmp = globals_misc_copy(fs->gmp);
15774 new->cmdtable = cmdtable_copy(fs->cmdtable);
15775 SAVE_PTR3(new->gvp, new->gmp, new->cmdtable);
15776 ANNOT3("gvp", "gmp", "cmdtable");
15777
15778 new->n = copynode(fs->n);
15779 new->argv = argv_copy(fs->argv);
15780 new->path = nodeckstrdup(fs->path);
15781 new->varlist = strlist_copy(fs->varlist);
15782 SAVE_PTR4(new->n, new->argv, new->path, new->varlist);
15783 ANNOT2("n", "argv");
15784 ANNOT_NO_DUP(xasprintf("path '%s'", fs->path ?: "NULL"));
15785 ANNOT("varlist");
15786}
15787
15788#if FORKSHELL_DEBUG
15789/* fp and notes can each be NULL */
15790static void
15791forkshell_print(FILE *fp0, struct forkshell *fs, char **notes)
15792{
15793 FILE *fp;
15794 void *lfuncblock;
15795 char *lfuncstring;
15796 char ***lnodeptr;
15797 char *s;
15798 int count;
15799
15800 if (fp0 != NULL) {
15801 fp = fp0;
15802 }
15803 else {
15804 char name[32];
15805
15806 sprintf(name, "fs_%d.out", getpid());
15807 if ((fp=fopen(name, "w")) == NULL)
15808 return;
15809 }
15810
15811 fprintf(fp, "size %d = %d + %d*%d + %d + %d\n", fs->size,
15812 (int)sizeof(struct forkshell), fs->nodeptrcount,
15813 (int)sizeof(char *), fs->funcblocksize, fs->funcstringsize);
15814
15815 lnodeptr = fs->nodeptr;
15816 lfuncblock = (char *)lnodeptr + (fs->nodeptrcount+1)*sizeof(char *);
15817 lfuncstring = (char *)lfuncblock + fs->funcblocksize;
15818
15819 fprintf(fp, "funcblocksize %d = %d + %d + %d\n\n", fs->funcblocksize,
15820 (int)((char *)fs->gmp-(char *)fs->gvp),
15821 (int)((char *)fs->cmdtable-(char *)fs->gmp),
15822 (int)(lfuncstring-(char *)fs->cmdtable));
15823
15824 fprintf(fp, "--- nodeptr ---\n");
15825 count = 0;
15826 if (notes == NULL) {
15827 while (*lnodeptr) {
15828 fprintf(fp, "%p ", *lnodeptr++);
15829 if ((count&3) == 3)
15830 fprintf(fp, "\n");
15831 ++count;
15832 }
15833 if (((count-1)&3) != 3)
15834 fprintf(fp, "\n");
15835 }
15836 else {
15837 while (*lnodeptr) {
15838 fprintf(fp, "%p %p %s\n", *lnodeptr, **lnodeptr, notes[count++]);
15839 lnodeptr++;
15840 }
15841 }
15842 if (count != fs->nodeptrcount)
15843 fprintf(fp, "--- %d pointers (expected %d) ---\n\n", count,
15844 fs->nodeptrcount);
15845 else
15846 fprintf(fp, "--- %d pointers ---\n\n", count);
15847
15848 fprintf(fp, "--- funcstring ---\n");
15849 count = 0;
15850 s = lfuncstring;
15851 while (s-lfuncstring < fs->funcstringsize) {
15852 if (!*s) {
15853 ++s;
15854 continue;
15855 }
15856 fprintf(fp, "%p '%s'\n", s, s);
15857 s += strlen(s)+1;
15858 ++count;
15859 }
15860 fprintf(fp, "--- %d strings ---\n", count);
15861
15862 if (fp0 == NULL)
15863 fclose(fp);
15864}
15865#endif
15866
15867static struct forkshell *
15868forkshell_prepare(struct forkshell *fs)
15869{
15870 int funcblocksize;
15871 struct forkshell *new;
15872 int size;
15873 HANDLE h;
15874 SECURITY_ATTRIBUTES sa;
15875#if FORKSHELL_DEBUG
15876 void *fb0;
15877 char name[32];
15878 FILE *fp;
15879#endif
15880
15881 /* Calculate size of "new" */
15882 fs->gvp = ash_ptr_to_globals_var;
15883 fs->gmp = ash_ptr_to_globals_misc;
15884 fs->cmdtable = cmdtable;
15885
15886 /*
15887 * Careful: much scope for off-by-one errors. nodeptrcount is the
15888 * number of actual pointers. There's also a terminating NULL pointer.
15889 * The array in the forkshell structure gives us one element for free.
15890 */
15891 nodeptrcount = 0;
15892 funcblocksize = forkshell_size(0, fs);
15893 size = sizeof(struct forkshell) + nodeptrcount*sizeof(char *) +
15894 funcblocksize;
15895
15896 /* Allocate, initialize pointers */
15897 memset(&sa, 0, sizeof(sa));
15898 sa.nLength = sizeof(sa);
15899 sa.lpSecurityDescriptor = NULL;
15900 sa.bInheritHandle = TRUE;
15901 h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, size, NULL);
15902 new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
15903 nodeptr = new->nodeptr;
15904 funcblock = (char *)nodeptr + (nodeptrcount+1)*sizeof(char *);
15905 funcstring_end = (char *)new + size;
15906#if FORKSHELL_DEBUG
15907 fb0 = funcblock;
15908 annot = xmalloc(sizeof(char *)*nodeptrcount);
15909 annot_count = 0;
15910#endif
15911
15912 /* Now pack them all */
15913 forkshell_copy(fs, new);
15914
15915 /* Finish it up */
15916 *nodeptr = NULL;
15917 new->size = size;
15918 new->old_base = (char *)new;
15919 new->hMapFile = h;
15920#if FORKSHELL_DEBUG
15921 sprintf(name, "fs_%d.out", getpid());
15922 if ((fp=fopen(name, "w")) != NULL) {
15923 int i;
15924
15925 /* perform some sanity checks on pointers */
15926 fprintf(fp, "%p start of forkshell struct\n", new);
15927 fprintf(fp, "%p start of nodeptr\n", new->nodeptr);
15928 fprintf(fp, "%p start of funcblock", fb0);
15929 if ((char *)(nodeptr+1) != (char *)fb0)
15930 fprintf(fp, " != end nodeptr block + 1 %p\n", nodeptr+1);
15931 else
15932 fprintf(fp, "\n");
15933
15934 fprintf(fp, "%p start of funcstring", funcstring_end);
15935 if ((char *)funcblock != funcstring_end)
15936 fprintf(fp, " != end funcblock + 1 %p\n\n", funcblock);
15937 else
15938 fprintf(fp, "\n\n");
15939
15940 if (nodeptrcount != annot_count)
15941 fprintf(fp, "nodeptrcount (%d) != annot_count (%d)\n\n",
15942 nodeptrcount, annot_count);
15943
15944 new->nodeptrcount = nodeptrcount;
15945 new->funcblocksize = (char *)funcblock - (char *)fb0;
15946 new->funcstringsize = (char *)new + size - funcstring_end;
15947 forkshell_print(fp, new, nodeptrcount == annot_count ? annot : NULL);
15948
15949 for (i = 0; i < annot_count; ++i)
15950 free(annot[i]);
15951 free(annot);
15952 annot = NULL;
15953 annot_count = 0;
15954 fclose(fp);
15955 }
15956#endif
15957 return new;
15958}
15959
15960#undef exception_handler
15961#undef trap
15962#undef trap_ptr
15963static void *sticky_mem_start, *sticky_mem_end;
15964static void
15965forkshell_init(const char *idstr)
15966{
15967 struct forkshell *fs;
15968 void *map_handle;
15969 HANDLE h;
15970 struct globals_var **gvpp;
15971 struct globals_misc **gmpp;
15972 int i;
15973 char **ptr;
15974
15975 if (sscanf(idstr, "%p", &map_handle) != 1)
15976 bb_error_msg_and_die("invalid forkshell ID");
15977
15978 h = (HANDLE)map_handle;
15979 fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
15980 if (!fs)
15981 bb_error_msg_and_die("Invalid forkshell memory");
15982
15983 /* this memory can't be freed */
15984 sticky_mem_start = fs;
15985 sticky_mem_end = (char *) fs + fs->size;
15986
15987 /* pointer fixup */
15988 for ( i=0; fs->nodeptr[i]; ++i ) {
15989 ptr = (char **)((char *)fs + ((char *)fs->nodeptr[i] - fs->old_base));
15990 if (*ptr)
15991 *ptr = (char *)fs + (*ptr - fs->old_base);
15992 }
15993
15994 /* Now fix up stuff that can't be transferred */
15995 for (i = 0; i < CMDTABLESIZE; i++) {
15996 struct tblentry *e = fs->cmdtable[i];
15997 while (e) {
15998 if (e->cmdtype == CMDBUILTIN)
15999 e->param.cmd = builtintab + e->param.index;
16000 e = e->next;
16001 }
16002 }
16003 fs->gmp->exception_handler = ash_ptr_to_globals_misc->exception_handler;
16004 for (i = 0; i < NSIG; i++)
16005 fs->gmp->trap[i] = ash_ptr_to_globals_misc->trap[i];
16006 fs->gmp->trap_ptr = ash_ptr_to_globals_misc->trap_ptr;
16007
16008 /* Switch global variables */
16009 gvpp = (struct globals_var **)&ash_ptr_to_globals_var;
16010 *gvpp = fs->gvp;
16011 gmpp = (struct globals_misc **)&ash_ptr_to_globals_misc;
16012 *gmpp = fs->gmp;
16013 cmdtable = fs->cmdtable;
16014
16015 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
16016
16017 reinitvar();
16018
16019 shlvl++;
16020 if (fs->mode == FORK_BG) {
16021 SetConsoleCtrlHandler(NULL, TRUE);
16022 if (fs->nprocs == 0) {
16023 close(0);
16024 if (open(bb_dev_null, O_RDONLY) != 0)
16025 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
16026 }
16027 }
16028 forkshell_child(fs);
16029}
16030
16031#undef free
16032static void
16033sticky_free(void *base)
16034{
16035 if (base >= sticky_mem_start && base < sticky_mem_end)
16036 return;
16037 free(base);
16038}
16039#endif
14375 16040
14376/*- 16041/*-
14377 * Copyright (c) 1989, 1991, 1993, 1994 16042 * Copyright (c) 1989, 1991, 1993, 1994