delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/1996/08/18/06:10:56

Date: Sun, 18 Aug 1996 13:06:26 +0200 (IST)
From: Eli Zaretskii <eliz AT is DOT elta DOT co DOT il>
To: DJ Delorie <dj AT delorie DOT com>
Cc: djgpp-workers AT delorie DOT com
Subject: Re: `system' and wildcard expansion
In-Reply-To: <199608111354.JAA08681@delorie.com>
Message-Id: <Pine.SUN.3.91.960818130006.1338B-100000@is>
Mime-Version: 1.0

On Sun, 11 Aug 1996, DJ Delorie wrote:

> Personally, I wouldn't mess with a proxy's argv[0] at all. 

I didn't.  For the time being, I left that issue alone.  The changes below
take care of all the other problems I know of in `system' and `spawnXX',
except one: long command lines in the style of Windows 95.  I'm not sure
how bad do we want that support now, and some of the code needs to go into
the startup code (if we want to be able to get long command lines from
Windows), so will bloat every image... 

The code that invokes Unix-style scripts was tested by Daisuke Aoyama
<jack AT st DOT rim DOT or DOT jp> with his port of `bash' and seems to work.

The changes in the startup code to support the new functionality were 
mailed with the LFN stuff in my other message.

---------------------------- cut here --------------------------------
*** src/libc/dos/process/dosexec.cdj	Wed Jul 24 00:35:38 1996
--- src/libc/dos/process/dosexec.c	Sat Aug 17 15:35:00 1996
***************
*** 5,21 ****
--- 5,27 ----
  #include <stdlib.h>
  #include <string.h>
  #include <errno.h>
+ #include <limits.h>
  #include <fcntl.h>
  #include <unistd.h>
  #include <process.h>
  #include <go32.h>
  #include <dpmi.h>
  #include <ctype.h>
+ #include <sys/movedata.h>
  #include <libc/dosexec.h>
  #include <libc/unconst.h>
  #include <libc/dosio.h>
  #include <libc/farptrgs.h>
  
+ /* FIXME: this is not LFN-clean.  Win95 has a way to
+    pass long command lines, but we don't support it here.  */
+ #define CMDLEN_LIMIT 125
+ 
  extern char **environ;
  
  int __dosexec_in_system = 0;
***************
*** 32,55 ****
  
  static Execp parm;
  
! static unsigned long tbuf;
  
! char *__unquote (char *, const char *, const char *);
  
  static unsigned long talloc(size_t amt)
  {
!   unsigned long rv = tbuf;
!   tbuf += amt;
    return rv;
  }
  
! static int direct_exec_tail(const char *program, const char *args, char * const envp[])
  {
    __dpmi_regs r;
    unsigned long program_la;
    unsigned long arg_la;
    unsigned long parm_la;
    unsigned long env_la, env_e_la;
    char arg_header[3];
    char short_name[FILENAME_MAX];
    const char *progname;
--- 38,158 ----
  
  static Execp parm;
  
! static unsigned long tbuf_ptr;
! static unsigned long tbuf_beg;
! static unsigned long tbuf_end;
! static unsigned long tbuf_len;
! #if 0
! static int	     tbuf_selector;
! #endif
  
! static int script_exec(const char *, char **, char **);
  
+ /* Allocate AMT bytes off the transfer buffer.  */
  static unsigned long talloc(size_t amt)
  {
!   unsigned long rv = tbuf_ptr;
!   tbuf_ptr += amt;
    return rv;
  }
  
! /* Make sure we can allocate AMT bytes off the transfer buffer
!    without overflowing it.  Return non-zero if we can, zero otherwise.
! 
!    WARNING: This can relocate the data already in the transfer buffer,
! 	    so all linear addresses which use it should be relative to
! 	    TBUF_BEG!  */
! static int check_talloc(size_t amt)
! {
!   int retval = 1;
! 
!   if (tbuf_ptr + amt > tbuf_end)
!   {
! #if 0
!     /* Code that reallocs the transfer buffer; currently disabled.  */
!     unsigned long new_tb;
!     unsigned long min_len = tbuf_len + amt;
!     unsigned long max_len = 0x10000; /* 64KB */
!     int old_selector = tbuf_selector;
!     int max_avail;
!     int e = errno;
! 
!     errno = E2BIG;
! 
!     /* Try to allocate new, larger DOS buffer, upto 64KB.  */
!     if (min_len > max_len)
!     {
!       retval = 0;
!       goto done;
!     }
!     while (tbuf_len <= max_len && tbuf_len < min_len)
!       tbuf_len *= 2;
!     if (tbuf_len < min_len)
!     {
!       retval = 0;
!       goto done;
!     }
! 
!     tbuf_len = (tbuf_len + 15) & 0xffff0; /* round to nearest paragraph */
! 
!     if ((new_tb =
! 	 __dpmi_allocate_dos_memory(tbuf_len/16, &max_avail)) == -1)
!     {
!       if (max_avail*16 < min_len
! 	  || (new_tb =
! 	      __dpmi_allocate_dos_memory(max_avail, &tbuf_selector)) == -1)
!       {
! 	retval = 0;
! 	goto done;
!       }
!       tbuf_len = max_avail*16;
!     }
!     else
!       tbuf_selector = max_avail;
! 
!     new_tb *= 16;  /* convert to linear address */
!     movedata (_dos_ds, tbuf_beg, _dos_ds, new_tb, tbuf_ptr - tbuf_beg);
!     tbuf_ptr = new_tb + tbuf_ptr - tbuf_beg;
!     tbuf_beg = new_tb;
!     tbuf_end = tbuf_beg + tbuf_len - 1;
! 
!     errno = e;
! 
!   done:
!     /* Assume caller will return immediately in case of
!        failure to reallocate, so they won't need the old data.  */
!     if (!retval)
!       tbuf_selector = 0;
!     if (old_selector)
!       __dpmi_free_dos_memory(old_selector);
! #else
!     errno = E2BIG;
!     retval = 0;
! #endif
!   }
! 
!   return retval;
! }
! 
! extern char   __PROXY[];	/* defined on crt0/crt1.c */
! extern size_t __PROXY_LEN;
! 
! /* Functions that call `direct_exec_tail' after they've put
!    some data into the transfer buffer, should set LFN parameter
!    to either 0 (no LFN support) or 1 (LFN supported), but NOT 2!
!    if LFN is 2, there is a possiblity that the contents of the
!    transfer buffer will be overrun!  */
! static int
! direct_exec_tail(const char *program, const char *args,
! 		 char * const envp[], const char *proxy, int lfn)
  {
    __dpmi_regs r;
    unsigned long program_la;
    unsigned long arg_la;
    unsigned long parm_la;
    unsigned long env_la, env_e_la;
+   size_t proxy_len = proxy ? strlen(proxy)+1 : 0;
+   int seen_proxy = 0;
    char arg_header[3];
    char short_name[FILENAME_MAX];
    const char *progname;
***************
*** 59,87 ****
    
    sync();
  
    /* The pathname of the executable to run.  */
    proglen = strlen(program)+1;
!   if(_USE_LFN) {
!     dosmemput(program, proglen, tbuf);
      r.x.ax = 0x7160;			/* Truename */
      r.x.cx = 1;				/* Get short name */
!     r.x.ds = r.x.es = tbuf / 16;
!     r.x.si = r.x.di = tbuf & 15;
      __dpmi_int(0x21, &r);
      if (r.x.flags & 1)
      {
        errno = __doserr_to_errno(r.x.ax);
        return -1;
      }
!     dosmemget(tbuf, FILENAME_MAX, short_name);
      progname = short_name;
      proglen = strlen(short_name)+1;
    } else
      progname = program;
!   
    program_la = talloc(proglen);
!   arg_la = talloc(strlen(args)+3);
!   parm_la = talloc(sizeof(Execp));
  
    dosmemput(progname, proglen, program_la);
  
--- 162,197 ----
    
    sync();
  
+   if (lfn == 2)		/* don't know yet */
+     lfn = _USE_LFN;
+ 
    /* The pathname of the executable to run.  */
    proglen = strlen(program)+1;
!   if (!check_talloc(proglen))
!     return -1;
!   if(lfn) {
!     dosmemput(program, proglen, tbuf_ptr);
      r.x.ax = 0x7160;			/* Truename */
      r.x.cx = 1;				/* Get short name */
!     r.x.ds = r.x.es = tbuf_ptr / 16;
!     r.x.si = r.x.di = tbuf_ptr & 15;
      __dpmi_int(0x21, &r);
      if (r.x.flags & 1)
      {
        errno = __doserr_to_errno(r.x.ax);
        return -1;
      }
!     dosmemget(tbuf_ptr, FILENAME_MAX, short_name);
      progname = short_name;
      proglen = strlen(short_name)+1;
    } else
      progname = program;
! 
!   if (!check_talloc(proglen + strlen(args) + 3 + sizeof(Execp) + 32))
!     return -1;
    program_la = talloc(proglen);
!   arg_la     = talloc(strlen(args)+3);
!   parm_la    = talloc(sizeof(Execp));
  
    dosmemput(progname, proglen, program_la);
  
***************
*** 132,153 ****
    } while (env_la & 15);
    talloc(-1);
  
