reactos/sdk/lib/ucrt/exec/spawnvp.cpp

212 lines
7.2 KiB
C++

//
// spawnvp.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Defines the -vp and -vpe flavors of the _exec() and _spawn() functions. See
// the comments in spawnv.cpp for details of the various flavors of these
// functions.
//
#include <corecrt_internal.h>
#include <corecrt_internal_traits.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <process.h>
#include <mbstring.h>
template <typename Character>
static intptr_t __cdecl common_spawnvp(
int const mode,
Character const* const file_name,
Character const* const* const arguments,
Character const* const* const environment
) throw()
{
typedef __crt_char_traits<Character> traits;
_VALIDATE_RETURN(file_name != nullptr, EINVAL, -1);
_VALIDATE_RETURN(file_name[0] != '\0', EINVAL, -1);
_VALIDATE_RETURN(arguments != nullptr, EINVAL, -1);
_VALIDATE_RETURN(arguments[0] != nullptr, EINVAL, -1);
_VALIDATE_RETURN(arguments[0][0] != '\0', EINVAL, -1);
__crt_errno_guard const guard_errno;
// Attempt to perform the spawn with the given file name. If this succeeds
// and is a spawn operation (mode != _P_OVERLAY), then it returns a status
// code other than -1 and we can just return it. If this succeds and is an
// exec operation (mode == _P_OVERLAY), then this call does not return.
intptr_t const initial_result = traits::tspawnve(mode, file_name, arguments, environment);
if (initial_result != -1)
return initial_result;
// If the spawn attempt failed, try to determine why. ENOENT indicates that
// the spawn operation itself failed, and there's nothing we can do:
if (errno != ENOENT)
return -1;
// If the file name is an absolute or relative path, then we do not search
// the %PATH%, so there is nothing left to do:
if (traits::tcschr(file_name, '\\') != nullptr)
return -1;
if (traits::tcschr(file_name, '/') != nullptr)
return -1;
if (file_name[1] == ':')
return -1;
// Otherwise, the file name string just names an executable. Let's try
// appending it to each path in the %PATH% and see if we can find the
// file to execute:
Character const path_name[] = { 'P', 'A', 'T', 'H', '\0' };
__crt_unique_heap_ptr<Character> path_value;
if (_ERRCHECK_EINVAL(traits::tdupenv_s_crt(path_value.get_address_of(), nullptr, path_name)) != 0)
return -1;
if (!path_value)
return -1;
// This will be used to store alternative path names to the executable:
__crt_unique_heap_ptr<Character> const owned_file_buffer(_calloc_crt_t(Character, _MAX_PATH));
if (!owned_file_buffer)
return -1;
Character* file_buffer = owned_file_buffer.get();
Character* path_state = path_value.get();
while ((path_state = traits::tgetpath(path_state, file_buffer, _MAX_PATH - 1)) != 0)
{
if (!*file_buffer)
break;
// Append a '\' if necessary:
Character* const last_character_it = file_buffer + traits::tcslen(file_buffer) - 1;
if (last_character_it != traits::tcsrchr(file_buffer, '\\') &&
last_character_it != traits::tcsrchr(file_buffer, '/'))
{
Character const backslash_string[] = { '\\', '\0' };
_ERRCHECK(traits::tcscat_s(file_buffer, _MAX_PATH, backslash_string));
}
// Make sure that the buffer is sufficiently large to hold the file name
// concatenated onto the end of this %PATH% component. If it is not, we
// return immediately (errno will still be set from the last call to
// _spawnve().
if (traits::tcslen(file_buffer) + traits::tcslen(file_name) >= _MAX_PATH)
break;
_ERRCHECK(traits::tcscat_s(file_buffer, _MAX_PATH, file_name));
// Try the spawn again with the newly constructed path. Like before, if
// this succeeds and is a spawn operation, then a status is returned and
// we can return immediately. If this succeeds and is an exec operation,
// the call does not return.
errno = 0;
intptr_t const result = traits::tspawnve(mode, file_buffer, arguments, environment);
if (result != -1)
return result;
// Two classes of failures are acceptable here: either the operation
// failed because the spawn operation itself failed (e.g., if the file
// could not be found)...
if (errno == ENOENT || _doserrno == ERROR_NOT_READY)
continue;
// ...or the path is a UNC path...
bool const is_unc_path_with_slashes =
traits::tcschr(file_buffer, '/') == file_buffer &&
traits::tcschr(file_buffer + 1, '/') == file_buffer + 1;
bool const is_unc_path_with_backslashes =
traits::tcschr(file_buffer, '\\') == file_buffer &&
traits::tcschr(file_buffer + 1, '\\') == file_buffer + 1;
if (is_unc_path_with_slashes || is_unc_path_with_backslashes)
continue;
// ...otherwise, we report the error back to the caller:
break;
}
return -1;
}
extern "C" intptr_t __cdecl _execvp(
char const* const file_name,
char const* const* const arguments
)
{
return common_spawnvp(_P_OVERLAY, file_name, arguments, static_cast<char const* const* const>(nullptr));
}
extern "C" intptr_t __cdecl _execvpe(
char const* const file_name,
char const* const* const arguments,
char const* const* const environment
)
{
return common_spawnvp(_P_OVERLAY, file_name, arguments, environment);
}
extern "C" intptr_t __cdecl _spawnvp(
int const mode,
char const* const file_name,
char const* const* const arguments
)
{
return common_spawnvp(mode, file_name, arguments, static_cast<char const* const* const>(nullptr));
}
extern "C" intptr_t __cdecl _spawnvpe(
int const mode,
char const* const file_name,
char const* const* const arguments,
char const* const* const environment
)
{
return common_spawnvp(mode, file_name, arguments, environment);
}
extern "C" intptr_t __cdecl _wexecvp(
wchar_t const* const file_name,
wchar_t const* const* const arguments
)
{
return common_spawnvp(_P_OVERLAY, file_name, arguments, static_cast<wchar_t const* const* const>(nullptr));
}
extern "C" intptr_t __cdecl _wexecvpe(
wchar_t const* const file_name,
wchar_t const* const* const arguments,
wchar_t const* const* const environment
)
{
return common_spawnvp(_P_OVERLAY, file_name, arguments, environment);
}
extern "C" intptr_t __cdecl _wspawnvp(
int const mode,
wchar_t const* const file_name,
wchar_t const* const* const arguments
)
{
return common_spawnvp(mode, file_name, arguments, static_cast<wchar_t const* const* const>(nullptr));
}
extern "C" intptr_t __cdecl _wspawnvpe(
int const mode,
wchar_t const* const file_name,
wchar_t const* const* const arguments,
wchar_t const* const* const environment
)
{
return common_spawnvp(mode, file_name, arguments, environment);
}