/*
 * CRTDLL spawn functions
 * 
 * Copyright 1996,1998 Marcus Meissner
 * Copyright 1996 Jukka Iivonen
 * Copyright 1997,2000 Uwe Bonnes
 * Copyright 2000 Jon Griffiths
 *
 * These functions differ in whether they pass arguments as an array
 * (v in the name) or as varags (l in the name), whether they
 * seach the path (p in the name) and/or whether they take an
 * environment (e in the name) or pass the parents environment.
 *                  Args as   Search   Take
 * Name             varargs?  path?    environment?
 * spawnl              N         N        N
 * spawnle             N         N        Y
 * spawnlp             N         Y        N
 * spawnlpe            N         Y        Y
 * spawnv              Y         N        N
 * spawnve             Y         N        Y
 * spawnvp             Y         Y        N
 * spawnvpe            Y         Y        Y
 *
 * Implementation Notes:
 * MT Safe - But only because of missing functionality.
 * 
 * After translating input arguments into the required format for
 * CreateProcess(), the internal function __CRTDLL__spawn() is
 * called to perform the actual spawning.
 *
 * FIXME:
 * -File handles need some special handling. Sometimes children get
 *  open file handles, sometimes not. The docs are confusing.
 * -No check for maximum path/argument/environment size is done.
 * -Wine has a "process.h" which is not the same as any crt version.
 * Unresolved issues Uwe Bonnes 970904:
 * -system-call calls another wine process, but without debugging arguments
 *  and uses the first wine executable in the path
 */

#include "crtdll.h"
#include <errno.h>
#include "process.h"
#include <stdlib.h>
#include <string.h>


DEFAULT_DEBUG_CHANNEL(crtdll);

/* Process creation flags */
#define _P_WAIT    0
#define _P_NOWAIT  1
#define _P_OVERLAY 2
#define _P_NOWAITO 3
#define _P_DETACH  4


extern void __CRTDLL__set_errno(ULONG err);
extern LPVOID CDECL CRTDLL_calloc(DWORD size, DWORD count);
extern VOID CDECL CRTDLL_free(void *ptr);
extern VOID CDECL CRTDLL__exit(LONG ret);
extern INT CRTDLL_doserrno;


/* INTERNAL: Spawn a child process */
static int __CRTDLL__spawn(INT flags, LPSTR exe, LPSTR args, LPSTR env);
static int __CRTDLL__spawn(INT flags, LPSTR exe, LPSTR args, LPSTR env)
{
  STARTUPINFOA si;
  PROCESS_INFORMATION pi;

  if ((unsigned)flags > _P_DETACH)
  {
    CRTDLL_errno = EINVAL;
    return -1;
  }

  FIXME(":must dup/kill streams for child process\n");

  memset(&si, 0, sizeof(si));
  si.cb = sizeof(si);

  if (!CreateProcessA(exe, args, NULL, NULL, TRUE,
		      flags == _P_DETACH ? DETACHED_PROCESS : 0,
		      env, NULL, &si, &pi))
  {
    __CRTDLL__set_errno(GetLastError());
    return -1;
  }

  switch(flags)
  {
  case _P_WAIT:
    WaitForSingleObject(pi.hProcess,-1); /* wait forvever */
    GetExitCodeProcess(pi.hProcess,&pi.dwProcessId);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return pi.dwProcessId;
  case _P_DETACH:
    CloseHandle(pi.hProcess);
    pi.hProcess = 0;
    /* fall through */
  case _P_NOWAIT:
  case _P_NOWAITO:
    CloseHandle(pi.hThread);
    return pi.hProcess;
  case  _P_OVERLAY:
    CRTDLL__exit(0);
  }
  return -1; /* cant reach here */
}


/* INTERNAL: Convert argv list to a single 'delim'-seperated string */
static LPSTR __CRTDLL__argvtos(LPSTR *arg, CHAR delim);
static LPSTR __CRTDLL__argvtos(LPSTR *arg, CHAR delim)
{
  LPSTR *search = arg;
  LONG size = 0;
  LPSTR ret;

  if (!arg && !delim)
    return NULL;

  /* get length */
  while(*search)
  {
    size += strlen(*search) + 1;
    search++;
  }

  if (!(ret = (LPSTR)CRTDLL_calloc(size + 1, 1)))
    return NULL;

  /* fill string */
  search = arg;
  size = 0;
  while(*search)
  {
    int strsize = strlen(*search);
    memcpy(ret+size,*search,strsize);
    ret[size+strsize] = delim;
    size += strsize + 1;
    search++;
  }
  return ret;
}


/*********************************************************************
 *                  _spawnve    (CRTDLL.274)
 *
 * Spawn a process.
 *
 */
HANDLE CDECL CRTDLL__spawnve(INT flags, LPSTR name, LPSTR *argv, LPSTR *envv)
{
  // return (_spawnve(i, s1, s2, s3));
  
  LPSTR args = __CRTDLL__argvtos(argv,' ');
  LPSTR envs = __CRTDLL__argvtos(envv,0);
  LPSTR fullname = name;

  FIXME(":not translating name %s to locate program\n",fullname);
  TRACE(":call (%s), params (%s), env (%s)\n",name,args,envs?"Custom":"Null");

  if (args)
  {
    HANDLE ret = __CRTDLL__spawn(flags, fullname, args, envs);
    CRTDLL_free(args);
    CRTDLL_free(envs);
    return ret;
  }
  if (envs)
    CRTDLL_free(envs);

  WARN(":No argv[0] passed - process will not be executed");
  return -1;
}


/*********************************************************************
 *                  system       (CRTDLL.485)
 */
INT CDECL CRTDLL_system(LPSTR x)
{
  // return system(string);
  
    /* FIXME: should probably launch cmd interpreter in COMSPEC */
    return __CRTDLL__spawn(_P_WAIT, NULL, x, NULL);
}