!   /* The environment, terminated by an extra NULL char.  */
    for (i=0; envp[i]; i++)
    {
!     env_e_la = talloc(strlen(envp[i])+1);
!     dosmemput(envp[i], strlen(envp[i])+1, env_e_la);
    }
    arg_header[0] = 0;
  
    /* The name of the program that owns the environment.  */
    arg_header[1] = 1;	/* the number of strings (1, little-endian) */
    arg_header[2] = 0;
    dosmemput(arg_header, 3, talloc(3));
    env_e_la = talloc(proglen);
    dosmemput(progname, proglen, env_e_la);
  
    /* Prepare the parameter block and call Int 21h/AX=4B00h.  */
    parm.eseg     = env_la / 16;
    parm.argseg	= arg_la / 16;
    parm.argoff	= arg_la & 15;
--- 242,314 ----
    } while (env_la & 15);
    talloc(-1);
  
! #if 0
!   /* Convert to relative, since `check_talloc' may relocate.  */
!   arg_la  -= tbuf_beg;
!   env_la  -= tbuf_beg;
!   fcb1_la -= tbuf_beg;
!   fcb2_la -= tbuf_beg;
!   parm_la -= tbuf_beg;
!   program_la -= tbuf_beg;
! #endif
!   if (!check_talloc(0))
!     return -1;
! 
!   /* The environment.  Replace the !proxy variable, if there is
!      one (for nested programs) if we are called from `system',
!      or skip it, if we are called from `spawnXX'.  */
    for (i=0; envp[i]; i++)
    {
!     const char *ep = envp[i];
!     size_t env_len = strlen(ep)+1;
! 
!     if (strncmp(ep, __PROXY, __PROXY_LEN) == 0 && ep[__PROXY_LEN] == '=')
!     {
!       seen_proxy = 1;
!       if (proxy)
!       {
! 	ep = proxy;
! 	env_len = proxy_len;
!       }
!       else
! 	continue;
!     }
!     if (!check_talloc(env_len))
!       return -1;
!     env_e_la = talloc(env_len);
!     dosmemput(ep, env_len, env_e_la);
!   }
! 
!   /* If no !proxy variable was found, create one.  */
!   if (proxy && !seen_proxy)
!   {
!     if (!check_talloc(proxy_len))
!       return -1;
!     env_e_la = talloc(proxy_len);
!     dosmemput(proxy, proxy_len, env_e_la);
    }
+ 
+   /* Terminate by an extra NULL char.  */
    arg_header[0] = 0;
  
    /* The name of the program that owns the environment.  */
    arg_header[1] = 1;	/* the number of strings (1, little-endian) */
    arg_header[2] = 0;
+   if (!check_talloc(3 + proglen))
+     return -1;
    dosmemput(arg_header, 3, talloc(3));
    env_e_la = talloc(proglen);
    dosmemput(progname, proglen, env_e_la);
  
    /* Prepare the parameter block and call Int 21h/AX=4B00h.  */
+ #if 0
+   arg_la  += tbuf_beg;
+   env_la  += tbuf_beg;
+   fcb1_la += tbuf_beg;
+   fcb2_la += tbuf_beg;
+   parm_la += tbuf_beg;
+   program_la += tbuf_beg;
+ #endif
    parm.eseg     = env_la / 16;
    parm.argseg	= arg_la / 16;
    parm.argoff	= arg_la & 15;
***************
*** 163,168 ****
--- 324,334 ----
    r.x.es = parm_la / 16;
    r.x.bx = parm_la & 15;
    __dpmi_int(0x21, &r);
+ #if 0
+   if (tbuf_selector)
+     __dpmi_free_dos_memory (tbuf_selector);
+   tbuf_selector = 0;
+ #endif
    if (r.x.flags & 1)
    {
      errno = __doserr_to_errno(r.x.ax);
***************
*** 189,199 ****
  int
  _dos_exec(const char *program, const char *args, char * const envp[])
  {
!   tbuf = __tb;
!   return direct_exec_tail(program, args, envp);
  }
  
! static char GO32_STRING[] = "go32.exe";
  
  /* A list of known shells which require we DON'T quote command
     lines that are passed to them with the /c or -c switch.  */
--- 355,368 ----
  int
  _dos_exec(const char *program, const char *args, char * const envp[])
  {
!   tbuf_beg = tbuf_ptr = __tb;
!   tbuf_len = _go32_info_block.size_of_transfer_buffer;
!   tbuf_end = tbuf_beg + tbuf_len - 1;
!   return direct_exec_tail(program, args, envp, 0, 2);
  }
  
! static char GO32_V2_STRING[] = "go32-v2.exe";
! static char GO32_STRING[]    = "go32.exe";
  
  /* A list of known shells which require we DON'T quote command
     lines that are passed to them with the /c or -c switch.  */
***************
*** 210,215 ****
--- 379,386 ----
    "SH.EXE",
    "SH16.EXE",
    "SH32.EXE",
+   "TCSH.EXE",
+   "BASH.EXE",
    0
  };
  
