diff --git a/include/eepp/system/process.hpp b/include/eepp/system/process.hpp index 7660ec241..785f41391 100644 --- a/include/eepp/system/process.hpp +++ b/include/eepp/system/process.hpp @@ -30,7 +30,11 @@ class EE_API Process { // Search for program names in the PATH variable. Always enabled on Windows. // Note: this will **not** search for paths in any provided custom environment // and instead uses the PATH of the spawning process. - SearchUserPath = 0x10 + SearchUserPath = 0x10, + + // If command path is not absolute it will try to resolve the absolute path + // and use that. + UseAbsolutePath = 0x20 }; static inline constexpr Uint32 getDefaultOptions() { diff --git a/src/eepp/system/process.cpp b/src/eepp/system/process.cpp index 684865878..02978eeca 100644 --- a/src/eepp/system/process.cpp +++ b/src/eepp/system/process.cpp @@ -134,10 +134,10 @@ bool Process::create( const std::string& command, const std::vector if ( mProcess ) return false; std::vector strings; + std::string rcommand; mProcess = eeMalloc( sizeof( subprocess_s ) ); memset( mProcess, 0, sizeof( subprocess_s ) ); if ( !environment.empty() ) { - std::string rcommand; if ( FileSystem::fileExists( command ) ) { rcommand = command; } else { @@ -188,7 +188,16 @@ bool Process::create( const std::string& command, const std::vector return ret; } - strings.push_back( command.c_str() ); + if ( ( options & UseAbsolutePath ) && command.find_first_of( "\\/" ) == std::string::npos ) { + rcommand = Sys::which( command ); + if ( !rcommand.empty() ) + strings.push_back( rcommand.c_str() ); + else + strings.push_back( command.c_str() ); + } else { + strings.push_back( command.c_str() ); + } + for ( size_t i = 0; i < cmdArr.size(); ++i ) strings.push_back( cmdArr[i].c_str() ); strings.push_back( NULL ); diff --git a/src/thirdparty/subprocess/subprocess.h b/src/thirdparty/subprocess/subprocess.h index 54402df03..0d1d61f52 100644 --- a/src/thirdparty/subprocess/subprocess.h +++ b/src/thirdparty/subprocess/subprocess.h @@ -39,6 +39,7 @@ /* disable warning: '__cplusplus' is not defined as a preprocessor macro, * replacing with '0' for '#if/#elif' */ #pragma warning(disable : 4668) +#define _WINBASE_ #endif #include @@ -282,9 +283,9 @@ struct subprocess_security_attributes_s { struct subprocess_startup_info_s { unsigned long cb; - char *lpReserved; - char *lpDesktop; - char *lpTitle; + wchar_t* lpReserved; + wchar_t* lpDesktop; + wchar_t* lpTitle; unsigned long dwX; unsigned long dwY; unsigned long dwXSize; @@ -301,6 +302,48 @@ struct subprocess_startup_info_s { void *hStdError; }; +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef int BOOL; +typedef wchar_t* LPWSTR; +typedef const wchar_t* LPCWSTR; +typedef unsigned char* LPBYTE; +typedef void* HANDLE; +typedef void* LPVOID; +typedef char CHAR; + +#define OFS_MAXPATHNAME 128 +typedef struct _OFSTRUCT { + BYTE cBytes; + BYTE fFixedDisk; + WORD nErrCode; + WORD Reserved1; + WORD Reserved2; + CHAR szPathName[OFS_MAXPATHNAME]; +} OFSTRUCT, *LPOFSTRUCT, *POFSTRUCT; + +typedef struct _STARTUPINFOW { + DWORD cb; + LPWSTR lpReserved; + LPWSTR lpDesktop; + LPWSTR lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + LPBYTE lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; +} STARTUPINFOW, *LPSTARTUPINFOW; + struct subprocess_overlapped_s { uintptr_t Internal; uintptr_t InternalHigh; @@ -354,6 +397,74 @@ __declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED, unsigned long *, int); __declspec(dllimport) int __stdcall FlushFileBuffers(void *); +__declspec(dllimport) int __stdcall CreateProcessW( + const wchar_t* lpApplicationName, wchar_t* lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + const wchar_t* lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation ); + +#ifndef CP_UTF8 +#define CP_UTF8 65001 // UTF-8 translation +#endif + +#ifndef MB_ERR_INVALID_CHARS +#define MB_ERR_INVALID_CHARS 0x00000008 // error for invalid chars +#endif + +#ifndef CREATE_UNICODE_ENVIRONMENT +#define CREATE_UNICODE_ENVIRONMENT 0x00000400 +#endif + +__declspec( dllimport ) int __stdcall MultiByteToWideChar( + unsigned int CodePage, // UINT + unsigned long dwFlags, // DWORD + const char* lpMultiByteStr, // LPCCH -> const char* + int cbMultiByte, + wchar_t* lpWideCharStr, // LPWSTR -> wchar_t* + int cchWideChar ); + +wchar_t* Utf8ToUtf16_C( const char* utf8Str ) { + if ( utf8Str == NULL ) { + return NULL; + } + + int wideCharCount = MultiByteToWideChar( + CP_UTF8, + MB_ERR_INVALID_CHARS, + utf8Str, + -1, + NULL, + 0 + ); + + if ( wideCharCount == 0 ) { + return NULL; + } + + wchar_t* wideStr = (wchar_t*)malloc( wideCharCount * sizeof( wchar_t ) ); + if ( wideStr == NULL ) { + return NULL; + } + + int result = MultiByteToWideChar( CP_UTF8, + MB_ERR_INVALID_CHARS, // Use same flags + utf8Str, -1, + wideStr, // Destination buffer + wideCharCount // Size of destination buffer in WCHARs + ); + + if ( result == 0 ) { + free( wideStr ); + return NULL; + } + + return wideStr; +} + #if defined(_DLL) #define SUBPROCESS_DLLIMPORT __declspec(dllimport) #else @@ -500,15 +611,15 @@ int subprocess_create_ex(const char *const commandLine[], int options, subprocess_size_t len; int i, j; int need_quoting; - unsigned long flags = 0; + unsigned long flags = CREATE_UNICODE_ENVIRONMENT; const unsigned long startFUseStdHandles = 0x00000100; const unsigned long handleFlagInherit = 0x00000001; const unsigned long createNoWindow = 0x08000000; struct subprocess_subprocess_information_s processInfo; struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), SUBPROCESS_NULL, 1}; - char *used_environment = SUBPROCESS_NULL; - struct subprocess_startup_info_s startInfo = {0, + wchar_t *used_environment = SUBPROCESS_NULL; + STARTUPINFOW startInfo = { 0, SUBPROCESS_NULL, SUBPROCESS_NULL, SUBPROCESS_NULL, @@ -537,36 +648,57 @@ int subprocess_create_ex(const char *const commandLine[], int options, if (subprocess_option_inherit_environment != (options & subprocess_option_inherit_environment)) { if (SUBPROCESS_NULL == environment) { - used_environment = SUBPROCESS_CONST_CAST(char *, "\0\0"); + used_environment = SUBPROCESS_NULL; } else { - // We always end with two null terminators. - len = 2; + if ( environment[0] == NULL ) { + used_environment = SUBPROCESS_NULL; + } else { + size_t total_wchars_for_env = 0; + for ( i = 0; environment[i] != NULL; i++ ) { + int wchars_for_str = MultiByteToWideChar( CP_UTF8, 0, environment[i], -1, NULL, 0 ); + if ( wchars_for_str == 0 ) { + if ( environment[i][0] != '\0' ) + return -1; + wchars_for_str = 1; + } + total_wchars_for_env += wchars_for_str; + } + total_wchars_for_env += 1; - for (i = 0; environment[i]; i++) { - for (j = 0; '\0' != environment[i][j]; j++) { - len++; - } + used_environment = (wchar_t*)malloc( total_wchars_for_env * sizeof( wchar_t ) ); + if ( !used_environment ) + return -1; - // For the null terminator too. - len++; - } + wchar_t* current_env_ptr = used_environment; + size_t remaining_wchar_capacity = total_wchars_for_env; + for ( i = 0; environment[i] != NULL; i++ ) { + int wchars_for_str = MultiByteToWideChar( CP_UTF8, 0, environment[i], -1, NULL, 0 ); + if ( wchars_for_str == 0 ) { + if ( environment[i][0] != '\0' ) + return -1; + wchars_for_str = 1; + } - used_environment = SUBPROCESS_CAST(char *, _alloca(len)); + if ( wchars_for_str > remaining_wchar_capacity ) { + return -1; + } - // Re-use len for the insertion position - len = 0; + int wchars_written = MultiByteToWideChar( + CP_UTF8, 0, environment[i], -1, current_env_ptr, remaining_wchar_capacity ); + if ( wchars_written == 0 || + wchars_written != wchars_for_str ) { + return -1; + } + current_env_ptr += wchars_written; + remaining_wchar_capacity -= wchars_written; + } - for (i = 0; environment[i]; i++) { - for (j = 0; '\0' != environment[i][j]; j++) { - used_environment[len++] = environment[i][j]; - } - - used_environment[len++] = '\0'; - } - - // End with the two null terminators. - used_environment[len++] = '\0'; - used_environment[len++] = '\0'; + if ( remaining_wchar_capacity > 0 ) { + *current_env_ptr = L'\0'; + } else { + return -1; + } + } } } else { if (SUBPROCESS_NULL != environment) { @@ -744,20 +876,30 @@ int subprocess_create_ex(const char *const commandLine[], int options, commandLineCombined[len] = '\0'; - if (!CreateProcessA( + wchar_t* commandLineCombinedW = Utf8ToUtf16_C( commandLineCombined ); + wchar_t* working_directoryW = Utf8ToUtf16_C( working_directory ); + + if (!CreateProcessW( SUBPROCESS_NULL, - commandLineCombined, // command line + commandLineCombinedW, // command line SUBPROCESS_NULL, // process security attributes SUBPROCESS_NULL, // primary thread security attributes 1, // handles are inherited flags, // creation flags - used_environment, // used environment - working_directory, // set process working directory - SUBPROCESS_PTR_CAST(LPSTARTUPINFOA, + used_environment, // used environment + working_directoryW, // set process working directory + SUBPROCESS_PTR_CAST( LPSTARTUPINFOW, &startInfo), // STARTUPINFO pointer SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) { + free( commandLineCombinedW ); + free( used_environment ); + free( working_directoryW ); return -1; } + + free( commandLineCombinedW ); + free( used_environment ); + free( working_directoryW ); out_process->hProcess = processInfo.hProcess; diff --git a/src/tools/ecode/plugins/git/git.cpp b/src/tools/ecode/plugins/git/git.cpp index 6aa951a88..d63e40652 100644 --- a/src/tools/ecode/plugins/git/git.cpp +++ b/src/tools/ecode/plugins/git/git.cpp @@ -40,10 +40,13 @@ int Git::git( const std::string& args, const std::string& projectDir, std::strin Clock clock; buf.clear(); Process p; - p.create( mGitPath, args, + if ( !p.create( mGitPath, args, Process::CombinedStdoutStderr | Process::Options::NoWindow | Process::Options::EnableAsync | Process::Options::InheritEnvironment, - { { "LC_ALL", "en_US.UTF-8" } }, projectDir.empty() ? mProjectPath : projectDir ); + { { "LC_ALL", "en_US.UTF-8" } }, + projectDir.empty() ? mProjectPath : projectDir ) ) { + return EXIT_FAILURE; + } p.readAllStdOut( buf ); int retCode = 0; p.join( &retCode ); diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index 9b21eeb34..c20269956 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -1310,8 +1310,11 @@ bool LSPClientServer::start() { } if ( !cmd.empty() ) { - bool ret = mProcess.create( cmd, Process::getDefaultOptions() | Process::EnableAsync, - mLSP.env, mRootPath ); + auto flags = Process::getDefaultOptions() | Process::EnableAsync; +#if EE_PLATFORM == EE_PLATFORM_WIN + flags |= Process::UseAbsolutePath; +#endif + bool ret = mProcess.create( cmd, flags, mLSP.env, mRootPath ); if ( ret ) { if ( mProcess.isAlive() ) { mUsingProcess = true;