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/popen.c.old	Fri Jun 14 20:10:24 1991
--- msdos/popen.c	Sun Nov 17 11:17:32 1991
***************
*** 24,184 ****
   */
  
  /*
!  * Popen and pclose for MS-DOS
   */
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <process.h>
  
! /*
!  * Possible actions on an popened file
!  */
! enum action {
! 	delete, 			/* Used for "r". Delete the tmp file */
! 	execute				/* Used for "w". Execute the command. */
! };
  
  /*
   * Linked list of things to do at the end of the program execution.
   */
! static struct todo {
! 	FILE *f;			/* File we are working on (to fclose) */
! 	const char *name;		/* Name of the file (to unlink) */
! 	const char *command;		/* Command to execute */
! 	enum action what;		/* What to do (execute or delete) */
! 	struct todo *next;		/* Next structure */
! } *todolist;
  
  
! /* Clean up function */
! static int close_pipes(void);
  
  /*
   * Add a file f running the command command on file name to the list
   * of actions to be done at the end.  The action is specified in what.
   * Return -1 on failure, 0 if ok.
   */
! static int
! add(FILE *f, const char *command, const char *name, enum action what)
! {
  	struct todo    *p;
  
  	if ((p = (struct todo *) malloc(sizeof(struct todo))) == NULL)
! 		return -1;
! 	p->f = f;
  	p->command = command;
  	p->name = name;
  	p->what = what;
  	p->next = todolist;
  	todolist = p;
! 	return 0;
  }
  
  FILE *
! mypopen(const char *command, const char *t)
  {
  	char buff[256];
  	char *name;
  	FILE *f;
! 	static init = 0;
  
! 	if (!init)
! 		if (onexit(close_pipes) == NULL)
! 			return NULL;
! 		else
! 			init++;
! 
! 	if ((name = tempnam((char*)NULL, "pp")) == NULL)
  		return NULL;
  
  	switch (*t) {
  	case 'r':
! 		sprintf(buff, "%s >%s", command, name);
! 		if (system(buff) || (f = fopen(name, "r")) == NULL) {
! 			free(name);
! 			return NULL;
! 		}
! 		if (add(f, command, name, delete)) {
! 			(void)fclose(f);
! 			(void)unlink(name);
! 			free(name);
! 			return NULL;
! 		}
! 		return f;
  	case 'w':
! 		if ((f = fopen(name, "w")) == NULL) {
! 			free(name);
! 			return NULL;
! 		}
! 		if (add(f, command, name, execute)) {
! 			(void)fclose(f);
! 			(void)unlink(name);
! 			free(name);
! 			return NULL;
! 		}
! 		return f;
! 	default:
  		free(name);
  		return NULL;
  	}
  }
  
  int
  mypclose(FILE *f)
  {
! 	struct todo *p, **prev;
! 	char buff[256];
! 	const char *name;
! 	int status;
  
! 	for (p = todolist, prev = &todolist; p; prev = &(p->next), p = p->next)
! 		if (p->f == f) {
! 			*prev = p->next;
! 			name = p->name;
! 			switch (p->what) {
! 			case delete:
! 				free(p);
! 				if (fclose(f) == EOF) {
! 					(void)unlink(name);
! 					status = EOF;
! 				} else if (unlink(name) < 0)
! 					status = EOF;
! 				else
! 					status = 0;
! 				free((void*)name);
! 				return status;
! 			case execute:
! 				(void)sprintf(buff, "%s <%s", p->command, p->name);
! 				free(p);
! 				if (fclose(f) == EOF) {
! 					(void)unlink(name);
! 					status = EOF;
! 				} else if (system(buff)) {
! 					(void)unlink(name);
! 					status = EOF;
! 				} else if (unlink(name) < 0)
! 					status = EOF;
! 				else
! 					status = 0;
! 				free((void*)name);
! 				return status;
! 			default:
! 				return EOF;
! 			}
! 		}
! 	return EOF;
  }
  
  /*
!  * Clean up at the end.  Called by the onexit handler.
   */