***************
*** 235,241 ****
  
    /* PROGRAM can be a shell which expects a single argument
       (beyond the /c or -c switch) that is the entire command
!      line.  With some shell, we must NOT quote that command
       line, because that will confuse the shell.
  
       The hard problem is to know when PROGRAM names a shell
--- 406,412 ----
  
    /* PROGRAM can be a shell which expects a single argument
       (beyond the /c or -c switch) that is the entire command
!      line.  With some shells, we must NOT quote that command
       line, because that will confuse the shell.
  
       The hard problem is to know when PROGRAM names a shell
***************
*** 265,272 ****
      }
    }
  
-   tbuf = __tb;
- 
    arglen = 0;
    for (i=1; argv[i]; i++)
      arglen += 2*strlen(argv[i]) + 1 + 2;
--- 436,441 ----
***************
*** 278,286 ****
      int quoted = 0;
      const char *p = argv[i];
  
!     /* FIXME: 125 is not LFN-clean.  Should we support long
!        command lines a-la Win95?  */
!     if (argp - args > 125)
        break;
      *argp++ = ' ';
      /* If invoked by `spawnXX' or `execXX' functions, we need to
--- 447,453 ----
      int quoted = 0;
      const char *p = argv[i];
  
!     if (argp - args > CMDLEN_LIMIT)
        break;
      *argp++ = ' ';
      /* If invoked by `spawnXX' or `execXX' functions, we need to
***************
*** 301,307 ****
      }
      while (*p)
      {
!       if (argp - args > 125)
          break;
        if (*p == '"' && (quoted || need_quote))
  	*argp++ = '\\';
--- 468,474 ----
      }
      while (*p)
      {
!       if (argp - args > CMDLEN_LIMIT)
          break;
        if (*p == '"' && (quoted || need_quote))
  	*argp++ = '\\';
***************
*** 312,323 ****
  	p++;
        *argp++ = *p++;
      }
!     if (quoted && argp - args <= 125)
        *argp++ = '"';
    }
    *argp = 0;
    
!   return direct_exec_tail(program, args, envp);
  }
  
  typedef struct {
--- 479,496 ----
  	p++;
        *argp++ = *p++;
      }
!     if (quoted && argp - args <= CMDLEN_LIMIT)
        *argp++ = '"';
    }
    *argp = 0;
+ 
+   if (argp - args > CMDLEN_LIMIT)
+     errno = E2BIG;
    
!   tbuf_beg = tbuf_ptr = __tb;
!   tbuf_len = _go32_info_block.size_of_transfer_buffer;
!   tbuf_end = tbuf_beg + tbuf_len - 1;
!   return direct_exec_tail(program, args, envp, 0, 2);
  }
  
  typedef struct {
***************
*** 330,368 ****
  static int go32_exec(const char *program, char **argv, char **envp)
  {
    char *save_argv0;
!   int is_stubbed = 0;
    int found_si = 0;
    StubInfo si;
    unsigned short header[3];
    int pf, i;
    char *go32, *sip=0;
!   char rpath[80];
!   int stub_offset, argc;
  
    int v2_0 = 0;
!   int si_la=0, rm_la, rm_seg;
!   short *rm_argv;
!   char cmdline[34];
!   char *unquoted = 0;
!   size_t maxlen = 0;
  
    pf = open(program, O_RDONLY|O_BINARY);
  
    read(pf, header, sizeof(header));
    if (header[0] == 0x010b || header[0] == 0x014c)
    {
!     is_stubbed = 1;
    }
!   else if (header[0] == 0x5a4d)
    {
      int header_offset = (long)header[2]*512L;
      if (header[1])
        header_offset += (long)header[1] - 512L;
      lseek(pf, 512, 0);
      read(pf, cmdline, 8);
      cmdline[8] = 0;
      if (strcmp(cmdline, "go32stub") == 0)
        v2_0 = 1;
      else
      {
        lseek(pf, header_offset - 4, 0);
--- 503,560 ----
  static int go32_exec(const char *program, char **argv, char **envp)
  {
    char *save_argv0;
!   int is_stubbed = 0, is_coff = 0;
    int found_si = 0;
    StubInfo si;
    unsigned short header[3];
    int pf, i;
    char *go32, *sip=0;
!   char rpath[FILENAME_MAX];
!   int stub_offset, argc=0;
  
    int v2_0 = 0;
!   int si_la=0, si_off=0, rm_off, argv_off;
!   char cmdline[CMDLEN_LIMIT+2], *cmdp = cmdline;
!   char *pcmd = cmdline, *pproxy = 0, *proxy_cmdline = 0;
!   int retval;
!   int lfn = 2;	/* means don't know yet */
  
    pf = open(program, O_RDONLY|O_BINARY);
  
    read(pf, header, sizeof(header));
    if (header[0] == 0x010b || header[0] == 0x014c)
    {
!     unsigned char firstbytes[1];
!     unsigned long coffhdr[40];
! 
!     /* Seems to be an unstubbed COFF.  See what the first opcode
!        is to determine if it's v1.x or v2 COFF (or an impostor).
! 
!        FIXME: the code here assumes that any COFF that's not a V1
!        can only be V2.  What about other compilers that use COFF?  */
!     is_coff = 1;
!     if (lseek(pf, 2, 1) < 0
! 	|| read(pf, coffhdr, sizeof(coffhdr)) != sizeof(coffhdr)
! 	|| lseek(pf, coffhdr[10 + 5], 0) < 0
! 	|| read(pf, firstbytes, 1) != 1) /* scnptr */
!       is_coff = 0;	/* "Aha! An impostor!" (The Adventure game) */
!     else if (firstbytes[0] != 0xa3) /* opcode of movl %eax, 0x12345678 (V1) */
!       v2_0 = 1;
    }
!   else if (header[0] == 0x5a4d)	/* "MZ" */
    {
      int header_offset = (long)header[2]*512L;
+     is_stubbed = 1;
      if (header[1])
        header_offset += (long)header[1] - 512L;
      lseek(pf, 512, 0);
      read(pf, cmdline, 8);
      cmdline[8] = 0;
      if (strcmp(cmdline, "go32stub") == 0)
+     {
        v2_0 = 1;
+       is_coff = 1;
+     }
      else
      {
        lseek(pf, header_offset - 4, 0);
***************
*** 370,385 ****
        header[0] = 0;
        read(pf, header, sizeof(header));
        if (header[0] == 0x010b)
! 	is_stubbed = 1;
        if (header[0] == 0x014c)
! 	is_stubbed = 1;
        lseek(pf, stub_offset, 0);
        read(pf, &si, sizeof(si));
        if (memcmp(STUB_INFO_MAGIC, si.magic, 16) == 0)
  	found_si = 1;
      }
    }
