MS-DOS patches to perl. Apply this patch to the standard perl source, version 4, patch level 19, using "patch -p." Do this in the root directory of the perl source distribution. You can cat all these patches together and pipe the output to patch -p. Len Reed Holos Software, Inc. ..!gatech!holos0!lbr holos0!lbr@gatech.edu -------------------------------------- *** msdos/swpspawn.c.old Sun Feb 23 08:48:17 1992 --- msdos/swpspawn.c Sun Nov 17 11:18:42 1991 *************** *** 0 **** --- 1,557 ---- + /* RCS -- $Header: c:/usr/lbr/perl/RCS/swpspawn.c 1.2 90/10/15 15:13:46 lbr Exp $ + -- SYNOPSIS -- Version of spawnvp that invokes .exe swapping. + -- + -- DESCRIPTION + -- Simulate path searching action of spawnvp and then invoke + -- the exe swapping spawn. + -- + -- AUTHOR + -- Len Reed, ..!gatech!holos0!lbr or holos0!lbr@gatech.edu. + -- Holos Software, Inc., Tucker, Ga. + -- + -- ACKNOWLEDGEMENT AND PLUG + -- Dennis Vadura, dvadura@watdragon.uwaterloo.ca, wrote the assembly + -- routine "exec()" and I have adopted small sections of his code + -- that calls that routine. That code comes from dmake 3.6, a + -- high-power portable (Unix, DOS, OS/2) make program, available + -- for anonymous ftp from watmsg.uwaterloo.ca. Address is 129.97.129.9. + -- It is in the pub/src directory, set your mode to binary, and copy + -- either: + -- dmake-3.6.tar.Z - compressed tar format + -- dmake-3.6.zoo - zoo archive + -- + -- COPYRIGHT + -- Copyright (c) 1990 by Leonard Reed. All rights reserved. + -- + -- This program is free software; you can redistribute it and/or + -- modify it under the terms of the GNU General Public License + -- (version 1), as published by the Free Software Foundation, and + -- found in the file 'LICENSE' included with this distribution. + -- + -- This program is distributed in the hope that it will be useful, + -- but WITHOUT ANY WARRANTY; without even the implied warrant of + -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + -- GNU General Public License for more details. + -- + -- You should have received a copy of the GNU General Public License + -- along with this program; if not, write to the Free Software + -- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + -- This program is distributed in the hope that it will be useful, + -- but WITHOUT ANY WARRANTY; without even the implied warrant of + -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + -- + -- LOG + -- $Log: swpspawn.c $ + * Revision 1.2 90/10/15 15:13:46 lbr + * Working pretty well. + * + * + */ + + #include + #include + #include + #include + #include + #include + #include + #include + + #include "EXTERN.h" + #include "perl.h" + + #define NIL(p) ((p*)NULL) + + static char *DirBrkStr = "/:\\"; /* path separator components */ + #define DirSepStr "/" /* used as path separator when making name */ + + static char *find_prog_file(char *); + static char * try_path_element(char *, char *, char *); + static char * try_path_suffixes(char *, char *); + + static void close_duplicates(void); + static void restore_duplicates(void); + static int set_no_inherit(int, int); + static void sig_fix_inherit(void); + static void disk_swap_setup(void); + static char far *get_child_env(unsigned int *); + + static int caught_sigint; + static int do_swap; + static int swap_handle = -1; + + #ifdef MKS_SUPPORT + static int mksargs; /* must check for MKS support at run time */ + #else + # define mksargs 0 /* allow compiler to optimize out MKS support */ + #endif + + int swap_spawn(int mksargs_value, char *pfile_name, char **argv) + { + char command_tail[129]; /* max tail plus terminate null for us */ + int i, alength; + char *ap; + char **arg_cpy; + char *build_point; + char far * eptr; /* pointer to child enviroment */ + unsigned int junk_at_end; + unsigned int max_e_size; /* max size of child environemt in bytes */ + unsigned int esize; + typedef void SIGHANDLER(int); /* typedef for a signal handler */ + SIGHANDLER *real_sigint; /* saved value of SIGINT handler */ + + #ifdef MKS_SUPPORT + mksargs = mksargs_value; + #endif + + /* Find the complete name (drive:path.ext) of the command */ + + pfile_name = find_prog_file(argv[0]); + if (pfile_name == NIL(char)) { /* no such file */ + errno = ENOENT; + return -1; + } + else if (pfile_name == (char *)2) { /* batch file */ + return -2; + } + else if (mksargs && pfile_name == (char *)3) { /* Ksh script */ + return -3; + } + + /* Create the child's environment. The space for the environment + is allocated elsewhere. It is addressed here as "far", since + it must be statically allocated below the swap point. + + junk_at_end is file name plus slop put at end of enviroment + by DOS 3.0+. + */ + + esize = strlen(pfile_name) + 4; /* name appended at end */ + eptr = get_child_env(&max_e_size); /* get ptr to child environ */ + + if (mksargs) { + /* put actual value of argv[0] into environment */ + ap = argv[0]; + *eptr++ = '~'; /* MKS indicator of arg, not env */ + while (*eptr++ = *ap++) /* small strcpy can't handle far target */ + ; + } + + /* Build the command tail from the argument list. */ + + command_tail[1] = 0; + arg_cpy = argv; + i = 2; /* count byte plus '\r' at end is min. length */ + build_point = &command_tail[1]; + while (ap = *++arg_cpy) { + alength = strlen(ap) + 1; /* another argument plus a ' ' */ + i += alength; + if (i > 128 && mksargs == 0) { /* niggardly MS-DOS arg list limit */ + errno = E2BIG; + return -1; + } + else { + *build_point++ = ' '; /* separate args (even put ' ' before 1st */ + (void) strcpy(build_point, ap); + build_point = &command_tail[i-1]; + } + if (mksargs) { + esize += strlen(ap) + 2; + if (esize > max_e_size) { + fatal("Out of memory"); + } + *eptr++ = '~'; /* MKS indicator of arg, not env */ + while (*eptr++ = *ap++) + ; + } + } + + if (mksargs) { + /* drop argument(s) from command tail if too long */ + if (i > 127) + i = build_point - command_tail - 1; + command_tail[i] = '\n'; /* MKS adds this */ + command_tail[i+1] = 0; /* terminate for our benefit, not DOS's */ + } + else { + command_tail[i] = 0; /* terminate for our benefit, not DOS's */ + } + command_tail[i-1] = '\r'; /* command.com does this, so we will */ + command_tail[0] = i - 2; /* don't count count or '\r' */ + + arg_cpy = environ; /* our enviroment as it currently exists */ + + if (mksargs) { + while (**arg_cpy == '~') /* skip parameters MKS program passed to us */ + ++arg_cpy; + } + + while (ap = *arg_cpy++) { + esize += strlen(ap) + 1; + if (esize > max_e_size) { + fatal("Out of memory"); + } + while (*eptr++ = *ap++) /* small strcpy can't handle far target */ + ; + } + *eptr++ = 0; /* terminate the environment */ + + disk_swap_setup(); /* initialize swap file, if needed */ + + /* Special handling of SIGINT while we're playing with the inheritance. + Note that the exec() routine will set up death by SIGINT as the + default for the child. + */ + + caught_sigint = 0; + real_sigint = signal(SIGINT, sig_fix_inherit); + close_duplicates(); + if ( !caught_sigint ) { + i = exec(do_swap, pfile_name, command_tail, FP_SEG(eptr), swap_handle); + } + restore_duplicates(); + if ( caught_sigint ) { + i = 0x100; /* DOS return code for killed by Ctrl-break */ + } + (void) signal(SIGINT, real_sigint); + + return i; /* this is still an MS-DOS style exit, as from int 21h, + function 4dh. + */ + } + + /* Find the file name based on the command name. Returns NIL(char) if + no applicable file found. + + If a delimited path is found no path search is done. A delimited path + is one containing a character from DirBrkStr, which under DOS is + {slash, backslash, colon}. Otherwise the file is looked for + under each directory in the path. If MKSARGS is in effect, the current + directory is search only in response to a . or a null path element, just + as with the shell. If MKSARGS is not in effect, the current directory + is always searched first, as in command.com. + + If the command contains does not contain an extension, ".com", ".exe", + ".bat", and ".ksh" are tried in that order. (The last only if MKSARGS + in effect.) + */ + + + static char * + find_prog_file(char *argv0) + { + char *this_path; + int append; + char *sep; + char *result; + + this_path = getenv("PATH"); + if (this_path == NIL(char)) { + this_path = ""; /* No path!! Use this directory */ + } + + /* If argv0 contains a path separator, use this_path, else use "". */ + + if (strcspn(argv0, DirBrkStr) != strlen(argv0)) + this_path = ""; + + /* If extensionless command name given, we'll try to append the + extensions. Find last '.'. If none, append extensions. + If there is a '.', make sure that it comes after the last + path delimiter: e.g., "xyz.q/abc" needs extensions added. + + Handle explicit ".bat" and ".ksh" files: these need a + command interpreter. + */ + + sep = strrchr(argv0, '.'); /* find last '.' */ + if (sep == NIL(char)) + append = 1; + else { + append = strcspn(sep, DirBrkStr) != strlen(sep); + if (append == 0) { + ++sep; + if (strcmp(sep, "bat") == 0) + return (char *)2; + else if (mksargs && strcmp(sep, "ksh") == 0) + return (char *)3; + } + } + + if ( ! mksargs ) { /* always search current directory */ + if (append) + result = try_path_suffixes("", argv0); + else + result = try_path_element("", argv0, ""); + if (result != NIL(char)) + return result; + } + + do { + sep = strchr(this_path, ';'); + if (sep != NIL(char)) + *sep = 0; /* temporarily truncate */ + + if (append) + result = try_path_suffixes(this_path, argv0); + else + result = try_path_element(this_path, argv0, ""); + + if (sep) { + *sep = ';'; /* fix the damage */ + this_path = sep + 1; + } + /* else we just got a hit or we're done and didn't find it */ + + if (result != NIL(char)) + return result; + + } while (sep != NIL(char)); + + return NIL(char); + } + + /* try_path_element builds the filename string based on path-prefix, + file name, and extension (which includes the '.'). Path_prefix + is "" if doing current directory or if fname contains path delimeters. + (The latter occurs when the command in the script had delimeters.) + A DirSepStr (i.e., a '/') is inserted after path_prefix unless + path_prefix is "". (We dont want "/homedirfile" or "//path/file".) + */ + + + static char * + try_path_element(char *path_prefix, char *fname, char *ext) + { + int i; + static char name[150]; + + (void) strcpy(name, path_prefix); + if (name[0]) + (void) strcat(name, DirSepStr); + (void) strcat(name, fname); + (void) strcat(name, ext); + i = open(name, O_RDONLY); + if (i >= 0) { + (void) close (i); + return name; + } + else + return NIL(char); + } + + /* Try various pathame extensions */ + + static char * + try_path_suffixes(char *path_prefix, char *fname) + { + register char *result; + + if (result = try_path_element(path_prefix, fname, ".com")) + return result; + if (result = try_path_element(path_prefix, fname, ".exe")) + return result; + if (result = try_path_element(path_prefix, fname, ".bat")) + return (char *)2; + if (mksargs && (result = try_path_element(path_prefix, fname, ".ksh"))) + return (char *)3; + return NIL(char); + } + + /* Routines to close and restore duplicate file handles while child is + running. Handles 0 thru maxsysfd (typically 2) + are always set for inheritance. Handles + greater than maxsysfd are set for no inheritance unless they are + duplicates of the system file handles. + + Close any duplicates greater than maxsysfd; we'll restore them after + returning from the child. + + Then disable inheritence for any descriptors > maxsysfd still + remaining. Note that we cannot exit this program with inheritence + set this way: we might be blocking inheritance of CON for all time! + + Finally, enable inheritence for file descripors <= maxsysfd. + This usually isn't necessary but can be due to something like: + open(NEW, "a_file"); + system "anything at all"; + open(STDOUT, "&NEW"); + close(NEW); + system "something else"; + + The first system call blocks inheritence for NEW. The second system + call requires that STDOUT's inheritence be enabled. + */ + + static int * restore_handle; + static int nofiles; + + static void close_duplicates(void) + { + unsigned char far * table; /* the map from handles to sys file */ + register int i, j; + unsigned char match; + + nofiles = dos_get_nofiles(); /* possible handles, typically 20 */ + table = dos_get_sft_map(); /* probably _psp:18h */ + + while (table[--nofiles] == 0xFF) + {} /* do nothing */ + + /* nofiles is now last used handle, not count of handles */ + + New(1107, restore_handle, nofiles-maxsysfd, int); + memset(restore_handle, 0xFF, 2*(nofiles-maxsysfd)); /* all to -1 */ + + /* Close duplicates of system file handles, + mark others no inherit. + */ + + for (i = 0; i <= maxsysfd; i++) { + if ((match = table[i]) == 0xFF) /* ingore it if not open */ + continue; + for (j = maxsysfd+1; j <= nofiles; j++) { + if (match == table[j]) { + restore_handle[j-maxsysfd-1] = i; + (void) dos_close (j); /* will set tables[j] == 0xFF */ + } + } + } + for (i = maxsysfd+1; i <= nofiles; i++) { + restore_handle[i] = 0x8000 | set_no_inherit(i, 1); + } + for (i = 0; i <= maxsysfd; i++) { + (void) set_no_inherit(i, 0); + } + } + + static void restore_duplicates(void) + { + register int j; + int value; + + for (j = maxsysfd+1; j <= nofiles; j++) { + value = restore_handle[j-maxsysfd-1]; + if (value != -1) { + if (value & 0x8000) { + if (value == 0x8000) + (void) set_no_inherit(j, 0); /* restore to inherit */ + /* else it's already set to no inherit */ + } + else { + (void) dos_dup2(restore_handle[j-maxsysfd-1], j); + } + } + } + Safefree(restore_handle); + } + + /* Routine to allocate child environment block. Called by routine + that does path searching. Returns pointer to it; sets + *max_size as number of bytes. Allocates at most + 0x8000 (32 K) bytes. + + Note that exec() frees this memory block. + */ + + static char _far *get_child_env(unsigned int *max_size) + { + unsigned int child_env_seg; + + if (_dos_allocmem(0x800, (unsigned int *) &child_env_seg)) { + *max_size = child_env_seg; + _dos_allocmem(*max_size, (unsigned int *) &child_env_seg); + *max_size <<= 4; + } + else + *max_size = 0x8000; + + return (MK_FP(child_env_seg, 0)); /* return far pointer to it */ + } + + /* Change the no-inherit status of an open file handle. + handle is the handle of the file to change. + new_status is zero for allow inheritance and non-zero for block inher. + + Returns the previous value 1 (on) or 0 (off) + */ + + static int set_no_inherit(int handle, int new_status) + { + struct sft far *p; + int old_value; + + p = dos_sys_file(handle); /* get sys file table ptr */ + old_value = (p->flags & DOS_SFT_NOINHERIT) != 0; + + if (new_status) + new_status = DOS_SFT_NOINHERIT; + + p->flags = (p->flags & ~DOS_SFT_NOINHERIT) | new_status; + return old_value; + } + + /* Interrupt handler for signal occuring while fooling with inheritence. + Must delay handling of the signal. + */ + + static void sig_fix_inherit(void) + { + (void) signal(SIGINT, SIG_IGN); + caught_sigint = 1; + } + + /* Open swap file. Directories: + 1) First choice is $EXESWAP, already set as swap_dir. + 2) Second choice is $TMP. + 3) Third choice is current directory. + */ + + static void disk_swap_setup(void) + { + char *swap_dir; + static char *swap_fname; + char swap_name[120]; + + /* Compute swap_dir and do_swap for use by exec(). */ + + swap_dir = getenv("EXESWAP"); + do_swap = swap_dir == NULL || strcmp(swap_dir, ".off") != 0; + + /* If we're not swapping this time, or if the file's already + open, return. + */ + + if (do_swap == 0 || swap_handle >= 0) + return; + + if (swap_dir == NULL) { + swap_dir = getenv("TMP"); + if (swap_dir == NULL) { + swap_dir = "."; /* last choice, use current dir */ + } + } + + (void) strcpy(swap_name, swap_dir); + (void) strcat(swap_name, "/swpXXXXXX"); + (void) mktemp(swap_name); + + swap_fname = strdup(swap_name); /* save for use in clean up at end */ + + block_signals(); /* block the signals while creating temp file */ + + swap_handle = open(swap_fname, O_RDWR | O_CREAT | O_EXCL | O_BINARY, + S_IREAD | S_IWRITE); + + if (swap_handle < 0) { + unblock_signals(); + fatal("Could not create swap file \"%s\"", swap_fname); + } + + /* Add swap file to temp name list. This will unblock the + signals. + */ + + (void) add_temp_file((FILE *)0, swap_handle, NULL, + swap_fname, swap_file, 0); + }