/* sys/spawnve.c (emx+gcc) -- Copyright (c) 1992-1993 by Eberhard Mattes */

#include <sys/emx.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <errno.h>
#define INCL_DOSPROCESS
#include <os2emx.h>
#include "syscalls.h"

#define ADD(n) do { \
  while (arg_size + n > arg_alloc) \
    { \
      arg_alloc += 512; \
      arg_buf = realloc (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;
  RESULTCODES res;
  char obj[40], *arg_ptr, *arg_buf;
  char *pgm_name, *base;
  const char *src;
  size_t arg_size, arg_alloc, len;
  int i, quote, bs, method;

  arg_buf = NULL; arg_alloc = 0; arg_size = 0; arg_ptr = NULL;
  switch (np->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 = (*src == 0 || strpbrk (src, " \t") != NULL
               || (method == 1 && strchr (src, '"') != NULL));
      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)
    free (arg_buf);
  if (rc != 0)
    {
      _sys_set_errno (rc);
      return (-1);
    }
  switch (np->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);
    }
}