!   if (!is_stubbed && !v2_0)
    {
      close(pf);
      return direct_exec(program, argv, envp);
--- 562,584 ----
        header[0] = 0;
        read(pf, header, sizeof(header));
        if (header[0] == 0x010b)
! 	is_coff = 1;
        if (header[0] == 0x014c)
! 	is_coff = 1;
        lseek(pf, stub_offset, 0);
        read(pf, &si, sizeof(si));
        if (memcmp(STUB_INFO_MAGIC, si.magic, 16) == 0)
  	found_si = 1;
      }
    }
!   else if (header[0] == 0x2123)	/* "#!" */
!   {
!     close(pf);
!     return script_exec(program, argv, envp);
!   }
! 
!   /* Non-DJGPP programs cannot be run by !proxy.  */
!   if (!is_coff)
    {
      close(pf);
      return direct_exec(program, argv, envp);
***************
*** 387,396 ****
  
    if (found_si)
      go32 = si.go32;
    else
      go32 = GO32_STRING;
  
!   if (v2_0)
    {
      strcpy(rpath, program);
    }
--- 586,597 ----
  
    if (found_si)
      go32 = si.go32;
+   else if (v2_0 && !is_stubbed)
+     go32 = GO32_V2_STRING;
    else
      go32 = GO32_STRING;
  
!   if (v2_0 && is_stubbed)
    {
      strcpy(rpath, program);
    }
***************
*** 413,473 ****
    }
    close(pf);
  
    save_argv0 = argv[0];
    argv[0] = unconst(program, char *); /* since that's where we really found it */
  
!   tbuf = __tb;
! 
!   if (found_si)
    {
!     si_la = talloc(si.struct_length);
!     dosmemput(sip, si.struct_length, si_la);
    }
!   
!   for (argc=0; argv[argc]; argc++)
!     if (__dosexec_in_system)
        {
! 	size_t len = strlen(argv[argc]);
! 	if (len > maxlen)
! 	  maxlen = len;
        }
      
!   rm_la = talloc(2*(argc+1));
!   rm_seg = (__tb >> 4) & 0xffff;
!   rm_argv = (short *)alloca((argc+1) * sizeof(short));
!   if (__dosexec_in_system)
!     unquoted = (char *)alloca(maxlen + 1);
!   /* FIXME: shell filename wildcards will be expanded by the
!      startup code on the child side, even if we are called
!      by `spawnXX' or `execXX'.  */
!   for (i=0; i<argc; i++)
!   {
!     int sl = strlen(argv[i]) + 1;
!     int q;
! 
!     /* If called by `system', remove quoting characters, like
!        a shell (or the child's startup code) would.  */
!     if (__dosexec_in_system)
!     {
!       __unquote(unquoted, argv[i], argv[i] + sl - 1);
!       sl = strlen(unquoted) + 1;
!     }
!     q = talloc(sl);
!     dosmemput(__dosexec_in_system ? unquoted : argv[i], sl, q);
!     rm_argv[i] = (q - (rm_seg<<4)) & 0xffff;
!   }
!   rm_argv[i] = 0;
!   dosmemput(rm_argv, 2*(argc+1), rm_la);
!   
!   sprintf(cmdline, " !proxy %04x %04x %04x %04x %04x",
!     argc, rm_seg, (rm_la - (rm_seg<<4))&0xffff,
!     rm_seg, (si_la - (rm_seg<<4))&0xffff);
!   if (!found_si)
!     cmdline[22] = 0; /* remove stub information */
  
!   argv[0] = save_argv0;
  
!   return direct_exec_tail(rpath, cmdline, envp);
  }
  
  int
--- 614,740 ----
    }
    close(pf);
  
+   /* V2.0 programs invoked by `system' must be run via
+      `direct_exec', because otherwise the command-line arguments
+      won't be globbed correctly by the child.  Only v2.01 and
+      later knows how to get long command lines from `system' AND
+      glob them correctly.  But we don't want to check with which
+      version was the child compiled, so we need to create both the
+      usual DOS command line and the !proxy one (which will be put
+      into the environment).  Sigh...  */
    save_argv0 = argv[0];
    argv[0] = unconst(program, char *); /* since that's where we really found it */
+   /* Construct the DOS command tail */
+   for (argc=0; argv[argc]; argc++);
  
!   if (__dosexec_in_system)
    {
!     /* If PROGRAM is an un-stubbed COFF, it must be passed in the
!        command tail as well, since we call GO32 to run it.  */
!     for (i = (is_stubbed ? 1 : 0); i <= argc; i++)
!     {
!       const char *p = argv[i];
!       if (cmdp - cmdline > CMDLEN_LIMIT)
! 	break;
!       *cmdp++ = ' ';
!       while (*p)
!       {
! 	if (cmdp - cmdline > CMDLEN_LIMIT)
! 	  break;
! 	*cmdp++ = *p++;
!       }
!     }
!     *cmdp = '\0';
    }
! 
!   lfn = _USE_LFN;
! 
!   /* Can't call any functions that use the transfer buffer beyond
!      this point: they will overwrite the data already in __tb.  */
!   tbuf_beg = tbuf_ptr = __tb;
!   tbuf_len = _go32_info_block.size_of_transfer_buffer;
!   tbuf_end = tbuf_ptr + tbuf_len - 1;
! 
!   /* If called from `system' and we have a command line shorter
!      than the DOS limit, we don't need to use !proxy at all.  */
!   if (!__dosexec_in_system || cmdp - cmdline > CMDLEN_LIMIT)
!   {
!     if (!check_talloc(found_si ?
! 		      si.struct_length : 0
! 		      + (argc+1)*sizeof(short)))
!     {
!       argv[0] = save_argv0;
!       return -1;
!     }
!     if (found_si)
!     {
!       si_la = talloc(si.struct_length);
!       si_off = si_la - tbuf_beg;
!       dosmemput(sip, si.struct_length, si_la);
!     }
! 
!     rm_off = argv_off = talloc((argc+1) * sizeof(short)) - tbuf_beg;
! #if 0
!     /* `alloca' could be dangerous with long command lines.  We
!        will instead move the offsets one by one with `_farpokew'.  */
!     rm_argv = (short *)alloca((argc+1) * sizeof(short));
! #endif
! 
!     for (i=0; i<argc; i++)
!     {
!       char *pargv = argv[i];
!       int sl = strlen(pargv) + 1;
!       unsigned long q;
! 
!       if (check_talloc(sl))
        {
! 	q = talloc(sl);
! 	dosmemput(pargv, sl, q);
! 	_farpokew(_dos_ds, tbuf_beg + argv_off, (q - tbuf_beg) & 0xffff);
! 	argv_off += sizeof(short);
        }
+       else	/* not enough space to pass args */
+       {
+ 	argv[0] = save_argv0;
+ 	return -1;
+       }
+     }
+ 
+     _farpokew (_dos_ds, tbuf_beg + argv_off, 0);
+     argv_off += sizeof(short);
+ 
+     argv[0] = save_argv0;
+     /* Environment variables are all malloced.  */
+     proxy_cmdline = (char *)malloc (34);
+     if (!proxy_cmdline)
+       return -1;
      
!     sprintf(proxy_cmdline, "%s=%04x %04x %04x %04x %04x",
! 	     __PROXY, argc,
! 	    (unsigned)(tbuf_beg >> 4), rm_off & 0xffff,
! 	    (unsigned)(tbuf_beg >> 4), si_off & 0xffff);
!     if (!found_si)
!       proxy_cmdline[22] = 0; /* remove stubinfo information */
  
!     if (__dosexec_in_system && v2_0)
!       pproxy = proxy_cmdline;
!     else
!     {
!       /* `proxy_cmdline looks like an environment variable " !proxy=value".
!          This is used as the REAL command line specification by 2.01
! 	 and later executables when called by `system'.  But if that's
! 	 not the case, we need a blank instead of the `='.  */
!       proxy_cmdline[__PROXY_LEN] = ' ';
!       pcmd = proxy_cmdline;
!     }
!   }
!   else
!     argv[0] = save_argv0;
  
