/* sys/spawnve.c (emx+gcc) -- Copyright (c) 1992-1996 by Eberhard Mattes */

#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <errno.h>
#define INCL_DOSPROCESS
#include <os2emx.h>
#include <emx/syscalls.h>
#include "syscalls.h"

/* Note: We are allowed to call _trealloc() as this module is not used
   in an .a library. */

#define ADD(n) do { \
  while (arg_size + n > arg_alloc) \
    { \
      arg_alloc += 512; \
      arg_buf = _trealloc (arg_buf, arg_alloc); \
      if (arg_buf == NULL) \
        { \
          errno = ENOMEM; \
          return -1; \
        } \
      arg_ptr = arg_buf + arg_size; \
    } \
  arg_size += n; } while (0)


int __spawnve (struct _new_proc *np)
{
  ULONG rc;
  ULONG exec_flag, mode;
  RESULTCODES res;
  char obj[40], *arg_ptr, *arg_buf;
  char *pgm_name, *base;
  const char *src, *s;
  size_t arg_size, arg_alloc, len;
  int i, quote, bs, method;

  arg_buf = NULL; arg_alloc = 0; arg_size = 0; arg_ptr = NULL;

  mode = np->mode;
  if (np->mode & 0x8000)
    mode |= np->mode2 << 16;

  switch (mode & 0xff)
    {
    case P_WAIT:
      exec_flag = EXEC_SYNC;
      break;
    case P_NOWAIT:
      exec_flag = EXEC_ASYNCRESULT;
      break;
    case P_OVERLAY:
      exec_flag = EXEC_ASYNC;
      break;
    default:
      errno = EINVAL;
      return -1;
    }
  pgm_name = alloca (strlen ((const char *)np->fname_off) + 5);
  strcpy (pgm_name, (const char *)np->fname_off);
  _defext (pgm_name, "exe");
  base = _getname (pgm_name);
  method = 0;
  if (stricmp (base, "cmd.exe") == 0 || stricmp (base, "4os2.exe") == 0)
    method = 1;
  src = (const char *)np->arg_off;
  if (np->arg_count > 0)
    {
      ++src;                    /* skip flags byte */
      len = strlen (src) + 1;
      ADD (len);
      memcpy (arg_ptr, src, len);
      arg_ptr += len; src += len;
    }
  for (i = 1; i < np->arg_count; ++i)
    {
      if (i > 1)
        {
          ADD (1);
          *arg_ptr++ = ' ';
        }
      ++src;                    /* skip flags byte */
      quote = FALSE;
      if (*src == 0)
        quote = TRUE;
      else if (mode & P_QUOTE)
        {
          if (src[0] == '@' && src[1] != 0)
            quote = TRUE;
          else
            for (s = src; *s != 0; ++s)
              if (*s == '?' || *s == '*')
                {
                  quote = TRUE;
                  break;
                }
        }
      if (!quote)
        for (s = src; *s != 0; ++s)
          if (*s == ' ' || *s == '\t' || (*s == '"' && method == 1))
            {
              quote = TRUE;
              break;
            }
      if (quote)
        {
          ADD (1);
          *arg_ptr++ = '"';
        }
      bs = 0;
      while (*src != 0)
        {
          if (*src == '"' && method == 0)
            {
              ++bs;
              ADD (bs);
              memset (arg_ptr, '\\', bs); arg_ptr += bs;
              bs = 0;
            }
          else if (*src == '\\' && method == 0)
            ++bs;
          else
            bs = 0;
          ADD (1);
          *arg_ptr++ = *src;
          ++src;
        }
      if (quote)
        {
          ADD (1+bs);
          memset (arg_ptr, '\\', bs); arg_ptr += bs;
          *arg_ptr++ = '"';
        }
      ++src;
    }
  ADD (1);
  *arg_ptr++ = 0;
  rc = DosExecPgm (obj, sizeof (obj), exec_flag, arg_buf,
                   (const char *)np->env_off, &res, pgm_name);
  if (arg_buf != NULL)
    _tfree (arg_buf);
  if (rc != 0)
    {
      _sys_set_errno (rc);
      return -1;
    }
  switch (mode & 0xff)
    {
    case P_WAIT:
      return res.codeResult;
    case P_NOWAIT:
      return res.codeTerminate;
    case P_OVERLAY:
      while (1)
        DosExit (EXIT_PROCESS, 0);
    default:
      errno = EINVAL;
      return -1;
    }
}
