2001-01-11 00:59:25 +01:00
|
|
|
/*
|
|
|
|
* msvcrt.dll spawn/exec functions
|
|
|
|
*
|
|
|
|
* Copyright 1996,1998 Marcus Meissner
|
|
|
|
* Copyright 1996 Jukka Iivonen
|
|
|
|
* Copyright 1997,2000 Uwe Bonnes
|
|
|
|
* Copyright 2000 Jon Griffiths
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
#include "msvcrt.h"
|
|
|
|
#include "ms_errno.h"
|
|
|
|
|
2001-04-11 01:25:25 +02:00
|
|
|
#include "msvcrt/process.h"
|
|
|
|
#include "msvcrt/stdlib.h"
|
2001-09-11 01:07:07 +02:00
|
|
|
#include "msvcrt/string.h"
|
2001-01-11 00:59:25 +01:00
|
|
|
|
2002-01-22 01:57:16 +01:00
|
|
|
#include "wine/debug.h"
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
|
2001-01-11 00:59:25 +01:00
|
|
|
|
2001-04-10 23:16:07 +02:00
|
|
|
/* FIXME: Check file extensions for app to run */
|
2001-01-11 00:59:25 +01:00
|
|
|
static const unsigned int EXE = 'e' << 16 | 'x' << 8 | 'e';
|
|
|
|
static const unsigned int BAT = 'b' << 16 | 'a' << 8 | 't';
|
|
|
|
static const unsigned int CMD = 'c' << 16 | 'm' << 8 | 'd';
|
|
|
|
static const unsigned int COM = 'c' << 16 | 'o' << 8 | 'm';
|
|
|
|
|
|
|
|
/* INTERNAL: Spawn a child process */
|
2001-09-11 01:07:07 +02:00
|
|
|
static int msvcrt_spawn(int flags, const char* exe, char* cmdline, char* env)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
STARTUPINFOA si;
|
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
|
|
|
|
if (sizeof(HANDLE) != sizeof(int))
|
|
|
|
WARN("This call is unsuitable for your architecture\n");
|
|
|
|
|
|
|
|
if ((unsigned)flags > _P_DETACH)
|
|
|
|
{
|
|
|
|
SET_THREAD_VAR(errno,MSVCRT_EINVAL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
FIXME(":must dup/kill streams for child process\n");
|
|
|
|
|
|
|
|
memset(&si, 0, sizeof(si));
|
|
|
|
si.cb = sizeof(si);
|
|
|
|
|
2001-09-11 01:07:07 +02:00
|
|
|
if (!CreateProcessA(exe, cmdline, NULL, NULL, TRUE,
|
2001-01-11 00:59:25 +01:00
|
|
|
flags == _P_DETACH ? DETACHED_PROCESS : 0,
|
|
|
|
env, NULL, &si, &pi))
|
|
|
|
{
|
|
|
|
MSVCRT__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 (int)pi.dwProcessId;
|
|
|
|
case _P_DETACH:
|
|
|
|
CloseHandle(pi.hProcess);
|
|
|
|
pi.hProcess = 0;
|
|
|
|
/* fall through */
|
|
|
|
case _P_NOWAIT:
|
|
|
|
case _P_NOWAITO:
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
return (int)pi.hProcess;
|
|
|
|
case _P_OVERLAY:
|
|
|
|
MSVCRT__exit(0);
|
|
|
|
}
|
|
|
|
return -1; /* can't reach here */
|
|
|
|
}
|
|
|
|
|
2001-09-11 01:07:07 +02:00
|
|
|
/* INTERNAL: Convert argv list to a single 'delim'-separated string, with an
|
|
|
|
* extra '\0' to terminate it
|
|
|
|
*/
|
2001-04-11 01:25:25 +02:00
|
|
|
static char* msvcrt_argvtos(const char* const* arg, char delim)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
2001-09-11 01:07:07 +02:00
|
|
|
const char* const* a;
|
|
|
|
long size;
|
|
|
|
char* p;
|
|
|
|
char* ret;
|
2001-01-11 00:59:25 +01:00
|
|
|
|
|
|
|
if (!arg && !delim)
|
2001-09-11 01:07:07 +02:00
|
|
|
{
|
|
|
|
/* Return NULL for an empty environment list */
|
|
|
|
return NULL;
|
|
|
|
}
|
2001-01-11 00:59:25 +01:00
|
|
|
|
|
|
|
/* get length */
|
2001-09-11 01:07:07 +02:00
|
|
|
a = arg;
|
|
|
|
size = 0;
|
|
|
|
while (*a)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
2001-09-11 01:07:07 +02:00
|
|
|
size += strlen(*a) + 1;
|
|
|
|
a++;
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
|
2001-09-11 01:07:07 +02:00
|
|
|
ret = (char*)MSVCRT_malloc(size + 1);
|
|
|
|
if (!ret)
|
2001-01-11 00:59:25 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* fill string */
|
2001-09-11 01:07:07 +02:00
|
|
|
a = arg;
|
|
|
|
p = ret;
|
|
|
|
while (*a)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
2001-09-11 01:07:07 +02:00
|
|
|
int len = strlen(*a);
|
|
|
|
memcpy(ret+size,*a,len);
|
|
|
|
p += len;
|
|
|
|
*p++ = delim;
|
|
|
|
a++;
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
2001-09-11 01:07:07 +02:00
|
|
|
*p='\0';
|
2001-01-11 00:59:25 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2001-09-11 01:07:07 +02:00
|
|
|
/* INTERNAL: Convert va_list to a single 'delim'-separated string, with an
|
|
|
|
* extra '\0' to terminate it
|
|
|
|
*/
|
|
|
|
static char* msvcrt_valisttos(const char* arg0, va_list alist, char delim)
|
2001-05-18 22:58:08 +02:00
|
|
|
{
|
2001-09-11 01:07:07 +02:00
|
|
|
va_list alist2 = alist;
|
2001-05-18 22:58:08 +02:00
|
|
|
long size;
|
2001-09-11 01:07:07 +02:00
|
|
|
const char *arg;
|
|
|
|
char* p;
|
2001-05-18 22:58:08 +02:00
|
|
|
char *ret;
|
|
|
|
|
|
|
|
if (!arg0 && !delim)
|
|
|
|
{
|
2001-09-11 01:07:07 +02:00
|
|
|
/* Return NULL for an empty environment list */
|
|
|
|
return NULL;
|
2001-05-18 22:58:08 +02:00
|
|
|
}
|
|
|
|
|
2001-09-11 01:07:07 +02:00
|
|
|
/* get length */
|
|
|
|
arg = arg0;
|
|
|
|
size = 0;
|
|
|
|
do {
|
|
|
|
size += strlen(arg) + 1;
|
|
|
|
arg = va_arg(alist, char*);
|
|
|
|
} while (arg != NULL);
|
|
|
|
|
|
|
|
ret = (char*)MSVCRT_malloc(size + 1);
|
|
|
|
if (!ret)
|
2001-05-18 22:58:08 +02:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* fill string */
|
2001-09-11 01:07:07 +02:00
|
|
|
arg = arg0;
|
|
|
|
p = ret;
|
|
|
|
do {
|
|
|
|
int len = strlen(arg);
|
|
|
|
memcpy(p,arg,len);
|
|
|
|
p += len;
|
|
|
|
*p++ = delim;
|
|
|
|
arg = va_arg(alist2, char*);
|
|
|
|
} while (arg != NULL);
|
|
|
|
*p = '\0';
|
2001-05-18 22:58:08 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2001-01-11 00:59:25 +01:00
|
|
|
/*********************************************************************
|
|
|
|
* _cwait (MSVCRT.@)
|
|
|
|
*/
|
2001-04-10 23:16:07 +02:00
|
|
|
int _cwait(int *status, int pid, int action)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
HANDLE hPid = (HANDLE)pid;
|
|
|
|
int doserrno;
|
|
|
|
|
|
|
|
action = action; /* Remove warning */
|
|
|
|
|
|
|
|
if (!WaitForSingleObject(hPid, -1)) /* wait forever */
|
|
|
|
{
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
DWORD stat;
|
|
|
|
GetExitCodeProcess(hPid, &stat);
|
|
|
|
*status = (int)stat;
|
|
|
|
}
|
|
|
|
return (int)pid;
|
|
|
|
}
|
|
|
|
doserrno = GetLastError();
|
|
|
|
|
|
|
|
if (doserrno == ERROR_INVALID_HANDLE)
|
|
|
|
{
|
|
|
|
SET_THREAD_VAR(errno, MSVCRT_ECHILD);
|
|
|
|
SET_THREAD_VAR(doserrno,doserrno);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
MSVCRT__set_errno(doserrno);
|
|
|
|
|
|
|
|
return status ? *status = -1 : -1;
|
|
|
|
}
|
|
|
|
|
2001-05-22 21:18:51 +02:00
|
|
|
/*********************************************************************
|
|
|
|
* _execl (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-05-22 21:18:51 +02:00
|
|
|
*/
|
|
|
|
int _execl(const char* name, const char* arg0, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char * args;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
va_start(ap, arg0);
|
|
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
ret = msvcrt_spawn(_P_OVERLAY, name, args, NULL);
|
|
|
|
MSVCRT_free(args);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _execlp (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-05-22 21:18:51 +02:00
|
|
|
*/
|
|
|
|
int _execlp(const char* name, const char* arg0, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char * args;
|
|
|
|
int ret;
|
|
|
|
char fullname[MAX_PATH];
|
|
|
|
|
|
|
|
_searchenv(name, "PATH", fullname);
|
|
|
|
|
|
|
|
va_start(ap, arg0);
|
|
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
ret = msvcrt_spawn(_P_OVERLAY, fullname[0] ? fullname : name, args, NULL);
|
|
|
|
MSVCRT_free(args);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _execv (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-05-22 21:18:51 +02:00
|
|
|
*/
|
|
|
|
int _execv(const char* name, char* const* argv)
|
|
|
|
{
|
|
|
|
return _spawnve(_P_OVERLAY, name, (const char* const*) argv, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _execve (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-05-22 21:18:51 +02:00
|
|
|
*/
|
|
|
|
int _execve(const char* name, char* const* argv, const char* const* envv)
|
|
|
|
{
|
|
|
|
return _spawnve(_P_OVERLAY, name, (const char* const*) argv, envv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _execvpe (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-05-22 21:18:51 +02:00
|
|
|
*/
|
|
|
|
int _execvpe(const char* name, char* const* argv, const char* const* envv)
|
|
|
|
{
|
|
|
|
char fullname[MAX_PATH];
|
|
|
|
|
|
|
|
_searchenv(name, "PATH", fullname);
|
|
|
|
return _spawnve(_P_OVERLAY, fullname[0] ? fullname : name,
|
|
|
|
(const char* const*) argv, envv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _execvp (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-05-22 21:18:51 +02:00
|
|
|
*/
|
|
|
|
int _execvp(const char* name, char* const* argv)
|
|
|
|
{
|
|
|
|
return _execvpe(name, argv, NULL);
|
|
|
|
}
|
|
|
|
|
2001-05-18 22:58:08 +02:00
|
|
|
/*********************************************************************
|
|
|
|
* _spawnl (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-05-18 22:58:08 +02:00
|
|
|
*/
|
|
|
|
int _spawnl(int flags, const char* name, const char* arg0, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char * args;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
va_start(ap, arg0);
|
|
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
ret = msvcrt_spawn(flags, name, args, NULL);
|
|
|
|
MSVCRT_free(args);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
2001-05-22 21:18:51 +02:00
|
|
|
* _spawnlp (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-05-18 22:58:08 +02:00
|
|
|
*/
|
|
|
|
int _spawnlp(int flags, const char* name, const char* arg0, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char * args;
|
|
|
|
int ret;
|
|
|
|
char fullname[MAX_PATH];
|
|
|
|
|
|
|
|
_searchenv(name, "PATH", fullname);
|
|
|
|
|
|
|
|
va_start(ap, arg0);
|
|
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
|
|
va_end(ap);
|
|
|
|
|
2001-05-22 21:18:51 +02:00
|
|
|
ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, NULL);
|
2001-05-18 22:58:08 +02:00
|
|
|
MSVCRT_free(args);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2001-01-11 00:59:25 +01:00
|
|
|
/*********************************************************************
|
|
|
|
* _spawnve (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-01-11 00:59:25 +01:00
|
|
|
*/
|
2001-04-11 01:25:25 +02:00
|
|
|
int _spawnve(int flags, const char* name, const char* const* argv,
|
|
|
|
const char* const* envv)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
2001-04-10 23:16:07 +02:00
|
|
|
char * args = msvcrt_argvtos(argv,' ');
|
|
|
|
char * envs = msvcrt_argvtos(envv,0);
|
2001-01-22 03:21:54 +01:00
|
|
|
const char *fullname = name;
|
2001-01-11 00:59:25 +01:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2001-04-10 23:16:07 +02:00
|
|
|
ret = msvcrt_spawn(flags, fullname, args, envs);
|
2001-01-11 00:59:25 +01:00
|
|
|
MSVCRT_free(args);
|
|
|
|
}
|
|
|
|
if (envs)
|
|
|
|
MSVCRT_free(envs);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _spawnv (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-01-11 00:59:25 +01:00
|
|
|
*/
|
2001-04-11 01:25:25 +02:00
|
|
|
int _spawnv(int flags, const char* name, const char* const* argv)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
2001-04-10 23:16:07 +02:00
|
|
|
return _spawnve(flags, name, argv, NULL);
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _spawnvpe (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-01-11 00:59:25 +01:00
|
|
|
*/
|
2001-04-11 01:25:25 +02:00
|
|
|
int _spawnvpe(int flags, const char* name, const char* const* argv,
|
|
|
|
const char* const* envv)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
char fullname[MAX_PATH];
|
2001-04-10 23:16:07 +02:00
|
|
|
_searchenv(name, "PATH", fullname);
|
|
|
|
return _spawnve(flags, fullname[0] ? fullname : name, argv, envv);
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _spawnvp (MSVCRT.@)
|
2001-09-11 01:07:07 +02:00
|
|
|
*
|
|
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
|
|
* or double-quotes.
|
2001-01-11 00:59:25 +01:00
|
|
|
*/
|
2001-04-11 01:25:25 +02:00
|
|
|
int _spawnvp(int flags, const char* name, const char* const* argv)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
2001-04-10 23:16:07 +02:00
|
|
|
return _spawnvpe(flags, name, argv, NULL);
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* system (MSVCRT.@)
|
|
|
|
*/
|
2001-04-10 23:16:07 +02:00
|
|
|
int MSVCRT_system(const char* cmd)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
2001-09-11 01:07:07 +02:00
|
|
|
char* cmdcopy;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
/* Make a writable copy for CreateProcess */
|
|
|
|
cmdcopy=_strdup(cmd);
|
|
|
|
/* FIXME: should probably launch cmd interpreter in COMSPEC */
|
|
|
|
res=msvcrt_spawn(_P_WAIT, NULL, cmdcopy, NULL);
|
|
|
|
MSVCRT_free(cmdcopy);
|
|
|
|
return res;
|
2001-01-11 00:59:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _loaddll (MSVCRT.@)
|
|
|
|
*/
|
2001-04-10 23:16:07 +02:00
|
|
|
int _loaddll(const char* dllname)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
return LoadLibraryA(dllname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
|
|
* _unloaddll (MSVCRT.@)
|
|
|
|
*/
|
2001-04-10 23:16:07 +02:00
|
|
|
int _unloaddll(int dll)
|
2001-01-11 00:59:25 +01:00
|
|
|
{
|
|
|
|
if (FreeLibrary((HANDLE)dll))
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int err = GetLastError();
|
|
|
|
MSVCRT__set_errno(err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|