!   retval = direct_exec_tail(rpath, pcmd, envp, pproxy, lfn);
!   if (proxy_cmdline)
!     free(proxy_cmdline);
!   return retval;
  }
  
  int
***************
*** 491,500 ****
    if (strchr(program, ' ') || strchr(program, '\t'))
    {
      was_quoted = 1;
!     cmdline[0] = '"';
    }
    for (i = 0; program[i] > ' '; i++)
    {
      if (program[i] == '/')
        cmdline[i+3+was_quoted] = '\\';
      else
--- 758,768 ----
    if (strchr(program, ' ') || strchr(program, '\t'))
    {
      was_quoted = 1;
!     cmdline[3] = '"';
    }
    for (i = 0; program[i] > ' '; i++)
    {
+     /* COMMAND.COM cannot grok program names with forward slashes.  */
      if (program[i] == '/')
        cmdline[i+3+was_quoted] = '\\';
      else
***************
*** 503,510 ****
    for (; program[i]; i++)
      cmdline[i+3+was_quoted] = program[i];
    if (was_quoted)
      cmdline[i+3+was_quoted] = '"';
!   cmdline[i+4+was_quoted] = 0;
    for (i=1; argv[i]; i++)
    {
      strcat(cmdline, " ");
--- 771,781 ----
    for (; program[i]; i++)
      cmdline[i+3+was_quoted] = program[i];
    if (was_quoted)
+   {
      cmdline[i+3+was_quoted] = '"';
!     i++;
!   }
!   cmdline[i+3+was_quoted] = 0;
    for (i=1; argv[i]; i++)
    {
      strcat(cmdline, " ");
***************
*** 560,575 ****
      comspec = "c:\\command.com";
  
    /* FIXME: 126-char limit below isn't LFN-clean.  */
!   if (strlen(cmdline) > 126)
!     cmdline[126] = '\0';
!   tbuf = __tb;
!   i = direct_exec_tail(comspec, cmdline, envp);
    return i;
  }
  
  static int script_exec(const char *program, char **argv, char **envp)
  {
!   char line[130], interp[80], iargs[130];
    FILE *f;
    char **newargs;
    int i, hasargs=0;
--- 831,852 ----
      comspec = "c:\\command.com";
  
    /* FIXME: 126-char limit below isn't LFN-clean.  */
!   if (strlen(cmdline) > CMDLEN_LIMIT + 1)
!   {
!     cmdline[CMDLEN_LIMIT+1] = '\0';
!     errno = E2BIG;
!   }
! 
!   tbuf_beg = tbuf_ptr = __tb;
!   tbuf_len = _go32_info_block.size_of_transfer_buffer;
!   tbuf_end = tbuf_ptr + tbuf_len - 1;
!   i = direct_exec_tail(comspec, cmdline, envp, 0, 2);
    return i;
  }
  
  static int script_exec(const char *program, char **argv, char **envp)
  {
!   char line[130], interp[FILENAME_MAX], iargs[130];
    FILE *f;
    char **newargs;
    int i, hasargs=0;
***************
*** 580,590 ****
      errno = ENOENT;
      return -1;
    }
!   fgets(line, 130, f);
    fclose(f);
    iargs[0] = 0;
    interp[0] = 0;
    sscanf(line, "#! %s %[^\n]", interp, iargs);
    if (interp[0] == 0)
      return __dosexec_command_exec(program, argv, envp); /* it couldn't be .exe or .com if here */
    if (iargs[0])
--- 857,875 ----
      errno = ENOENT;
      return -1;
    }
!   fgets(line, sizeof(line), f);
    fclose(f);
+ 
+   /* Paranoia: is this at all a text file?  */
+   for (i=0; i < sizeof(line)-1 && line[i] != '\0'; i++)
+     if (line[i] < 7 && line[i] >= 0)
+       return go32_exec(program, argv, envp);
+ 
    iargs[0] = 0;
    interp[0] = 0;
    sscanf(line, "#! %s %[^\n]", interp, iargs);
+ 
+   /* If no interpreter, invoke the default shell in $COMSPEC.  */
    if (interp[0] == 0)
      return __dosexec_command_exec(program, argv, envp); /* it couldn't be .exe or .com if here */
    if (iargs[0])
***************
*** 595,601 ****
    for (i=0; argv[i]; i++)
      newargs[i+1+hasargs] = unconst(argv[i], char *);
    newargs[i+1+hasargs] = 0;
!   newargs[0] = unconst(argv[0], char *); /* it might work right, if not in system() */
    if (hasargs)
      newargs[1] = iargs;
  
--- 880,886 ----
    for (i=0; argv[i]; i++)
      newargs[i+1+hasargs] = unconst(argv[i], char *);
    newargs[i+1+hasargs] = 0;
!   newargs[0] = unconst(argv[0], char *);
    if (hasargs)
      newargs[1] = iargs;
  
***************
*** 603,608 ****
--- 888,897 ----
    return i;
  }
  
+ /* Note: the following list is not supposed to mention *every*
+    possible extension of an executable file.  It only mentions
+    those extensions that can be *omitted* when you invoke the
+    executable from one of the shells used on MSDOS.  */
  static struct {
    const char *extension;
    int (*interp)(const char *, char **, char **);
***************
*** 611,621 ****
    { ".exe", go32_exec },
    { ".bat", __dosexec_command_exec },
    { ".btm", __dosexec_command_exec },
    { "",     go32_exec },
!   { 0,      script_exec },
    { 0,      0 },
  };
! #define INTERP_NO_EXT 3
  
  /*-------------------------------------------------*/
  
--- 900,915 ----
    { ".exe", go32_exec },
    { ".bat", __dosexec_command_exec },
    { ".btm", __dosexec_command_exec },
+   { ".sh",  script_exec },  /* for compatibility with ms_sh */
+   { ".ksh", script_exec },
    { "",     go32_exec },
!   { 0,      script_exec },  /* every extension not mentioned above calls it */
    { 0,      0 },
  };
! 
! /* This is the index into the above array of the interpreter
!    which is called when the program filename has no extension.  */
! #define INTERP_NO_EXT (sizeof(interpreters)/sizeof(interpreters[0]) - 3)
  
  /*-------------------------------------------------*/
  
