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