! static int
! close_pipes(void)
  {
! 	struct todo    *p;
  
! 	for (p = todolist; p; p = p->next)
! 		(void)mypclose(p->f);
  	return 0;
  }
--- 24,403 ----
   */
  
  /*
!  * Popen and pclose for MS-DOS.  Originally by Diomidis Spinellis.
!  * Many changes 10/1990 by L. Reed to make things more efficient, to
!  * integrate with swapping spawner, to fix bugs, to make mypclose
!  * return the child's exit status as in Unix, and to allow the todo
!  * list to be used for maintaining other temp files.
! 
!  * Now contains a general mechanism for cleaning up temporary files.
!  * This mechanism is used by features other than pipes, in particular
!  * the swap file and the -e file.
! 
!  * These changes were widely distributed in binary format in my 3.041
!  * but did not get into the standard source until at least 4.011. (L. Reed)
   */
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <process.h>
+ #include <fcntl.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <io.h>
+ #include <signal.h>
+ #include <errno.h>
  
! #include "EXTERN.h"
! #include "perl.h"
  
+ /* saved_desc holds the handle number of where we saved our stdin
+    or stdout handle before spawning a child with input redirected.
+    Only one of stdin or stdout can be saved at a time.
+    If saved_desc == -1 it is not in use.
+ 
+    Ed. comment: can you believe DOS was done _after_ Unix?
+ */
+ 
+ static int saved_desc = -1;
+ 
  /*
   * Linked list of things to do at the end of the program execution.
   */
! static struct todo *todolist;
  
+ extern int do_spawn(const char *);	/* swapping system() function */
  
! typedef void SIGHANDLER(int);	/* typedef for a signal handler */
! typedef SIGHANDLER *SIGHPTR;	/* function pointer to a signal handler */
  
+ static SIGHPTR new_sigabrt, new_sigfpe, new_sigint;
+ 
+ static void remove_temp_entry (struct todo *);
+ static void dos_restore_desc(unsigned int);
+ static int dos_save_desc(unsigned int);
+ 
+ static
+ SIGHPTR new_sig(int signum)
+ {
+     void (*old_value)();
+ 
+     old_value = signal(signum, SIG_IGN);
+     return old_value == SIG_DFL ? msdos_sig_death : old_value;
+ }
+ 
+ /* Inhibit signals while creating a temporary file so that the file
+    can't be stranded.  Save the values to be restored.  Note that
+    the restored values are never SIG_DFL, since we don't wan't to
+    strand the temp files.  SIG_DFL is routed to msdos_sig_death, which
+    cleans up and then re-rasies the signal.
+ */
+ 
+ void
+ block_signals(void)
+ {
+     new_sigabrt = new_sig(SIGABRT);
+     new_sigfpe = new_sig(SIGFPE);
+     new_sigint = new_sig(SIGINT);
+ }
+ 
+ void
+ unblock_signals(void)
+ {
+     (void) signal(SIGABRT, new_sigabrt);
+     (void) signal(SIGFPE, new_sigfpe);
+     (void) signal(SIGINT, new_sigint);
+ }
+ 
  /*
   * Add a file f running the command command on file name to the list
   * of actions to be done at the end.  The action is specified in what.
   * Return -1 on failure, 0 if ok.
+  *
+  * Must be preceeded by a call to block_signals and the opening of the file;
+  * calls unblock_signals as a side effect.
   */
! 
! struct todo
! *add_temp_file(FILE *fp,	/* call with either (FILE *) or handle */
! 	      int handle,	/* handle or -1 for use (FILE *) */
! 	      char *command,
! 	      char *name,
! 	      enum action what,
! 	      int stat
! ) {
  	struct todo    *p;
+ 	static init = 0;
  
  	if ((p = (struct todo *) malloc(sizeof(struct todo))) == NULL)
! 	    fatal("Out of memory");
! 
! 	if (!init) {
! 	    if ( atexit(cleanup_msdos_temps) )
! 	    	fatal("Out of memory");
! 	    init = 1;
! 	}
! 
! 	if (handle == -1)
! 	    p->hfd.f = fp;
! 	else
! 	    p->hfd.h = handle;
  	p->command = command;
  	p->name = name;
  	p->what = what;
+ 	p->child_status = stat;
  	p->next = todolist;
  	todolist = p;
! 	unblock_signals();
! 	return p;
  }
  
  FILE *