***************
*** 626,635 ****
    const char *ptr;
    int i, hasdot=0, haspath=0;
    int tried_dot = 0;
!   int e = errno;
  
!   strcpy(buf, program);
!   rp = buf + strlen(buf);
  
    for (ptr=program; *ptr; ptr++)
    {
--- 920,934 ----
    const char *ptr;
    int i, hasdot=0, haspath=0;
    int tried_dot = 0;
!   int e = errno, blen = strlen(program);
  
!   if (blen > FILENAME_MAX - 1)
!   {
!     errno = ENAMETOOLONG;
!     return 0;
!   }
!   strncpy(buf, program, blen + 1);
!   rp = buf + blen;
  
    for (ptr=program; *ptr; ptr++)
    {
***************
*** 645,651 ****
--- 944,953 ----
    if (hasdot)
    {
      if (access(buf, 0) == 0 && access(buf, D_OK))
+     {
+       errno = e;
        return buf;
+     }
    }
    else
      for (i=0; interpreters[i].extension; i++)
***************
*** 719,760 ****
    }
  }
  
- /* Return a copy of a word between BEG and (excluding) END with all
-    quoting characters removed from it.  */
- 
- char *
- __unquote (char *to, const char *beg, const char *end)
- {
-   const char *s = beg;
-   char *d = to;
-   int quote = 0;
- 
-   while (s < end)
-   {
-     switch (*s)
-     {
-       case '"':
-       case '\'':
- 	if (!quote)
- 	  quote = *s;
- 	else if (quote == *s)
- 	  quote = 0;
- 	s++;
- 	break;
-       case '\\':
- 	if (s[1] == '"' || s[1] == '\'')
- 	  s++;
- 	/* Fall-through.  */
-       default:
- 	*d++ = *s++;
- 	break;
-     }
-   }
- 
-   *d = 0;
-   return to;
- }
- 
  int __spawnve(int mode, const char *path, char *const argv[], char *const envp[])
  {
    /* This is the one that does the work! */
--- 1021,1026 ----
***************
*** 770,775 ****
--- 1036,1046 ----
      errno = EINVAL;
      return -1;
    }
+   if (strlen(path) > FILENAME_MAX - 1)
+   {
+     errno = ENAMETOOLONG;
+     return -1;
+   }
  
    u.x = argv; argvp = u.p;
    u.x = envp; envpp = u.p;
***************
*** 807,811 ****
      exit(i);
    return i;
  }
- 
- 
--- 1078,1080 ----
*** src/libc/ansi/stdlib/system.c~5	Thu Jul 25 00:18:48 1996
--- src/libc/ansi/stdlib/system.c	Fri Aug  9 12:20:00 1996
***************
*** 30,36 ****
  } cmd_sym_t;
  
  static cmd_sym_t get_sym (char *s, char **beg, char **end);
! char *    __unquote (char *to, const char *beg, const char *end);
  
  int __system_flags = __system_redirect
  		   | __system_handle_null_commands
--- 30,36 ----
  } cmd_sym_t;
  
  static cmd_sym_t get_sym (char *s, char **beg, char **end);
! static char *    __unquote (char *to, const char *beg, const char *end);
  
  int __system_flags = __system_redirect
  		   | __system_handle_null_commands
***************
*** 188,194 ****
     disable file wildcards expansion by the child.)  */
  
  static int
! plainsystem(const char *prog, const char *args)
  {
    char found_at[FILENAME_MAX];
    int e = errno;
--- 188,194 ----
     disable file wildcards expansion by the child.)  */
  
  static int
