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 <stdio.h>
+ #include <fcntl.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <errno.h>
+ #include <memory.h>
+ #include <signal.h>
+ #include <dos.h>
+ 
+ #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);
+ }