! mypopen(char *command, const char *t)
  {
  	char buff[256];
  	char *name;
  	FILE *f;
! 	int child_stat;
! 	int our_read_handle;
! 	struct todo *tdp;
  
! 	if ((name = tempnam("", "pp")) == NULL)
  		return NULL;
  
  	switch (*t) {
  	case 'r':
! 	    if (dos_save_desc(1))  /* save STDOUT descriptor and close it */
! 	    	return NULL;
! 
! 		    /* Open child's STDOUT, which is the "pipe" */
! 
! 	    block_signals();	/* unblocked by add_temp_file */
! 	    switch( open(name, O_RDWR | O_CREAT | O_TRUNC | O_TEXT,
! 		    S_IWRITE | S_IREAD) )
! 	    {
! 		case 0:
! 		    (void) dup2(0, 1);	/* STDIN was not open! */
! 		    (void) close(0);	/* now we're fixed up */
! 		    break;
! 
! 		case 1:	/* what we hoped and expected */
! 		    break;
! 		    
! 		default:		/* file open error */
! 		    unlink(name);
! 		    free(name);
! 		    unblock_signals();
! 		    dos_restore_desc(1);
! 		    return NULL;
! 	    }
! 
! 	    tdp = add_temp_file((FILE *)0, 1, NULL, name, han_delete, 0xFF00);
! 	    child_stat = do_spawn(command);	/* run child */
! 
! 	    block_signals();
! 	    (void) lseek(1, 0L, SEEK_SET);	/* rewind child's output */
! 	    our_read_handle = dup(1);		/* we need to read this guy */
! 	    dos_restore_desc(1); 	/* restore our STDOUT, close its dup */
! 	    f = fdopen(our_read_handle, "rt");	/* open a stream to it */
! 
! 	    if (f == NULL) {
! 	    	remove_temp_entry (tdp);
! 		return NULL;
! 	    }
! 
! 	    tdp->hfd.f = f;			/* switch to stream */
! 	    tdp->what = fdelete;
! 	    unblock_signals();
! 
! 	    return f;
! 
  	case 'w':
! 	    block_signals();
! 	    if ((f = fopen(name, "w+t")) == NULL) {
  		free(name);
+ 		unblock_signals();
  		return NULL;
+ 	    }
+ 	    (void) add_temp_file(f, -1, command, name, execute, 0);
+ 	    return f;
+ 
+ 	default:
+ 	    free(name);	/* popen call bug: should be r or w ! */
+ 	    return NULL;
  	}
  }
  
+ /* Remove an item from the todolist */
+ 
+ static void
+ remove_temp_entry (struct todo *tp)
+ {
+     struct todo *p;
+     struct todo **prev;
+ 
+     for (p = todolist, prev = &todolist; p; prev = &(p->next), p = p->next) {
+     	if (p == tp) {
+ 	    unlink(p->name);
+ 	    free(p->name);
+ 	    *prev = p->next;
+ 	    free (p);
+ 	}
+     }
+ }
+ 
+ /* Pclose opertation.  If piping outward, run the child.  If piping inward,
+    simply return the exit status.  In either case clean up the todo
+    list, free up memory, and unlink the file.
+ */
+ 
  int
  mypclose(FILE *f)
  {
!     struct todo *p, **prev;
!     int status;
  
!     for (p = todolist, prev = &todolist; p; prev = &(p->next), p =p->next) {
! 	if (p->hfd.f == f) {
! 	    if (p->what == execute) {
! 		block_signals();
! 		(void) dos_save_desc(0);	/* save our STDIN descriptor */
! 		(void) dup(fileno(f));		/* dup to zero */
! 		fclose(f);
! 		(void) lseek(0, 0L, SEEK_SET); /* rewind for child */
! 
! 		p->hfd.f = 0;		/* temp file now handle zero */
! 		p->what = han_delete;
! 		unblock_signals();
! 		status = do_spawn(p->command);
! 		dos_restore_desc(0); 	/* restore our STDIN, close its dup */
! 	    }
! 	    else if (p->what == fdelete) {
! 		fclose(f);
! 		status = p->child_status; /* from earlier */
! 	    }
! 	    (void) unlink(p->name);
! 	    free(p->name);
! 	    *prev = p->next;
! 	    free(p);
! 	    return status;
! 	}
!     }
!     return -1;	/* not on the list! must be a bug in this code */
  }
  
  /*
!  * Clean up at the end.  Called by the atexit handler.
   */