! plainsystem(const char *prog, char *args)
  {
    char found_at[FILENAME_MAX];
    int e = errno;
***************
*** 197,203 ****
    {
      char **pargv, **this_arg;
      int    pargc = 2;		/* PROG is one, terminating 0 is another */
!     const char *pcmd = args;
      char *b, *e2;
  
      if (! (sys_flags & __system_allow_long_cmds))
--- 197,203 ----
    {
      char **pargv, **this_arg;
      int    pargc = 2;		/* PROG is one, terminating 0 is another */
!     char *pcmd = args;
      char *b, *e2;
  
      if (! (sys_flags & __system_allow_long_cmds))
***************
*** 215,222 ****
      while (*pcmd)
      {
        /* Only words and the terminating 0 are legal at this point.  */
!       /* FIXME we shouldn't have to unconst this! */
!       if (get_sym (unconst(pcmd, char *), &b, &e2) == WORD)
  	pargc++;
        else if (*b)
        {
--- 215,221 ----
      while (*pcmd)
      {
        /* Only words and the terminating 0 are legal at this point.  */
!       if (get_sym (pcmd, &b, &e2) == WORD)
  	pargc++;
        else if (*b)
        {
***************
*** 234,241 ****
  
      for (pcmd = args; --pargc; pcmd = e2, this_arg++)
      {
!       /* FIXME we shouldn't have to unconst this! */
!       get_sym (unconst(pcmd, char *), &b, &e2);
        *this_arg = (char *)alloca (e2 - b + 1);
        strncpy (*this_arg, b, e2 - b);
        (*this_arg)[e2 - b] = '\0';
--- 233,239 ----
  
      for (pcmd = args; --pargc; pcmd = e2, this_arg++)
      {
!       get_sym (pcmd, &b, &e2);
        *this_arg = (char *)alloca (e2 - b + 1);
        strncpy (*this_arg, b, e2 - b);
        (*this_arg)[e2 - b] = '\0';
***************
*** 247,253 ****
    else
    {
      /* PROG is nowhere on the PATH.  If it got explicit ".exe",
!        ".bat" or ".com" extension, return an error.  */
      const char *endcmd = 0;
      int i;
  
--- 245,251 ----
    else
    {
      /* PROG is nowhere on the PATH.  If it got explicit ".exe",
!        ".bat", ".btm" or ".com" extension, return an error.  */
      const char *endcmd = 0;
      int i;
  
***************
*** 261,267 ****
      if (endcmd
  	&& (stricmp (endcmd, "exe") == 0
  	    || stricmp (endcmd, "com") == 0
! 	    || stricmp (endcmd, "bat") == 0))
      {
        errno = ENOENT;
        return -1;
--- 259,266 ----
      if (endcmd
  	&& (stricmp (endcmd, "exe") == 0
  	    || stricmp (endcmd, "com") == 0
! 	    || stricmp (endcmd, "bat") == 0
! 	    || stricmp (endcmd, "btm") == 0))
      {
        errno = ENOENT;
        return -1;
***************
*** 302,308 ****
  /* This function handles certain dummy or simple commands and
     passes the rest to plainsystem.  */
  static int
! system1 (const char *cmd, const char *cmdline)
  {
    /* There are certain commands that are no-ops only with arguments,
       or only without them, or both.  There are other commands which
--- 301,307 ----
  /* This function handles certain dummy or simple commands and
     passes the rest to plainsystem.  */
  static int
! system1 (const char *cmd, char *cmdline)
  {
    /* There are certain commands that are no-ops only with arguments,
       or only without them, or both.  There are other commands which
***************
*** 328,338 ****
  	      || CHECK_FOR (cmd, "path")
  	      || CHECK_FOR (cmd, "prompt"))  && *cmdline)))
      return 0;
- #if 0
-   /* system() can't call chdir() (ansi calling posix?  bad)
-      Also, this implementation of chdir isn't the same as the
-      command.com version, so it breaks some makefiles.  Thus,
-      comment it out until it can be fixed properly. */
    else if (CHECK_FOR (cmd, "chdir") || CHECK_FOR (cmd, "cd"))
    {
      if (*cmdline && (sys_flags & __system_ignore_chdir))
--- 327,332 ----
***************
*** 342,358 ****
        /* We can `chdir' better, because we know about forward
  	 slashes, and also change the drive.  */
        if (*cmdline)
! 	return chdir (cmdline);
        else
        {
! 	char *homedir = getenv ("HOME");
! 
! 	if (homedir)
! 	  return chdir (homedir);
        }
      }
    }
- #endif
  #if 0
    else if ((sys_flags & __system_emulate_echo)
  	   && CHECK_FOR (cmd, "echo"))
--- 336,354 ----
        /* We can `chdir' better, because we know about forward
  	 slashes, and also change the drive.  */
        if (*cmdline)
! 	return __chdir (cmdline);
        else
        {
! 	/* COMMAND.COM prints the current directory if given
! 	   no argument to CD.  */
! 	char wd[FILENAME_MAX];
! 
! 	printf ("%s\n", __getcwd(wd, FILENAME_MAX-1)
! 		? wd : "Current drive is no longer valid");
! 	return 0;
        }
      }
    }
  #if 0
    else if ((sys_flags & __system_emulate_echo)
  	   && CHECK_FOR (cmd, "echo"))
***************
*** 364,369 ****
--- 360,401 ----
    return plainsystem (cmd, cmdline);
  }
  
+ /* Return a copy of a word between BEG and (excluding) END with all
+    quoting characters removed from it.  */
+ 
+ static char *
+ __unquote (char *to, const char *beg, const char *end)
+ {
+   const char *s = beg;
+   char *d = to;
+   int quote = 0;
+ 
+   while (s < end)
+   {
+     switch (*s)
+     {
+       case '"':
+       case '\'':
+ 	if (!quote)
+ 	  quote = *s;
+ 	else if (quote == *s)
+ 	  quote = 0;
+ 	s++;
+ 	break;
+       case '\\':
+ 	if (s[1] == '"' || s[1] == '\'')
+ 	  s++;
+ 	/* Fall-through.  */
+       default:
+ 	*d++ = *s++;
+ 	break;
+     }
+   }
+ 
+   *d = 0;
+   return to;
+ }
+ 
  /* A poor-man's lexical analyzer for simplified command processing.
  
     It only knows about these:
***************
*** 538,543 ****
--- 570,587 ----
  
  	switch (token)
  	{
+ 	case WORD:
+ 	  /* First word we see is the program to run.  */
+ 	  if (needcmd)
+ 	  {
+ 	    __unquote (prog, t, u); /* unquote and copy to prog */
+ 	    strcpy (s, u);	  /* remove program name from cmdline */
+ 	    needcmd = 0;
+ 	  }
+ 	  else
+ 	    s = u;
+ 	  again = 1;
+ 	  break;
  	case REDIR_INPUT:
  	case REDIR_OUTPUT:
  	case REDIR_APPEND:
***************
*** 683,700 ****
  	    s = u;
  	  }
  	  break;
- 	case WORD:
- 	  /* First word we see is the program to run.  */
- 	  if (needcmd)
- 	  {
- 	    __unquote (prog, t, u); /* unquote and copy to prog */
- 	    strcpy (s, u);	  /* remove program name from cmdline */
- 	    needcmd = 0;
- 	  }
- 	  else
- 	    s = u;
- 	  again = 1;
- 	  break;
  	case UNMATCHED_QUOTE:
  	  result = emiterror ("Unmatched quote character.", 0);
  	  errno = EINVAL;
--- 727,732 ----
***************
*** 722,728 ****
  
  int main (int argc, char *argv[])
  {
!   __system_flags |= __system_allow_multiple_cmds;
    signal (SIGINT, SIG_IGN);
    if (argc > 1)
      {
--- 754,760 ----
  
  int main (int argc, char *argv[])
  {
!   __system_flags |= (__system_allow_multiple_cmds | __system_emulate_chdir);
    signal (SIGINT, SIG_IGN);
    if (argc > 1)
      {
*** src/libc/dos/process/dosexec.t~0	Mon Jul 10 05:40:12 1995
--- src/libc/dos/process/dosexec.txh	Sat Aug 17 15:54:50 1996
***************
*** 18,25 ****
  @subheading Description
  
  These functions run other programs.  The @var{path} points to the
! program to run.  The extension is optional - if not given, the usual
! extensions @code{.com}, @code{.exe}, and @code{.bat} are checked.
  
  The programs are invoked with the arguments given.  The zeroth argument
  is normally not used, since MS-DOS cannot pass it separately.  There are
--- 18,39 ----
  @subheading Description
  
  These functions run other programs.  The @var{path} points to the
! program to run.  The extension is optional---if not given, and
! @var{path} is not found neither in the current directly nor along the
! @samp{PATH}, the extensions @file{.com}, @file{.exe}, @file{.bat},
! @file{.btm}, @file{.sh}, and @file{.ksh} are checked.  @file{.com}
! programs are invoked via the usual DOS calls; DJGPP @file{.exe} programs
! are invoked in a way that allows long command lines to be passed; other
! @file{.exe} programs are invoked via DOS; @file{.bat} and @file{.btm}
! programs are invoked via the command processor given by the
! @samp{COMSPEC} environment variable; @file{.sh}, @file{.ksh} programs
! and programs with any other extensions that have @code{#!} as their
! first two caharacters are assumed to be Unix-style scripts and are
! invoked by calling a program whose pathname immediately follows the
! first two characters.
! 
! Note that built-in commands of the shells can @emph{not} be invoked via
! these functions; use @code{system} instead.
  
  The programs are invoked with the arguments given.  The zeroth argument
  is normally not used, since MS-DOS cannot pass it separately.  There are
***************
*** 37,45 ****
  the current environment) for the executable, else it will only check the
  explicit path given. 
  
! Note that these function understand about other djgpp programs, and will
! call them directly, so that you can pass command lines longer than 128
! characters to them without any special code. 
  
  @xref{exec*}.
  
--- 51,62 ----
  the current environment) for the executable, else it will only check the
  explicit path given. 
  
! Note that these function understand about other DJGPP programs, and will
! call them directly, so that you can pass command lines longer than 126
! characters to them without any special code.  DJGPP programs called by
! these functions will @emph{not} glob the arguments passed to them; other
! programs also won't glob the arguments if they suppress expansion when
! given quoted filenames.
  
  @xref{exec*}.
  
***************
*** 55,61 ****
  not return. 
  
  If there is an error, these functions return -1 and set @code{errno} to
! indicate the error. 
  
  @subheading Example
  
--- 72,80 ----
  not return. 
  
  If there is an error, these functions return -1 and set @code{errno} to
! indicate the error.  If the child program was interrupted by
! @key{Ctrl-C} or a Critical Device error, @code{errno} is set to
! @code{EINTR} even if the child's exit code is 0.
  
  @subheading Example
  
***************
*** 74,79 ****
    0
  @};
  
! spawnvpe("gcc", args, environ);
  @end example
  
--- 93,98 ----
    0
  @};
  
! spawnvpe(P_WAIT, "gcc", args, environ);
  @end example
  
*** src/libc/ansi/stdlib/system.t~0	Mon Jul 10 05:39:58 1995
--- src/libc/ansi/stdlib/system.txh	Sat Aug 17 16:03:24 1996
***************
*** 9,28 ****
  
  @subheading Description
  
! This function runs the specified command.  When calling programs
! compiled by djgpp this function will not use command.com and so
! will not be subject to its 126 character limit on command lines.
  
  Command lines and pipes( i.e., the use of "<", ">", ">>", and "|")
! will be simulated internally in this function.
  
! Command.com will only be invoked to run commands internal to it,
! or to run batch files.  In these cases, the returned error code
! will always be zero, since command.com always exits with code 0.
  
  @subheading Return Value
  
! The return value of the child process.
  
  @subheading Example
  
--- 9,136 ----
  
  @subheading Description
  
! This function runs the command or program specified by @var{cmd}.  When
! calling programs compiled by DJGPP this function will not use
! @file{command.com} and so will not be subject to its 126 character limit
! on command lines.
  
  Command lines and pipes( i.e., the use of "<", ">", ">>", and "|")
! will be simulated internally in this function; this means that you can
! have both long command lines and redirection/pipes when running DJGPP
! programs with this function.
  
! By default, @file{command.com} will only be invoked to run commands
! internal to it, or to run batch files (but this can be changed, see
! below).  In these cases, the returned error code will always be zero,
! since @file{command.com} always exits with code 0.
! 
! Certain commands internal to @file{command.com} that don't make sense or
! cause no effect in the context of @code{system} are ignored by this
! function.  These are @code{REM}, @code{EXIT}, @code{GOTO}, @code{SHIFT};
! @code{SET}, @code{PATH} and @code{PROMPT} are ignored only if called
! with an argument.  You can disable this feature if you need, see below.
! 
! Some commands are emulated internally by @code{system}, because the
! emulation is better than the original.  Currently, the only emulated
! command is @samp{CD} or @samp{CHDIR}: the emulation knows about forward
! slashes and also switches the current drive.  This emulation can also be
! switched off, as explained below.
! 
! When @code{system} is presented with a batch file, a shell script, or an
! internal shell command, it checks the environment variables @samp{SHELL}
! and @samp{COMSPEC} (in that order) and invokes the program that they
! point to.  If the shell thus found is one of the DOS shells
! (@file{command.com}, @file{4DOS} or @file{NDOS}), they are called with
! the @samp{/c} switch prepended to the command line.  Otherwise,
! @code{system} assumes that the shell is a Unix-style shell and invokes
! it with a @samp{-c} switch, passing the entire command line via a
! response file (this should work with any shell compiled with DJGPP, and
! with the @code{ms_sh} shell).
! 
! You can customize the behavior of @code{system} using a bit-mapped
! variable @var{__system_flags}, defined on @file{<libc/system.h>}.  The
! following bits are currently defined:
! 
! @table @code
! @item __system_redirect
! When set (the default), specifies that @code{system} can use its
! internal redirection and pipe code.  If reset, any command line that
! includes an unquoted redirection symbol will be passed to the shell.
! 
! @item __system_call_cmdproc
! When set, @code{system} will always call the shell to execute the
! command line.  If reset (the default), the shell will only be called
! when needed, as described above.
! 
! You should @emph{always} set this bit if you use a real, Unix-style
! shell (also, set @code{__system_use_shell}, described below, and the
! @samp{SHELL} environment variable).
! 
! @item __system_use_shell
! When set (the default), the @samp{SHELL} environment variable will take
! precedence upon @samp{COMSPEC}; this allows you to specify a special
! shell for @code{system} that doesn't affect the rest of DOS.  If reset,
! only @samp{COMSPEC} is used to find the name of the command processor.
! 
! @item __system_allow_multiple_cmds
! When set, you can put multiple commands together separated by the
! @samp{;} character.  If reset (the default), the command line passed to
! @code{system} is executed as a single command and @samp{;} has no
! special meaning.
! 
! @item __system_allow_long_cmds
! When set (the default), @code{system} will handle command lines longer
! than the DOS 126-character limit; this might crash your program in some
! cases, as the low-level functions that invoke the child program will
! only pass them the first 126 characters.  When reset, @code{system} will
! detect early that the command line is longer than 126 characters and
! refuse to run it.
! 
! @item __system_handle_null_commands
! When set (the default), commands internal to @file{COMMAND.COM} and
! compatible shells which have no effect in the context of @code{system},
! are ignored (the list of this commands was given above).  If reset,
! these commands are processed as all others, which means
! @file{COMMAND.COM} will be called to execute them.
! 
! Note that this bit shouldn't be used with a Unix-style shell, because it
! does the wrong thing then.  With Unix-style shells, you are supposed to
! set the @code{__system_call_cmdproc} bit which will always call the
! shell.
! 
! @item __system_ignore_chdir
! If set, the @samp{CD} and @samp{CHDIR} commands are ignored.  When reset
! (the default), the processing of these commands depends on the
! @code{__system_emulate_chdir} bit, see below.
! 
! This bit is for compatibility with Unix, where a single @samp{cd dir}
! command has no effect, because the current working directory there is
! not a global notion (as on MSDOS).  Don't set this bit if you use
! multiple commands (see @code{__system_allow_multiple_cmds} above).
! 
! @item __system_emulate_chdir
! When set, the @samp{CD} and @samp{CHDIR} commands are emulated
! internally: they change the drive when the argument specifies a drive
! letter, and they support both forward slashes and backslashes in
! pathnames.  When @samp{CD} is called without an argument, it prints the
! current working directory with forward slashes and down-cases DOS 8+3
! names.  If this bit is reset (the default), @samp{CD} and @samp{CHDIR}
! are passed to the shell.
! @end table
! 
! The behavior of @code{system} can be customized at run time by defining
! the variable @samp{DJSYSFLAGS} in the environment.  The value of that
! variable should be the numerical value of @code{__system_flags} that
! you'd like to set; it will override the value of @code{__system_flags}
! specified when the program was compiled.
  
  @subheading Return Value
  
! The exit status of the child process.  If the child couldn't be run,
! @code{system} will return -1 and set @code{errno} to an appropriate
! value.  Note that if @file{COMMAND.COM} was used to run the child, it
! will always return a 0 status, even if the command didn't run
! successfully.
  
  @subheading Example
  

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019