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/mksargv.c.old	Sun Feb 23 08:48:16 1992
--- msdos/mksargv.c	Thu Nov 14 08:56:34 1991
***************
*** 0 ****
--- 1,551 ----
+ /* RCS      -- $Header: c:/usr/lbr/perl/RCS/mksargv.c 1.1 90/10/15 15:16:56 lbr Exp $
+ -- SYNOPSIS -- MKS argument handling.
+ -- 
+ -- DESCRIPTION
+ --	Pulls MKS-style arugments out of the environment or runs
+ --	MKS glob.exe or runs Microsoft ___setargv as needed.
+ --
+ -- AUTHOR
+ --	Len Reed, ..!gatech!holos0!lbr or holos0!lbr@gatech.edu.
+ --	Holos Software, Inc., Tucker, Ga.
+ --
+ -- COPYRIGHT
+ --      Copyright (c) 1990 by Leonard Reed.  All rights reserved.
+ --
+ --	Though this program interfaces with MKS software, it contains
+ --	no Mortice Kern Systems code.  For information on the MKS
+ --	tools, write Mortice Kern Systems, Inc.  35 King Street North,
+ --	Waterloo, Ontario, Canada N2J2W9, or ..!uunet!watmath!mks!inquiry.
+ -- 
+ --      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:	mksargv.c $
+  * Revision 1.1  90/10/15  15:16:56  lbr
+  * Initial revision
+  * 
+ */
+ 
+ #include <stdlib.h>
+ #include <string.h>
+ #include <stdio.h>
+ #include <errno.h>
+ #include "ms-dos.h"
+ 
+     /* This code is specific to the Microsoft C 5.x and 6.0 compilers
+        It should work with small, medium, compact, or large memory model,
+        but has been thoroughly tested only with large model.
+ 
+        MSC 6.0 defines _MSC_VER as 600.  MSC 5.1 (and I guess 5.0) doesn't
+        define it at all.
+     */
+ 
+ #if !defined(_MSC_VER)
+ # define _MSC_VER 500		/* a guess, of course */
+ #endif
+ 
+ #if _MSC_VER < 600
+ # define _far far		  /* 5.x uses far not _far */
+ # define _based(x) far		  /* 5.x doesn't have _based ptrs */
+   typedef unsigned int _segment;  /* 5.x doesn't have _segment type */
+ #endif	/* _MSC_VER < 600 */
+ 
+ #if defined(M_I86SM) || defined(M_I86MM)
+ # define SMALL_DATA_POINTERS 1
+ # if _MSC_VER < 600
+ #	error "no far memcpy in MSC 5.x"
+ # endif
+ #else
+ # define SMALL_DATA_POINTERS 0
+ # define _fmemcpy memcpy	/* need by MSC 5.x and fine in 6.0 */
+ #endif
+ 
+ extern void *__argv;	/* used by crt0.obj in Microsoft C library
+ 			   to set main's argv.
+ 			*/
+ 
+ extern unsigned int __argc;	/* used by crt0.obj in Microsoft C library
+ 				   to set main's argc.
+ 				*/
+ 
+ extern void _far *get_glob_space(void);		/* get buffer for globbing */
+ extern unsigned int spawn_mks_glob(char *);	/* run MKS glob program */
+ extern void glob_problem(int);			/* MKS glob failed */
+ 
+ typedef struct {
+     unsigned char various_1[0x2C];	/* various stuff */
+     _segment ev_seg;			/* environment segment */
+     unsigned char various_2[0x52];	/* some more junk */
+     unsigned char com_tail_size;	/* MS-DOS command tail length */
+     char command_tail[127];		/* MS-DOS command tail */
+ } PSP;
+ 
+ static char *mks_glob = 0;	/* GLOB string, if found in environment */
+ static char *mks_rootdir = 0;	/* ROOTDIR string, if found in environment */
+ static int mksargs = 0;		/* true if MKSARGS found in environment */
+ 
+ static char _far *_pgm_ptr;	/* DOS argv[0] string (MS-DOS 3.0 or better) */
+ 
+ /****
+     Handle MKS compatible argument passing.  Called from special
+     version of the startup code (_setenvp).  MKS calling sequence
+     puts the arguments at the head of the environment.  Each argument
+     is preceeded by a tilde ('~'); the first item without a tilde is
+     the start of the real environment.  This allows a program like
+     the Korn shell to expand wild cards and pass long lists of arguments,
+     as in Unix.  Under normal MS-DOS, you only get 128 bytes.
+ 
+     All argv[] and environment pointers are put on to the stack.
+     The actual argument strings are also copied on to the stack, since
+     even for _far data pointer models we need to terminate argv[] with
+     a null pointer.  The actual environment strings are copied only for
+     small data pointer models; for compact, large, and huge models we
+     just set the _far pointers pointing into the original environment segment.
+     We have max_bytes of space for this purpose starting at &where,
+     but we leave space starting at &where unused.  If we need total
+     bytes, then we allocate them starting at (&where + max_bytes - total).
+     The additional (max_bytes - total) space is then freed by the caller
+     and can be used for subsequent stack allocations.
+ 
+     This routine returns the amount of space used, in bytes, or -1 if
+     it required more than max_bytes.
+ ****/
+ 
+ int mks_init(
+     const unsigned int max_bytes,	/* maximum bytes we can use
+     					   for enviroment
+ 					   and arguments.
+ 					*/
+     char where			/* max_bytes worth of space */
+ ) {
+ #if _MSC_VER >= 600
+     _segment my_psp;		/* program segment prefix for this program */
+     _segment my_eseg;		/* our environment segment */
+ #endif
+ 
+     PSP _based(my_psp) *p;	/* pointer to the prefix block */
+     char _based(my_eseg) *evp;	/* pointer to passed-in environment data */
+     char _based(my_eseg) *ev_this;
+     char _based(my_eseg) *av_this;
+ 
+ #if SMALL_DATA_POINTERS
+     char *ev_strings;		/* used in copy over to stack */
+     int envp_bytes;		/* bytes needed for environ strings */
+ #else
+ # define envp_bytes 0		/* we'll use _far originals */
+ #endif
+     char *av_strings;		/* used in copy over to stack */
+     unsigned int argv_bytes;	/* number of bytes needed for arg strings */
+ 
+     int env_count;	/* count of items in the environmet */
+     int total;		/* number of bytes of space needed */
+     char **av_array, **ev_array;
+     register unsigned int i;
+ 
+ 
+ #if _MSC_VER >= 600
+     evp = av_this = ev_this = 0;	/* start at offset 0 in env seg */
+     p = 0;				/* offset zero in PSP seg */
+     my_psp = (_segment) _psp;		/* the PSP of this program */
+     my_eseg = p->ev_seg;		/* the environment segment */
+ #else
+     p = MK_FP(_psp, 0);
+     evp = MK_FP(p->ev_seg, 0);
+     av_this = ev_this = evp;
+ #endif
+ 
+     	/* Count the number of MKS-style arguments (leading '~') in
+ 	   the enviroment.
+ 	*/
+ 
+     for (i = 0; *evp == '~'; i++) {
+     	while (*evp++)	/* skip the actual string */
+ 	    ;
+     }
+ 
+     	/* Compute number of arguments found and number of bytes
+ 	   needed for the arguments strings (less the tildes).  Round
+ 	   up to even number.
+ 	*/
+ 
+     __argc = i;
+     argv_bytes = ((evp - ev_this - i) + 1) & ~1;
+ 
+     	/* Count the environment strings */
+ 
+     ev_this = evp;
+     for (i = 0; *evp; i++) {
+     	while (*evp++)	/* skip the actual string */
+ 	    ;
+     }
+ 
+ 	/* MS-DOS 3.0 and better puts command name after environment.
+ 	   We'll use this if we run MKS glob.  No apologies to DOS 2.x
+ 	   users: it's time to upgrade.
+ 	*/
+ 
+     _pgm_ptr = evp + 3;
+ 
+     	/* Compute bytes needed, rounded up to an even number */
+ 
+     env_count = i;
+ #if SMALL_DATA_POINTERS
+     envp_bytes = ((evp - ev_this) + 1) & ~1;
+ #endif
+ 
+ 	/* Make sure that the stuff that looked like arguments
+ 	   was for this program.  (If an MKS command runs a non-MKS
+ 	   command that runs us, the MKS arguments for our parent
+ 	   will probably still be in the enviroment.)  Look at the
+ 	   end of the command tail to disambiguate this: MKS commans
+ 	   put "\r\n" after the command tail; command.com puts "\r".
+ 	*/
+ 
+     i = p->com_tail_size;
+     if (p->command_tail[i+1] != '\n' || p->command_tail[i] != '\r') {
+ 	__argc = 0;
+ 	argv_bytes = 0;
+     }
+ 
+     	/* Do we have enough space? Don't forget the NULL pointers at
+ 	   the end of the argv[] and environment arrays.
+ 	*/
+ 
+     total = sizeof (char *) * (__argc + env_count + 2)
+ 		+ argv_bytes + envp_bytes;
+ 
+     if (total > max_bytes)
+     	return -1;
+ 
+     	/* Figure out where to put all the data */
+ 
+     __argv = av_array = (char **) (&where + (max_bytes - total));
+     av_strings = (char *) (av_array + __argc + 1);
+     environ = ev_array = (char **) (av_strings + argv_bytes);
+ #if SMALL_DATA_POINTERS
+     ev_strings = (char *) (ev_array + env_count + 1);
+ #endif
+ 
+     	/* Set the enviroment data pointers, skipping the _C_FILE_INFO
+ 	   string, if found.  We've allocated the space, though, for
+ 	   the pointer (and the string, if small data pointers);
+ 	   the space will just get wasted at the end.
+ 	*/
+ 
+     while ( *ev_this ) {
+ #if SMALL_DATA_POINTERS
+     	*ev_array = ev_strings;
+     	while (*ev_strings++ = *ev_this++)
+ 	    ;
+ #else
+     	*ev_array = ev_this;
+     	while (*ev_this++)
+ 	    ;
+ #endif
+ 	if (strncmp(*ev_array, "_C_FILE_INFO", 12) == 0) {
+ #if SMALL_DATA_POINTERS
+ 	    ev_strings = *ev_array;	/* back up over copied string,
+ 	    				   just to keep things neat
+ 					*/
+ #endif
+ 	}
+ 	else {
+ 	    if (strncmp(*ev_array, "MKSARGS=", 7) == 0) {
+ 	    	mksargs = 1;
+ 	    }
+ 	    else if (strncmp(*ev_array, "GLOB=", 5) == 0) {
+ 	    	mks_glob = (*ev_array) + 5;
+ 	    }
+ 	    else if (strncmp(*ev_array, "ROOTDIR=", 8) == 0) {
+ 	    	mks_rootdir = (*ev_array) + 8;
+ 	    }
+ 	    ++ev_array;	/* move forward */
+ 	}
+     }
+     *ev_array = (char *)0;
+ 
+     	/* If __argc is zero, we got a non-MKS call.  Take the space for
+ 	   a null argument list (one argv[0] pointer) out of the total
+ 	   and return.  The caller will see that __argc is null and call
+ 	   mks_glob in this routine.
+ 	*/
+ 
+     if (__argc == 0)
+ 	return total - sizeof(char *);
+ 
+     	/* Set the argv[] pointers, setting just past the '~' character */
+ 
+     for (i = 0; i < __argc; i++) {
+     	*av_array++ = av_strings;		/* set argv[] pointer */
+ 	++av_this;				/* skip the tilde */
+     	while (*av_strings++ = *av_this++)	/* copy the string */
+ 	    ;
+     }
+     *av_array = (char *)0;
+ 
+     return total;	/* return number of bytes of stack we used */
+ }
+ 
+     /* Non MKS-call.  If GLOB or ROOTDIR was found, try running MKS glob.
+        If neither is set, unlike MKS commands we won't bother trying
+        /etc/glob.exe.
+ 
+        Return value is -1 (error, not enough stack space).
+        			0 (use Microsoft globber).
+ 		    other (successful MKS globbing, value is stack space used.
+     */
+ 
+ int mks_globber(
+     const unsigned int max_bytes,	/* max bytes we can use for args */
+     char where				/* max_bytes worth of space */
+ ) {
+ #if _MSC_VER >= 600
+     _segment my_psp;		/* program segment prefix for this program */
+ #endif
+     PSP _based(my_psp) *p;	/* pointer to the prefix block */
+     char tail_copy[128];
+     int tail_size;
+     void _far *gbuffer;
+     char _far *gp;
+     char *tp;
+     int i, mode;
+     unsigned int glob_rcode;
+     int argv_bytes;
+     int total;
+     char **av_array;
+     char *av_strings;
+     char glob_name[100];
+ 
+     if (mksargs == 0)		/* if no MKS support return */
+     	return 0;
+ 
+ #if _MSC_VER >= 600
+     my_psp = (_segment) _psp;	/* the PSP of this program */
+     p = 0;
+ #else
+     p = MK_FP(_psp, 0);
+ #endif
+ 
+     tail_size = p->com_tail_size;
+     _fmemcpy(tail_copy, p->command_tail, tail_size);
+     tail_copy[tail_size] = '\0';	/* make it a C string */
+ 
+     	/* If the command tail doesn't have any metacharacters, we
+ 	   can pass it to the Microsoft globber without fear.
+ 	*/
+ 
+     if (strcspn(tail_copy, KSH_META_CHARS) == tail_size)
+     	return 0;	/* let's run Microsoft's code */
+ 
+ 	/* Run the MKS glob program: $GLOB or $ROOTDIR/etc/glob.exe
+ 	   or /etc/glob.exe.
+ 	*/
+ 
+     if (mks_glob == (char *)0) {
+     	if (mks_rootdir) {
+ 	    mks_glob = strcpy(glob_name, mks_rootdir);
+ 	    (void) strcat(mks_glob, "/etc/glob.exe");
+ 	}
+ 	else
+ 	    mks_glob = "/etc/glob.exe";
+     }
+ 
+     gbuffer = get_glob_space();		/* get space for globbing */
+     if (gbuffer == (void _far *)0)
+     	return -1;
+ 
+     gp = gbuffer;
+     tp = tail_copy;
+ 
+     	/* Run through the command tail, splitting on white space into
+ 	   arguments and dealing with quotes.  There is no good way
+ 	   to deal with something like "ab*"*.c since we can't both
+ 	   expand and not expand the argument.  Tough.  For this reason,
+ 	   anything that starts with a quote is not expanded; anything
+ 	   that doesn't start with a quote is expanded.
+ 	*/
+ 
+ 	/* mode == ' '	 white space between words
+ 		   '\''	 inside single quotes
+ 		   '"'	 inside double quotes
+ 		   1	 proccessing an argument, not in quotes
+ 	*/
+ 
+     mode = ' ';
+     while (i = *tp++) {
+     	switch (i) {
+ 	    case ' ':
+ 	    case '\t':
+ 		if (mode == '\'' || mode == '"') {
+ 		    *gp++ = i;	/* retain quoted space */
+ 		}
+ 		else if (mode == 1) {
+ 		    *gp++ = 0;	/* space indicates end of argument */
+ 		    mode = ' ';
+ 		}
+ 		/* else ignore additional spaces */
+ 		break;
+ 
+ 	    case '\'':
+ 	    case '"':
+ 	    	if (mode == ' ') {
+ 		    mode = i;		/* quote to start argument */
+ 		    *gp++ = '~';	/* tell glob not to expand this */
+ 		}
+ 		else if (mode == i) {
+ 		    mode = 1;		/* close quote */
+ 		}
+ 		else {
+ 		    *gp++ = i;		/* literal character */
+ 		}
+ 		break;
+ 
+ 	    default:
+ 	    	if (mode == ' ') {
+ 		    *gp++ = '*';	/* tell glob to expand this one */
+ 		    mode = 1;		/* inside an argument */
+ 		}
+ 		*gp++ = i;		/* add the character */
+ 		break;
+ 	}
+     }
+ 
+     if (mode != ' ')	/* if no trailing space, close last argument */
+ 	*gp++ = 0;
+     *gp = 0;		/* end of list marked by second null */
+ 
+ 
+ 	/* Spawn the MKS glob program.
+ 	   returns (0xFF00 | dos_return_code) for spawn call (int 21h, function
+ 	   4bh) failure.  Otherwise returns AX value from get return code
+ 	   (int 21h, function 4dh)).
+ 	*/
+ 
+     glob_rcode = spawn_mks_glob(mks_glob);
+     switch (glob_rcode & 0xFF00) {
+ 
+ 	case 0xFF00:	/* spawn failure */
+ 	    switch (glob_rcode & 0xFF) {
+ 	    	case 2:
+ 		    i = 0;	/* glob not found */
+ 		    break;
+ 
+ 		case 0x8:
+ 		    i = 1;	/* not enough space */
+ 		    break;
+ 
+ 		default:
+ 		    i = 2;	/* some other problem running glob */
+ 		    break;
+ 	    }
+ 	    glob_problem(i);	/* message and die */
+ 	    /* no return from glob_problem */
+ 
+ 	case 0x100:	/* ^break kill */
+ 	case 0x200:	/* critical error */
+ 	case 0x300:	/* glob was a TSR! */
+ 	default:	/* killed by ^C, critical error, TSR exit, or
+ 			   something not documented.
+ 			*/
+ 	    _exit(1);
+ 
+ 	case 0:		/* glob ran to completion */
+ 	    break;
+     }
+ 
+     	/* Glob ran to completion.  Did it work? */
+ 
+     if (glob_rcode) {
+ 	switch (glob_rcode) {
+ 	    case E2BIG:
+ 		i = 3;	/* expanded argument list too long */
+ 		break;
+ 
+ 	    case ENOMEM:
+ 		i = 1;	/* not enough space for glob to work */
+ 		break;
+ 
+ 	    default:
+ 		i = 2;	/* unknown glob problem */
+ 		break;
+ 	}
+ 	glob_problem(i);
+ 	/* no return from glob_problem */
+     }
+ 
+     	/* Count the number of arguments after expansion.  Start with
+ 	   one, not zero, because argv[0] isn't there.
+ 	*/
+ 
+     gp = gbuffer;
+     for (i = 1; *gp ; i++) {
+     	while (*gp++)	/* skip the actual string */
+ 	    ;
+     }
+ 
+     	/* Compute number of arguments found and number of bytes
+ 	   needed for the arguments strings (less the tildes).  Round
+ 	   up to even number.
+ 	*/
+ 
+     __argc = i;
+     argv_bytes = ((gp - gbuffer - i) + 1) & ~1;
+ 
+     	/* Do we have enough space? Don't forget the NULL pointers at
+ 	   the end of the argv[] array.
+ 	*/
+ 
+     total = sizeof (char *) * (__argc + 1) + argv_bytes;
+ 
+ #if SMALL_DATA_POINTERS
+     gp = _pgm_ptr;
+     for (i = 1; *gp++; i++)	/* strlen on _far data, count final '\0' */
+     	;
+     total += i;			/* add space for argv[0] string */
+ #endif
+ 
+     if (total > max_bytes)
+     	return -1;
+ 
+     	/* Figure out where to put the data */
+ 
+     __argv = av_array = (char **) (&where + (max_bytes - total));
+     av_strings = (char *) (av_array + __argc + 1);
+ 
+ #if SMALL_DATA_POINTERS
+     _fmemcpy(av_strings, _pgm_ptr, i);
+     *av_array++ = av_strings;
+     av_strings += i;
+ #else
+     *av_array++ = _pgm_ptr;	/* arg 0 (more or less) in MS-DOS 3.0 */
+ #endif
+ 
+ 	/* Copy the arguments on to the stack */
+ 
+     gp = gbuffer;
+ 
+     for (i = 1; i < __argc; i++) {
+     	*av_array++ = av_strings;	/* set argv[] pointer */
+ 	++gp;				/* skip the tilde */
+     	while (*av_strings++ = *gp++)	/* copy the string */
+ 	    ;
+     }
+     *av_array = (char *)0;
+ 
+     return total;
+ }