! void
! cleanup_msdos_temps(void)
  {
!     register struct todo *p;
!     struct todo *swp;
!     struct todo **prev;
  
! 	/* Handle all but swap file */
! 
!     prev = &todolist;
!     for (p = todolist; p; p = p->next) {
!     	
!     	switch (p->what) {
! 	    case fdelete:
! 	    case execute:
! 	    	mypclose(p->hfd.f);
! 		break;
! 
! 	    case han_delete:
! 		(void) close(p->hfd.h);
! 		(void) unlink(p->name);
! 		free(p->name);
! 	    	*prev = p->next;
! 		free(p);
! 		break;
! 	    	
! 	    case swap_file:
! 	    	swp = p;	/* skip momentarily */
! 	    	break;
! 	}
!     }
! 
! 	    /* Handle swap file */
! 
!     (void) close( swp->hfd.h );
!     (void) unlink( swp->name );
!     free(swp->name);
!     free(swp);
!     todolist = 0;
! }
! 
! /* Default signal handler used instead of allowing MS-DOS to kill the
!    process.  ^C won't run atexit list, so we have to catch the signal.
!    Perl allows the user to program signal handling, complicating this
!    whole business.
! */
! 
! void msdos_sig_death(int signum)
! {
!     register struct todo *p;
!     SIGHPTR save_sigabrt, save_sigfpe, save_sigint;
! 
!     save_sigabrt = signal(SIGABRT, SIG_IGN);
!     save_sigint = signal(SIGINT, SIG_IGN);
!     save_sigfpe = signal(SIGFPE, SIG_IGN);
! 
!     for (p = todolist; p; p = p->next) {
!     	switch (p->what) {
! 	    case fdelete:
! 	    case han_delete:
! 	    	(void) fclose(p->hfd.f);
! 		break;
! 
! 	    default:
! 	    	(void) close(p->hfd.h);
! 		break;
! 	}
! 	(void) unlink(p->name);
!     }
!     signal(SIGABRT, save_sigabrt);
!     signal(SIGINT, save_sigint);
!     signal(SIGFPE, save_sigfpe);
! 
!     signal(signum, SIG_DFL);	/* set real default (== death) */
!     raise(signum);		/* do it */
! }
! 
!     /* Save our STDOUT (1) or STDIN (0) in preparation for spawning
!        a subprocess that is to have STDIN or STDOUT redirected.
! 
!        Call with 0 or 1 only.
!        Note use of dos_dup, not dup: we're are end-running the C library's
!        knowledge of stream I/O and text vs. binary.
!    */
! 
! static int dos_save_desc(unsigned int handle)
! {
!     saved_desc = dos_dup(handle);
!     if (saved_desc >= 0) {
! 	(void) dos_close(handle);
  	return 0;
+     }
+ 
+     if (errno == EBADF) {	/* there was no open descriptor: */
+     	return 0;		/*   that's okay */
+     }
+ 
+     	/* Failed, probably for a lack of file descriptors */
+ 
+     return -1;
+ }
+ 
+ static void dos_restore_desc(unsigned int handle)
+ {
+     if (saved_desc == -1)	/* was there anything to restore ? */
+     	return;
+ 
+     (void) dos_dup2(saved_desc, handle);
+     (void) dos_close(saved_desc);
+ 
+     saved_desc = -1;
  }
