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 #include #include ! /* ! * 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 #include #include + #include + #include + #include + #include + #include + #include ! #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; }