mirror of
https://github.com/reactos/reactos.git
synced 2025-08-07 05:52:57 +00:00
[UCRT] Import Microsoft.Windows.SDK.CRTSource version 10.0.22621.3
Imported from https://www.nuget.org/packages/Microsoft.Windows.SDK.CRTSource/10.0.22621.3 License: MIT
This commit is contained in:
parent
f1b60c66f0
commit
04e0dc4a7a
568 changed files with 115483 additions and 0 deletions
369
sdk/lib/ucrt/exec/cenvarg.cpp
Normal file
369
sdk/lib/ucrt/exec/cenvarg.cpp
Normal file
|
@ -0,0 +1,369 @@
|
|||
//
|
||||
// cenvarg.cpp
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Defines the _cenvarg() and _capture_argv functions, which transform argument
|
||||
// vectors and environments for use by the _exec() and _spawn() functions.
|
||||
//
|
||||
#include <corecrt_internal.h>
|
||||
#include <errno.h>
|
||||
#include <corecrt_internal_traits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#pragma warning(disable:__WARNING_POSTCONDITION_NULLTERMINATION_VIOLATION) // 26036
|
||||
|
||||
|
||||
// Converts a main()-style argv arguments vector into a command line. On success,
|
||||
// returns a pointer to the newly constructed arguments block; the caller is
|
||||
// responsible for freeing the string. On failure, returns null and sets errno.
|
||||
template <typename Character>
|
||||
static errno_t __cdecl construct_command_line(
|
||||
Character const* const* const argv,
|
||||
Character** const command_line_result
|
||||
) throw()
|
||||
{
|
||||
typedef __crt_char_traits<Character> traits;
|
||||
|
||||
*command_line_result = nullptr;
|
||||
|
||||
// Compute the number of bytes required to store the arguments in argv in a
|
||||
// command line string (including spaces between arguments and a terminator):
|
||||
size_t const command_line_count = [&]
|
||||
{
|
||||
size_t n = 0;
|
||||
for (Character const* const* it = argv; *it; n += traits::tcslen(*it++) + 1) { }
|
||||
|
||||
// If there were no arguments, return 1 so that we can return an empty
|
||||
// string:
|
||||
return __max(n, 1);
|
||||
}();
|
||||
|
||||
__crt_unique_heap_ptr<Character> command_line(_calloc_crt_t(Character, command_line_count));
|
||||
if (!command_line)
|
||||
{
|
||||
__acrt_errno_map_os_error(ERROR_NOT_ENOUGH_MEMORY);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
Character const* const* source_it = argv;
|
||||
Character* result_it = command_line.get();
|
||||
|
||||
// If there are no arguments, just return the empty string:
|
||||
if (*source_it == '\0')
|
||||
{
|
||||
*command_line_result = command_line.detach();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the arguments, separated by spaces:
|
||||
while (*source_it != '\0')
|
||||
{
|
||||
_ERRCHECK(traits::tcscpy_s(result_it, command_line_count - (result_it - command_line.get()), *source_it));
|
||||
result_it += traits::tcslen(*source_it);
|
||||
*result_it++ = ' ';
|
||||
++source_it;
|
||||
}
|
||||
|
||||
// Replace the last space with a terminator:
|
||||
result_it[-1] = '\0';
|
||||
|
||||
*command_line_result = command_line.detach();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Converts a main()-style envp environment vector into an environment block in
|
||||
// the form required by the CreateProcess API. On success, returns a pointer to
|
||||
// the newly constructed environment block; the caller is responsible for freeing
|
||||
// the block. On failure, returns null and sets errno.
|
||||
template <typename Character>
|
||||
static errno_t __cdecl construct_environment_block(
|
||||
_In_opt_z_ Character const* const* const envp,
|
||||
_Outptr_result_maybenull_ Character** const environment_block_result
|
||||
) throw()
|
||||
{
|
||||
typedef __crt_char_traits<Character> traits;
|
||||
|
||||
*environment_block_result = nullptr;
|
||||
|
||||
// If envp is null, we will use the current environment of this process as
|
||||
// the environment for the new process. No action is required in this case
|
||||
// because simply passing a null environment pointer to CreateProcess will
|
||||
// do the right thing.
|
||||
if (envp == nullptr)
|
||||
return 0;
|
||||
|
||||
// Get the value of the SystemRoot environment variable, if it is defined,
|
||||
// and compute the number of characters required to store it in the
|
||||
// envrionment block:
|
||||
Character const system_root_name[] = { 'S', 'y', 's', 't', 'e', 'm', 'R', 'o', 'o', 't', '\0' };
|
||||
|
||||
__crt_unique_heap_ptr<Character> system_root_value;
|
||||
if (_ERRCHECK_EINVAL(traits::tdupenv_s_crt(system_root_value.get_address_of(), nullptr, system_root_name)) != 0)
|
||||
return errno;
|
||||
|
||||
size_t const system_root_value_count = system_root_value
|
||||
? traits::tcslen(system_root_value.get()) + 1
|
||||
: 0;
|
||||
|
||||
size_t const system_root_count = _countof(system_root_name) + system_root_value_count;
|
||||
|
||||
// Compute the number of characters required to hold the environment
|
||||
// strings provided by the user:
|
||||
size_t const envp_count = [&]
|
||||
{
|
||||
size_t n = 2; // Account for double null terminator
|
||||
for (auto it = envp; *it != nullptr; n += traits::tcslen(*it++) + 1) { }
|
||||
return n;
|
||||
}();
|
||||
|
||||
// Get the current environment from the OS so that we can get the current
|
||||
// directory strings (those starting with '=') and append them to the user-
|
||||
// provided environment.
|
||||
__crt_unique_heap_ptr<Character> const os_environment(traits::get_environment_from_os());
|
||||
if (!os_environment)
|
||||
return EINVAL;
|
||||
|
||||
// Find the first shell environment variable:
|
||||
Character* const first_cwd = [&]
|
||||
{
|
||||
Character* it = os_environment.get();
|
||||
while (*it != '=')
|
||||
it += traits::tcslen(it) + 1;
|
||||
return it;
|
||||
}();
|
||||
|
||||
// Find the end of the shell environment variables (assume they are contiguous):
|
||||
Character* const last_cwd = [&]
|
||||
{
|
||||
Character* it = first_cwd;
|
||||
while (it[0] == '=' && it[1] != '\0' && it[2] == ':' && it[3] == '=')
|
||||
it += 4 + traits::tcslen(it + 4) + 1;
|
||||
return it;
|
||||
}();
|
||||
|
||||
size_t const cwd_count = last_cwd - first_cwd;
|
||||
|
||||
|
||||
// Check to see if the SystemRoot is already defined in the environment:
|
||||
bool const system_root_defined_in_environment = [&]
|
||||
{
|
||||
for (auto it = envp; *it != nullptr; ++it)
|
||||
{
|
||||
if (traits::tcsnicmp(*it, system_root_name, traits::tcslen(system_root_name)) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
// Allocate storage for the new environment:
|
||||
size_t const environment_block_count = system_root_defined_in_environment
|
||||
? envp_count + cwd_count
|
||||
: envp_count + cwd_count + system_root_count;
|
||||
|
||||
__crt_unique_heap_ptr<Character> environment_block(_calloc_crt_t(Character, environment_block_count));
|
||||
if (!environment_block)
|
||||
{
|
||||
__acrt_errno_map_os_error(ERROR_OUTOFMEMORY);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
// Build the environment block by concatenating the environment strings with
|
||||
// null characters between them, and with a double null terminator.
|
||||
Character* result_it = environment_block.get();
|
||||
size_t remaining_characters = environment_block_count;
|
||||
|
||||
// Copy the cwd strings into the new environment:
|
||||
if (cwd_count != 0)
|
||||
{
|
||||
memcpy(result_it, first_cwd, cwd_count * sizeof(Character));
|
||||
result_it += cwd_count;
|
||||
remaining_characters -= cwd_count;
|
||||
}
|
||||
|
||||
// Copy the environment strings from envp into the new environment:
|
||||
for (auto it = envp; *it != nullptr; ++it)
|
||||
{
|
||||
_ERRCHECK(traits::tcscpy_s(result_it, remaining_characters, *it));
|
||||
|
||||
size_t const count_copied = traits::tcslen(*it) + 1;
|
||||
result_it += count_copied;
|
||||
remaining_characters -= count_copied;
|
||||
}
|
||||
|
||||
// Copy the SystemRoot into the new environment:
|
||||
if (!system_root_defined_in_environment)
|
||||
{
|
||||
static Character const equal_sign[] = { '=', '\0' };
|
||||
|
||||
_ERRCHECK(traits::tcscpy_s(result_it, system_root_count, system_root_name));
|
||||
_ERRCHECK(traits::tcscat_s(result_it, system_root_count, equal_sign));
|
||||
if (system_root_value)
|
||||
{
|
||||
_ERRCHECK(traits::tcscat_s(result_it, system_root_count, system_root_value.get()));
|
||||
}
|
||||
result_it += system_root_count;
|
||||
}
|
||||
|
||||
// Null-terminate the environment block and return it. If the environment
|
||||
// block is empty, it requires two null terminators:
|
||||
if (result_it == environment_block.get())
|
||||
*result_it++ = '\0';
|
||||
|
||||
*result_it = '\0';
|
||||
|
||||
*environment_block_result = environment_block.detach();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Converts a main()-style argv arguments vector and envp environment vector into
|
||||
// a command line and an environment block, for use in the _exec and _spawn
|
||||
// functions. On success, returns 0 and sets the two result argumetns to point
|
||||
// to the newly created command line and environment block. The caller is
|
||||
// responsible for freeing these blocks. On failure, returns -1 and sets errno.
|
||||
template <typename Character>
|
||||
_Success_(return == 0)
|
||||
_Ret_range_(-1, 0)
|
||||
static int __cdecl common_pack_argv_and_envp(
|
||||
_In_z_ Character const* const* const argv,
|
||||
_In_opt_z_ Character const* const* const envp,
|
||||
_Outptr_result_maybenull_ Character** const command_line_result,
|
||||
_Outptr_result_maybenull_ Character** const environment_block_result
|
||||
) throw()
|
||||
{
|
||||
typedef __crt_char_traits<Character> traits;
|
||||
|
||||
__crt_unique_heap_ptr<Character> command_line;
|
||||
if (construct_command_line(argv, command_line.get_address_of()) != 0)
|
||||
return -1;
|
||||
|
||||
__crt_unique_heap_ptr<Character> environment_block;
|
||||
if (construct_environment_block(envp, environment_block.get_address_of()) != 0)
|
||||
return -1;
|
||||
|
||||
*command_line_result = command_line.detach();
|
||||
*environment_block_result = environment_block.detach();
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int __cdecl __acrt_pack_narrow_command_line_and_environment(
|
||||
char const* const* const argv,
|
||||
char const* const* const envp,
|
||||
char** const command_line_result,
|
||||
char** const environment_block_result
|
||||
)
|
||||
{
|
||||
return common_pack_argv_and_envp(argv, envp, command_line_result, environment_block_result);
|
||||
}
|
||||
|
||||
extern "C" int __cdecl __acrt_pack_wide_command_line_and_environment(
|
||||
wchar_t const* const* const argv,
|
||||
wchar_t const* const* const envp,
|
||||
wchar_t** const command_line_result,
|
||||
wchar_t** const environment_block_result
|
||||
)
|
||||
{
|
||||
return common_pack_argv_and_envp(argv, envp, command_line_result, environment_block_result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Creates an argv array for the _exec and _spawn functions. This function walks
|
||||
// the provided varargs list, copying the char* or wchar_t* pointers into an
|
||||
// array. The caller_array is used first; if it is too small to fit all of the
|
||||
// arguments, an array is dynamically allocated. A pointer to the argv array is
|
||||
// returned to the caller. If the returned pointer is not 'caller_array', the
|
||||
// caller must free the array. On failure, nullptr is returned and errno is set.
|
||||
template <typename Character>
|
||||
_Success_(return != 0)
|
||||
static Character** __cdecl common_capture_argv(
|
||||
_In_ va_list* const arglist,
|
||||
_In_z_ Character const* const first_argument,
|
||||
_When_(return == caller_array, _Post_z_)
|
||||
_Out_writes_(caller_array_count) Character** const caller_array,
|
||||
_In_ size_t const caller_array_count
|
||||
) throw()
|
||||
{
|
||||
Character** argv = caller_array;
|
||||
size_t argv_count = caller_array_count;
|
||||
|
||||
__crt_unique_heap_ptr<Character*> local_array;
|
||||
|
||||
size_t i = 0;
|
||||
Character* next_argument = const_cast<Character*>(first_argument);
|
||||
for (;;)
|
||||
{
|
||||
if (i >= argv_count)
|
||||
{
|
||||
_VALIDATE_RETURN_NOEXC(SIZE_MAX / 2 > argv_count, ENOMEM, nullptr);
|
||||
|
||||
// If we have run out of room in the caller-provided array, allocate
|
||||
// an array on the heap and copy the contents of the caller-provided
|
||||
// array:
|
||||
if (argv == caller_array)
|
||||
{
|
||||
local_array = _calloc_crt_t(Character*, argv_count * 2);
|
||||
_VALIDATE_RETURN_NOEXC(local_array.get() != nullptr, ENOMEM, nullptr);
|
||||
|
||||
_ERRCHECK(memcpy_s(local_array.get(), argv_count * 2, caller_array, caller_array_count));
|
||||
|
||||
argv = local_array.get();
|
||||
}
|
||||
// Otherwise, we have run out of room in a dynamically allocated
|
||||
// array. We need to reallocate:
|
||||
else
|
||||
{
|
||||
__crt_unique_heap_ptr<Character*> new_array(_recalloc_crt_t(Character*, local_array.get(), argv_count * 2));
|
||||
_VALIDATE_RETURN_NOEXC(new_array.get() != nullptr, ENOMEM, nullptr);
|
||||
|
||||
local_array.detach();
|
||||
local_array.attach(new_array.detach());
|
||||
|
||||
argv = local_array.get();
|
||||
}
|
||||
|
||||
argv_count *= 2;
|
||||
}
|
||||
|
||||
argv[i++] = next_argument;
|
||||
if (!next_argument)
|
||||
break;
|
||||
|
||||
#pragma warning(suppress:__WARNING_INCORRECT_ANNOTATION) // 26007 Possibly incorrect single element annotation on arglist
|
||||
next_argument = va_arg(*arglist, Character*);
|
||||
}
|
||||
|
||||
// At this point, we have succeeded; either local_array is null, or argv is
|
||||
// local_array. In either case, we detach so that we can transfer ownership
|
||||
// to the caller:
|
||||
local_array.detach();
|
||||
return argv;
|
||||
}
|
||||
|
||||
extern "C" char** __acrt_capture_narrow_argv(
|
||||
va_list* const arglist,
|
||||
char const* const first_argument,
|
||||
char** const caller_array,
|
||||
size_t const caller_array_count
|
||||
)
|
||||
{
|
||||
return common_capture_argv(arglist, first_argument, caller_array, caller_array_count);
|
||||
}
|
||||
|
||||
extern "C" wchar_t** __acrt_capture_wide_argv(
|
||||
va_list* const arglist,
|
||||
wchar_t const* const first_argument,
|
||||
wchar_t** const caller_array,
|
||||
size_t const caller_array_count
|
||||
)
|
||||
{
|
||||
return common_capture_argv(arglist, first_argument, caller_array, caller_array_count);
|
||||
}
|
67
sdk/lib/ucrt/exec/getproc.cpp
Normal file
67
sdk/lib/ucrt/exec/getproc.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
/***
|
||||
*getproc.c - Get the address of a procedure in a DLL.
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
*Purpose:
|
||||
* defines _getdllprocadd() - gets a procedure address by name or
|
||||
* ordinal
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <corecrt_internal.h>
|
||||
|
||||
#define _CRT_ENABLE_OBSOLETE_LOADLIBRARY_FUNCTIONS
|
||||
|
||||
#include <process.h>
|
||||
|
||||
/***
|
||||
*int (*)() _getdllprocaddr(handle, name, ordinal) - Get the address of a
|
||||
* DLL procedure specified by name or ordinal
|
||||
*
|
||||
*Purpose:
|
||||
*
|
||||
*Entry:
|
||||
* int handle - a DLL handle from _loaddll
|
||||
* char * name - Name of the procedure, or nullptr to get by ordinal
|
||||
* int ordinal - Ordinal of the procedure, or -1 to get by name
|
||||
*
|
||||
*
|
||||
*Exit:
|
||||
* returns a pointer to the procedure if found
|
||||
* returns nullptr if not found
|
||||
*
|
||||
*Exceptions:
|
||||
*
|
||||
*******************************************************************************/
|
||||
typedef int (__cdecl* proc_address_type)();
|
||||
|
||||
extern "C"
|
||||
DECLSPEC_GUARD_SUPPRESS
|
||||
proc_address_type __cdecl _getdllprocaddr(
|
||||
intptr_t const module_handle_value,
|
||||
char* const procedure_name,
|
||||
intptr_t const ordinal
|
||||
)
|
||||
{
|
||||
HMODULE const module_handle = reinterpret_cast<HMODULE>(module_handle_value);
|
||||
if (procedure_name == nullptr)
|
||||
{
|
||||
if (ordinal <= 65535)
|
||||
{
|
||||
char* const ordinal_as_string = reinterpret_cast<char*>(ordinal);
|
||||
return reinterpret_cast<proc_address_type>(
|
||||
GetProcAddress(module_handle, ordinal_as_string));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ordinal == static_cast<intptr_t>(-1))
|
||||
{
|
||||
return reinterpret_cast<proc_address_type>(
|
||||
GetProcAddress(module_handle, procedure_name));
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
65
sdk/lib/ucrt/exec/loaddll.cpp
Normal file
65
sdk/lib/ucrt/exec/loaddll.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/***
|
||||
*loaddll.c - load or free a Dynamic Link Library
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
*Purpose:
|
||||
* defines _loaddll() and _unloaddll() - load and unload DLL
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <corecrt_internal.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define _CRT_ENABLE_OBSOLETE_LOADLIBRARY_FUNCTIONS
|
||||
|
||||
#include <process.h>
|
||||
|
||||
/***
|
||||
*int _loaddll(filename) - Load a dll
|
||||
*
|
||||
*Purpose:
|
||||
* Load a DLL into memory
|
||||
*
|
||||
*Entry:
|
||||
* char *filename - file to load
|
||||
*
|
||||
*Exit:
|
||||
* returns a unique DLL (module) handle if succeeds
|
||||
* returns 0 if fails
|
||||
*
|
||||
*Exceptions:
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
extern "C" intptr_t __cdecl _loaddll(char* szName)
|
||||
{
|
||||
return reinterpret_cast<intptr_t>(__acrt_LoadLibraryExA(szName, nullptr, 0));
|
||||
}
|
||||
|
||||
/***
|
||||
*int _unloaddll(handle) - Unload a dll
|
||||
*
|
||||
*Purpose:
|
||||
* Unloads a DLL. The resources of the DLL will be freed if no other
|
||||
* processes are using it.
|
||||
*
|
||||
*Entry:
|
||||
* int handle - handle from _loaddll
|
||||
*
|
||||
*Exit:
|
||||
* returns 0 if succeeds
|
||||
* returns DOS error if fails
|
||||
*
|
||||
*Exceptions:
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
extern "C" int __cdecl _unloaddll(intptr_t hMod)
|
||||
{
|
||||
if (!FreeLibrary(reinterpret_cast<HMODULE>(hMod))) {
|
||||
return ((int)GetLastError());
|
||||
}
|
||||
return (0);
|
||||
}
|
143
sdk/lib/ucrt/exec/spawnl.cpp
Normal file
143
sdk/lib/ucrt/exec/spawnl.cpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
//
|
||||
// spawnl.cpp
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Defines the -l and -le 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 <stddef.h>
|
||||
#include <process.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
|
||||
template <typename Character>
|
||||
static intptr_t __cdecl common_spawnl(
|
||||
bool const pass_environment,
|
||||
int const mode,
|
||||
Character const* const file_name,
|
||||
Character const* const arguments,
|
||||
va_list varargs
|
||||
) 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] != '\0', EINVAL, -1);
|
||||
|
||||
Character* arguments_buffer[64];
|
||||
Character** const captured_arguments = traits::capture_argv(
|
||||
&varargs,
|
||||
arguments,
|
||||
&arguments_buffer[0],
|
||||
64);
|
||||
|
||||
_VALIDATE_RETURN_NOEXC(captured_arguments != nullptr, ENOMEM, -1);
|
||||
|
||||
__crt_unique_heap_ptr<Character*> const captured_arguments_cleanup(
|
||||
captured_arguments == arguments_buffer ? nullptr : captured_arguments);
|
||||
|
||||
Character const* const* const environment = pass_environment
|
||||
? va_arg(varargs, Character const* const*)
|
||||
: nullptr;
|
||||
|
||||
return traits::tspawnve(mode, file_name, captured_arguments, environment);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" intptr_t __cdecl _execl(
|
||||
char const* const file_name,
|
||||
char const* const arguments,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnl(false, _P_OVERLAY, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _execle(
|
||||
char const* const file_name,
|
||||
char const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnl(true, _P_OVERLAY, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _spawnl(
|
||||
int const mode,
|
||||
char const* const file_name,
|
||||
char const* const arguments,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnl(false, mode, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _spawnle(
|
||||
int const mode,
|
||||
char const* const file_name,
|
||||
char const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnl(true, mode, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" intptr_t __cdecl _wexecl(
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const arguments,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnl(false, _P_OVERLAY, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _wexecle(
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnl(true, _P_OVERLAY, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _wspawnl(
|
||||
int const mode,
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const arguments,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnl(false, mode, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _wspawnle(
|
||||
int const mode,
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnl(true, mode, file_name, arguments, varargs);
|
||||
}
|
140
sdk/lib/ucrt/exec/spawnlp.cpp
Normal file
140
sdk/lib/ucrt/exec/spawnlp.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
//
|
||||
// spawnlp.cpp
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Defines the -lp and -lpe 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 <stddef.h>
|
||||
#include <process.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
|
||||
template <typename Character>
|
||||
static intptr_t __cdecl common_spawnlp(
|
||||
bool const pass_environment,
|
||||
int const mode,
|
||||
Character const* const file_name,
|
||||
Character const* const arguments,
|
||||
va_list varargs
|
||||
) 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] != '\0', EINVAL, -1);
|
||||
|
||||
Character* arguments_buffer[64];
|
||||
Character** const captured_arguments = traits::capture_argv(
|
||||
&varargs,
|
||||
arguments,
|
||||
&arguments_buffer[0],
|
||||
64);
|
||||
|
||||
_VALIDATE_RETURN_NOEXC(captured_arguments != nullptr, ENOMEM, -1);
|
||||
|
||||
__crt_unique_heap_ptr<Character*> const captured_arguments_cleanup(
|
||||
captured_arguments == arguments_buffer ? nullptr : captured_arguments);
|
||||
|
||||
Character const* const* const environment = pass_environment
|
||||
? va_arg(varargs, Character const* const*)
|
||||
: nullptr;
|
||||
|
||||
return traits::tspawnvpe(mode, file_name, captured_arguments, environment);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" intptr_t _execlp(
|
||||
char const* const file_name,
|
||||
char const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnlp(false, _P_OVERLAY, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t _execlpe(
|
||||
char const* const file_name,
|
||||
char const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnlp(true, _P_OVERLAY, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t _spawnlp(
|
||||
int const mode,
|
||||
char const* const file_name,
|
||||
char const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnlp(false, mode, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t _spawnlpe(
|
||||
int const mode,
|
||||
char const* const file_name,
|
||||
char const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnlp(true, mode, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" intptr_t _wexeclp(
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnlp(false, _P_OVERLAY, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t _wexeclpe(
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnlp(true, _P_OVERLAY, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t _wspawnlp(
|
||||
int const mode,
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnlp(false, mode, file_name, arguments, varargs);
|
||||
}
|
||||
|
||||
extern "C" intptr_t _wspawnlpe(
|
||||
int const mode,
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const arguments,
|
||||
...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, arguments);
|
||||
return common_spawnlp(true, mode, file_name, arguments, varargs);
|
||||
}
|
||||
|
451
sdk/lib/ucrt/exec/spawnv.cpp
Normal file
451
sdk/lib/ucrt/exec/spawnv.cpp
Normal file
|
@ -0,0 +1,451 @@
|
|||
//
|
||||
// spawnv.cpp
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Defines the -v and -ve flavors of the _exec() and _spawn() functions.
|
||||
//
|
||||
// There are many varieties of the _exec() and _spawn() functions. A high level
|
||||
// summary of the behavioral differences is as follows:
|
||||
//
|
||||
// All of these functions execute a new process. The _spawn() functions all take
|
||||
// a 'mode' parameter, which specifies the way in which the process is started.
|
||||
// The 'mode' must be one of the _P_-prefixed modes from <process.h>. The _exec()
|
||||
// family of functions execute a new process, then call _exit() to terminate the
|
||||
// calling process. Each _exit() function is equivalent to the corresponding
|
||||
// _spawn() function with the _P_OVERLAY mode.
|
||||
//
|
||||
// There are eight variants each of _exec() and _spawn(), suffixed with -l, -le,
|
||||
// -lp, -lpe, -v, -ve, -vp, and -vpe.
|
||||
//
|
||||
// If a function has 'e' in its suffix, it accepts an environment with which to
|
||||
// create the new process. If a function does not have 'e' in its suffix, it
|
||||
// does not accept an environment and instead uses the environment of the calling
|
||||
// process. A call to an 'e' function with a null environment argument has the
|
||||
// same effect as calling the non-'e'-suffixed equivalent.
|
||||
//
|
||||
// Each _exec() or _spawn() function has either an 'l' or 'v' suffix. These have
|
||||
// equivalent functionality; they differ only in how they accept their arguments.
|
||||
// The 'l'-suffixed functions accept the command-line arguments and the environment
|
||||
// as varargs. There must be at least one command line argument (conventionally
|
||||
// the name of the program to be executed). If the function accepts an environment
|
||||
// (if it is 'e'-suffixed), then there must be a null pointer between the last
|
||||
// argument and the first environment variable. The arguments are terminated by a
|
||||
// null pointer.
|
||||
//
|
||||
// The 'v'-suffixed functions accept a pair of pointers: one to the argv vector
|
||||
// and one to the envp vector. Each is an array of pointers terminated by a null
|
||||
// pointer, similar to how arguments and the environment are passed to main().
|
||||
//
|
||||
// Finally, if a function has a 'p' in its suffix, and if the provided executable
|
||||
// file name is just a file name (and does not contain any path component), the
|
||||
// %PATH% is searched for an executable with the given name. If a function does
|
||||
// not have a 'p' suffix, then the environment is not searched; the executable
|
||||
// must be found simply by passing its name to CreateProcess.
|
||||
//
|
||||
// All functions return -1 and set errno on failure. On success, the _exec()
|
||||
// functions do not return. On success, the _spawn() functions return different
|
||||
// things, depending on the provided mode. See the CreateProcess invocation
|
||||
// logic in this file for details.
|
||||
//
|
||||
// Note that the only supported modes are wait and overlay.
|
||||
//
|
||||
// These functions may set errno to one of the following values:
|
||||
// * E2BIG: Failed in argument or environment processing because the argument
|
||||
// list or environment is too large.
|
||||
// * EACCESS: Locking or sharing violation on a file
|
||||
// * EMFILE: Too many files open
|
||||
// * ENOENT: Failed to find the program (no such file or directory)
|
||||
// * ENOEXEC: Failed in a call to exec() due to a bad executable format
|
||||
// * ENOMEM: Failed to allocate memory required for the spawn operation
|
||||
// * EINVAL: Invalid mode argumnt or process state for spawn (note that most
|
||||
// invalid arguments cause the invalid parameter handler to be
|
||||
// invoked).
|
||||
//
|
||||
#include <corecrt_internal.h>
|
||||
#include <corecrt_internal_lowio.h>
|
||||
#include <corecrt_internal_traits.h>
|
||||
#include <errno.h>
|
||||
#include <io.h>
|
||||
#include <mbstring.h>
|
||||
#include <process.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
// Accumulates the inheritable file handles into *data, in the structure expected
|
||||
// by the spawnee (see the lowio initialization code for the logic that decodes
|
||||
// this data structure). On success, *data and *size have the handle data array
|
||||
// and the size of the handle data, and true is returned. The caller must free
|
||||
// *data. On failure, false is returned and errno is set.
|
||||
static bool accumulate_inheritable_handles(
|
||||
BYTE** const data,
|
||||
size_t* const size,
|
||||
bool const include_std_handles
|
||||
) throw()
|
||||
{
|
||||
return __acrt_lock_and_call(__acrt_lowio_index_lock, [&]() -> bool
|
||||
{
|
||||
*data = nullptr;
|
||||
*size = 0;
|
||||
|
||||
// Count the number of handles to be inherited:
|
||||
size_t handle_count = 0;
|
||||
for (handle_count = _nhandle; handle_count != 0 && _osfile(handle_count - 1) != 0; --handle_count)
|
||||
{
|
||||
}
|
||||
|
||||
size_t const max_handle_count = (USHRT_MAX - sizeof(int)) / (sizeof(char) + sizeof(intptr_t));
|
||||
_VALIDATE_RETURN_NOEXC(handle_count < max_handle_count, ENOMEM, false);
|
||||
|
||||
size_t const handle_data_header_size = sizeof(int);
|
||||
size_t const handle_data_element_size = sizeof(char) + sizeof(intptr_t);
|
||||
|
||||
unsigned short const handle_data_size = static_cast<unsigned short>(
|
||||
handle_data_header_size +
|
||||
handle_count * handle_data_element_size);
|
||||
|
||||
__crt_unique_heap_ptr<BYTE> handle_data(_calloc_crt_t(BYTE, handle_data_size));
|
||||
_VALIDATE_RETURN_NOEXC(handle_data.get() != nullptr, ENOMEM, false);
|
||||
|
||||
// Set the handle count in the data:
|
||||
*reinterpret_cast<int*>(handle_data.get()) = static_cast<int>(handle_count);
|
||||
|
||||
auto const first_flags = reinterpret_cast<char*>(handle_data.get() + sizeof(int));
|
||||
auto const first_handle = reinterpret_cast<intptr_t UNALIGNED*>(first_flags + (handle_count * sizeof(char)));
|
||||
|
||||
// Copy the handle data:
|
||||
auto flags_it = first_flags;
|
||||
auto handle_it = first_handle;
|
||||
for (size_t i = 0; i != handle_count; ++i, ++flags_it, ++handle_it)
|
||||
{
|
||||
__crt_lowio_handle_data* const pio = _pioinfo(i);
|
||||
if ((pio->osfile & FNOINHERIT) == 0)
|
||||
{
|
||||
*flags_it = pio->osfile;
|
||||
*handle_it = pio->osfhnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
*flags_it = 0;
|
||||
*handle_it = reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude the first three handles (stdin, stdout, stderr) if asked:
|
||||
if (!include_std_handles)
|
||||
{
|
||||
flags_it = first_flags;
|
||||
handle_it = first_handle;
|
||||
for (size_t i = 0; i != __min(handle_count, 3); ++i, ++flags_it, ++handle_it)
|
||||
{
|
||||
*flags_it = 0;
|
||||
*handle_it = reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
*data = handle_data.detach();
|
||||
*size = handle_data_size;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool __cdecl should_create_unicode_environment(char) throw() { return false; }
|
||||
static bool __cdecl should_create_unicode_environment(wchar_t) throw() { return true; }
|
||||
|
||||
|
||||
|
||||
// Spawns a child process. The mode must be one of the _P-modes from <process.h>.
|
||||
// The return value depends on the mode:
|
||||
// * _P_OVERLAY: On success, calls _exit() and does not return. Returns -1 on failure.
|
||||
// * _P_WAIT: Returns (termination_code << 8 + result_code)
|
||||
// * _P_DETACH: Returns 0 on success; -1 on failure
|
||||
// * Others: Returns a handle to the process. The caller must close the handle.
|
||||
template <typename Character>
|
||||
static intptr_t __cdecl execute_command(
|
||||
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(arguments != nullptr, EINVAL, -1);
|
||||
|
||||
_VALIDATE_CLEAR_OSSERR_RETURN(mode >= 0 && mode <= _P_DETACH, EINVAL, -1);
|
||||
|
||||
__crt_unique_heap_ptr<Character> command_line;
|
||||
__crt_unique_heap_ptr<Character> environment_block;
|
||||
if (traits::pack_command_line_and_environment(
|
||||
arguments,
|
||||
environment,
|
||||
command_line.get_address_of(),
|
||||
environment_block.get_address_of()
|
||||
) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
__crt_unique_heap_ptr<BYTE> handle_data;
|
||||
size_t handle_data_size;
|
||||
if (!accumulate_inheritable_handles(handle_data.get_address_of(), &handle_data_size, mode != _P_DETACH))
|
||||
return -1;
|
||||
|
||||
DWORD creation_flags = 0;
|
||||
if (mode == _P_DETACH)
|
||||
creation_flags |= DETACHED_PROCESS;
|
||||
|
||||
if (should_create_unicode_environment(Character()))
|
||||
creation_flags |= CREATE_UNICODE_ENVIRONMENT;
|
||||
|
||||
_doserrno = 0;
|
||||
|
||||
STARTUPINFOW startup_info = { };
|
||||
startup_info.cb = sizeof(startup_info);
|
||||
startup_info.cbReserved2 = static_cast<WORD>(handle_data_size);
|
||||
startup_info.lpReserved2 = handle_data.get();
|
||||
|
||||
PROCESS_INFORMATION process_info;
|
||||
BOOL const create_process_status = traits::create_process(
|
||||
const_cast<Character*>(file_name),
|
||||
command_line.get(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
TRUE,
|
||||
creation_flags,
|
||||
environment_block.get(),
|
||||
nullptr,
|
||||
&startup_info,
|
||||
&process_info);
|
||||
|
||||
__crt_unique_handle process_handle(process_info.hProcess);
|
||||
__crt_unique_handle thread_handle(process_info.hThread);
|
||||
|
||||
if (!create_process_status)
|
||||
{
|
||||
__acrt_errno_map_os_error(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mode == _P_OVERLAY)
|
||||
{
|
||||
// Destroy ourselves:
|
||||
_exit(0);
|
||||
}
|
||||
else if (mode == _P_WAIT)
|
||||
{
|
||||
WaitForSingleObject(process_info.hProcess, static_cast<DWORD>(-1));
|
||||
|
||||
// Return the termination code and exit code. Note that we return
|
||||
// the full exit code.
|
||||
DWORD exit_code;
|
||||
if (0 != GetExitCodeProcess(process_info.hProcess, &exit_code))
|
||||
{
|
||||
return static_cast<int>(exit_code);
|
||||
}
|
||||
else
|
||||
{
|
||||
__acrt_errno_map_os_error(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (mode == _P_DETACH)
|
||||
{
|
||||
/* like totally detached asynchronous spawn, dude,
|
||||
close process handle, return 0 for success */
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Asynchronous spawn: return process handle:
|
||||
return reinterpret_cast<intptr_t>(process_handle.detach());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename Character>
|
||||
static intptr_t __cdecl common_spawnv(
|
||||
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);
|
||||
|
||||
Character const* const final_backslash = traits::tcsrchr(file_name, '\\');
|
||||
Character const* const final_slash = traits::tcsrchr(file_name, '/');
|
||||
|
||||
Character const* mutated_file_name = file_name;
|
||||
Character const* end_of_directory = final_backslash;
|
||||
if (!final_slash)
|
||||
{
|
||||
if (!final_backslash)
|
||||
{
|
||||
Character const* const final_colon = traits::tcsrchr(file_name, ':');
|
||||
if (final_colon)
|
||||
{
|
||||
end_of_directory = final_colon;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The path is a file name only. We force it to be a relative
|
||||
// path name.
|
||||
size_t const file_name_size = traits::tcslen(file_name) + 3;
|
||||
__crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, file_name_size));
|
||||
if (!buffer)
|
||||
return -1;
|
||||
|
||||
static Character const dot_slash[] = { '.', '\\', '\0' };
|
||||
_ERRCHECK(traits::tcscpy_s(buffer.get(), file_name_size, dot_slash));
|
||||
_ERRCHECK(traits::tcscat_s(buffer.get(), file_name_size, file_name));
|
||||
|
||||
mutated_file_name = buffer.detach();
|
||||
end_of_directory = mutated_file_name + 2; // Adjust for ".\"
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!final_backslash || final_slash > final_backslash)
|
||||
{
|
||||
end_of_directory = final_slash;
|
||||
}
|
||||
|
||||
// If we allocated a file name above, make sure we clean it up:
|
||||
__crt_unique_heap_ptr<Character const> const mutated_file_name_cleanup(file_name == mutated_file_name
|
||||
? nullptr
|
||||
: mutated_file_name);
|
||||
|
||||
if (traits::tcsrchr(end_of_directory, '.'))
|
||||
{
|
||||
// If an extension was provided, just invoke the path:
|
||||
if (traits::taccess_s(mutated_file_name, 0) == 0)
|
||||
{
|
||||
return execute_command(mode, mutated_file_name, arguments, environment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no extension was provided, try known executable extensions:
|
||||
size_t const buffer_size = traits::tcslen(mutated_file_name) + 5;
|
||||
__crt_unique_heap_ptr<Character> const buffer(_calloc_crt_t(Character, buffer_size));
|
||||
if (!buffer)
|
||||
return -1;
|
||||
|
||||
_ERRCHECK(traits::tcscpy_s(buffer.get(), buffer_size, mutated_file_name));
|
||||
Character* extension_buffer = buffer.get() + buffer_size - 5;
|
||||
|
||||
typedef Character const extension_type[5];
|
||||
static extension_type const extensions[4] =
|
||||
{
|
||||
{ '.', 'c', 'o', 'm', '\0' },
|
||||
{ '.', 'e', 'x', 'e', '\0' },
|
||||
{ '.', 'b', 'a', 't', '\0' },
|
||||
{ '.', 'c', 'm', 'd', '\0' }
|
||||
};
|
||||
|
||||
errno_t const saved_errno = errno;
|
||||
|
||||
extension_type const* const first_extension = extensions;
|
||||
extension_type const* const last_extension = first_extension + _countof(extensions);
|
||||
for (auto it = first_extension; it != last_extension; ++it)
|
||||
{
|
||||
_ERRCHECK(traits::tcscpy_s(extension_buffer, 5, *it));
|
||||
|
||||
if (traits::taccess_s(buffer.get(), 0) == 0)
|
||||
{
|
||||
errno = saved_errno;
|
||||
return execute_command(mode, buffer.get(), arguments, environment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" intptr_t __cdecl _execv(
|
||||
char const* const file_name,
|
||||
char const* const* const arguments
|
||||
)
|
||||
{
|
||||
return common_spawnv(_P_OVERLAY, file_name, arguments, static_cast<char const* const*>(nullptr));
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _execve(
|
||||
char const* const file_name,
|
||||
char const* const* const arguments,
|
||||
char const* const* const environment
|
||||
)
|
||||
{
|
||||
return common_spawnv(_P_OVERLAY, file_name, arguments, environment);
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _spawnv(
|
||||
int const mode,
|
||||
char const* const file_name,
|
||||
char const* const* const arguments
|
||||
)
|
||||
{
|
||||
return common_spawnv(mode, file_name, arguments, static_cast<char const* const*>(nullptr));
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _spawnve(
|
||||
int const mode,
|
||||
char const* const file_name,
|
||||
char const* const* const arguments,
|
||||
char const* const* const environment
|
||||
)
|
||||
{
|
||||
return common_spawnv(mode, file_name, arguments, environment);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" intptr_t __cdecl _wexecv(
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const* const arguments
|
||||
)
|
||||
{
|
||||
return common_spawnv(_P_OVERLAY, file_name, arguments, static_cast<wchar_t const* const*>(nullptr));
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _wexecve(
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const* const arguments,
|
||||
wchar_t const* const* const environment
|
||||
)
|
||||
{
|
||||
return common_spawnv(_P_OVERLAY, file_name, arguments, environment);
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _wspawnv(
|
||||
int const mode,
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const* const arguments
|
||||
)
|
||||
{
|
||||
return common_spawnv(mode, file_name, arguments, static_cast<wchar_t const* const*>(nullptr));
|
||||
}
|
||||
|
||||
extern "C" intptr_t __cdecl _wspawnve(
|
||||
int const mode,
|
||||
wchar_t const* const file_name,
|
||||
wchar_t const* const* const arguments,
|
||||
wchar_t const* const* const environment
|
||||
)
|
||||
{
|
||||
return common_spawnv(mode, file_name, arguments, environment);
|
||||
}
|
212
sdk/lib/ucrt/exec/spawnvp.cpp
Normal file
212
sdk/lib/ucrt/exec/spawnvp.cpp
Normal file
|
@ -0,0 +1,212 @@
|
|||
//
|
||||
// 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);
|
||||
}
|
86
sdk/lib/ucrt/exec/system.cpp
Normal file
86
sdk/lib/ucrt/exec/system.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// system.cpp
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// Defines the system() family of functions, which execute a command via the
|
||||
// shell.
|
||||
//
|
||||
#include <corecrt_internal.h>
|
||||
#include <corecrt_internal_traits.h>
|
||||
#include <process.h>
|
||||
#include <io.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
|
||||
// Executes a command via the shell. If the command is null, attempts to execute
|
||||
// the command processor specified by the %COMSPEC% environment variable. If
|
||||
// that fails, attempts to execute cmd.exe.
|
||||
template <typename Character>
|
||||
static int __cdecl common_system(Character const* const command) throw()
|
||||
{
|
||||
typedef __crt_char_traits<Character> traits;
|
||||
|
||||
static Character const comspec_name[] = { 'C', 'O', 'M', 'S', 'P', 'E', 'C', '\0' };
|
||||
static Character const cmd_exe[] = { 'c', 'm', 'd', '.', 'e', 'x', 'e', '\0' };
|
||||
static Character const slash_c[] = { '/', 'c', '\0' };
|
||||
|
||||
__crt_unique_heap_ptr<Character> comspec_value;
|
||||
_ERRCHECK_EINVAL(traits::tdupenv_s_crt(comspec_value.get_address_of(), nullptr, comspec_name));
|
||||
|
||||
// If the command is null, return TRUE only if %COMSPEC% is set and the file
|
||||
// to which it points exists.
|
||||
if (!command)
|
||||
{
|
||||
if (!comspec_value)
|
||||
return 0;
|
||||
|
||||
return traits::taccess_s(comspec_value.get(), 0) == 0;
|
||||
}
|
||||
|
||||
_ASSERTE(command[0] != '\0');
|
||||
|
||||
Character const* arguments[4] =
|
||||
{
|
||||
comspec_value.get(),
|
||||
slash_c,
|
||||
command,
|
||||
nullptr
|
||||
};
|
||||
|
||||
if (comspec_value)
|
||||
{
|
||||
errno_t const saved_errno = errno;
|
||||
errno = 0;
|
||||
|
||||
int const result = static_cast<int>(traits::tspawnve(_P_WAIT, arguments[0], arguments, nullptr));
|
||||
if (result != -1)
|
||||
{
|
||||
errno = saved_errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (errno != ENOENT && errno != EACCES)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// If the error wasn't one of those two errors, try again with cmd.exe...
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
arguments[0] = cmd_exe;
|
||||
return static_cast<int>(traits::tspawnvpe(_P_WAIT, arguments[0], arguments, nullptr));
|
||||
}
|
||||
|
||||
extern "C" int __cdecl system(char const* const command)
|
||||
{
|
||||
return common_system(command);
|
||||
}
|
||||
|
||||
extern "C" int __cdecl _wsystem(wchar_t const* const command)
|
||||
{
|
||||
return common_system(command);
|
||||
}
|
87
sdk/lib/ucrt/exec/wait.cpp
Normal file
87
sdk/lib/ucrt/exec/wait.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
/***
|
||||
*wait.c - wait for child process to terminate
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
*Purpose:
|
||||
* defines _wait() - wait for child process to terminate
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
#include <corecrt_internal.h>
|
||||
#include <errno.h>
|
||||
#include <process.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/***
|
||||
*int _cwait(stat_loc, process_id, action_code) - wait for specific child
|
||||
* process
|
||||
*
|
||||
*Purpose:
|
||||
* The function _cwait() suspends the calling-process until the specified
|
||||
* child-process terminates. If the specifed child-process terminated
|
||||
* prior to the call to _cwait(), return is immediate.
|
||||
*
|
||||
*Entry:
|
||||
* int *stat_loc - pointer to where status is stored or nullptr
|
||||
* process_id - specific process id to be interrogated (0 means any)
|
||||
* action_code - specific action to perform on process ID
|
||||
* either _WAIT_CHILD or _WAIT_GRANDCHILD
|
||||
*
|
||||
*Exit:
|
||||
* process ID of terminated child or -1 on error
|
||||
*
|
||||
* *stat_loc is updated to contain the following:
|
||||
* Normal termination: lo-byte = 0, hi-byte = child exit code
|
||||
* Abnormal termination: lo-byte = term status, hi-byte = 0
|
||||
*
|
||||
*Exceptions:
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
extern "C" intptr_t __cdecl _cwait(
|
||||
int* const exit_code_result,
|
||||
intptr_t const process_id,
|
||||
int const action_code
|
||||
)
|
||||
{
|
||||
DBG_UNREFERENCED_PARAMETER(action_code);
|
||||
|
||||
if (exit_code_result)
|
||||
*exit_code_result = static_cast<DWORD>(-1);
|
||||
|
||||
// Explicitly check for process ids -1 and -2. In Windows NT, -1 is a handle
|
||||
// to the current process and -2 is a handle to the current thread, and the
|
||||
// OS will let you wait (forever) on either.
|
||||
_VALIDATE_RETURN_NOEXC(process_id != -1 && process_id != -2, ECHILD, -1);
|
||||
|
||||
__crt_unique_handle process_handle(reinterpret_cast<HANDLE>(process_id));
|
||||
|
||||
// Wait for the child process and get its exit code:
|
||||
DWORD exit_code;
|
||||
if (WaitForSingleObject(process_handle.get(), static_cast<DWORD>(-1)) == 0 &&
|
||||
GetExitCodeProcess(process_handle.get(), &exit_code))
|
||||
{
|
||||
if (exit_code_result)
|
||||
*exit_code_result = exit_code;
|
||||
|
||||
return process_id;
|
||||
}
|
||||
|
||||
// One of the API calls failed; map the error and return failure.
|
||||
DWORD const os_error = GetLastError();
|
||||
if (os_error == ERROR_INVALID_HANDLE)
|
||||
{
|
||||
errno = ECHILD;
|
||||
_doserrno = os_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
__acrt_errno_map_os_error(os_error);
|
||||
}
|
||||
|
||||
if (exit_code_result)
|
||||
*exit_code_result = static_cast<DWORD>(-1);
|
||||
|
||||
return -1;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue