123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750 |
- /*
- * mptWine.cpp
- * -----------
- * Purpose: Wine stuff.
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "mptWine.h"
- #include "mptOS.h"
- #include "../common/mptFileIO.h"
- #include <deque>
- #include <map>
- #if MPT_OS_WINDOWS
- #include <windows.h>
- #endif
- OPENMPT_NAMESPACE_BEGIN
- #if MPT_OS_WINDOWS
- namespace mpt
- {
- namespace Wine
- {
- Context::Context(mpt::OS::Wine::VersionContext versionContext)
- : m_VersionContext(versionContext)
- , wine_get_dos_file_name(nullptr)
- , wine_get_unix_file_name(nullptr)
- {
- if(!mpt::OS::Windows::IsWine())
- {
- throw mpt::Wine::Exception("Wine not detected.");
- }
- if(!m_VersionContext.Version().IsValid())
- {
- throw mpt::Wine::Exception("Unknown Wine version detected.");
- }
- m_Kernel32 = std::make_shared<std::optional<mpt::library>>(mpt::library::load({ mpt::library::path_search::system, mpt::library::path_prefix::none, MPT_PATH("kernel32.dll"), mpt::library::path_suffix::none }));
- if(!m_Kernel32->has_value())
- {
- throw mpt::Wine::Exception("Could not load Wine kernel32.dll.");
- }
- if(!(*m_Kernel32)->bind(wine_get_unix_file_name, "wine_get_unix_file_name"))
- {
- throw mpt::Wine::Exception("Could not bind Wine kernel32.dll:wine_get_unix_file_name.");
- }
- if(!(*m_Kernel32)->bind(wine_get_dos_file_name, "wine_get_dos_file_name"))
- {
- throw mpt::Wine::Exception("Could not bind Wine kernel32.dll:wine_get_dos_file_name.");
- }
- {
- std::string out;
- std::string err;
- try
- {
- if(ExecutePosixShellCommand("uname -m", out, err) != 0)
- {
- throw mpt::Wine::Exception("Wine 'uname -m' failed.");
- }
- if(!err.empty())
- {
- throw mpt::Wine::Exception("Wine 'uname -m' failed.");
- }
- out = mpt::trim(out, std::string("\r\n"));
- m_Uname_m = out;
- } catch(const std::exception &)
- {
- m_Uname_m = std::string();
- }
- }
- try
- {
- m_HOME = GetPosixEnvVar("HOME");
- } catch(const std::exception &)
- {
- m_HOME = std::string();
- }
- try
- {
- m_XDG_DATA_HOME = GetPosixEnvVar("XDG_DATA_HOME");
- if(m_XDG_DATA_HOME.empty())
- {
- m_XDG_DATA_HOME = m_HOME + "/.local/share";
- }
- } catch(const std::exception &)
- {
- m_XDG_DATA_HOME = std::string();
- }
- try
- {
- m_XDG_CACHE_HOME = GetPosixEnvVar("XDG_CACHE_HOME");
- if(m_XDG_CACHE_HOME.empty())
- {
- m_XDG_CACHE_HOME = m_HOME + "/.cache";
- }
- } catch(const std::exception &)
- {
- m_XDG_CACHE_HOME = std::string();
- }
- try
- {
- m_XDG_CONFIG_HOME = GetPosixEnvVar("XDG_CONFIG_HOME");
- if(m_XDG_CONFIG_HOME.empty())
- {
- m_XDG_CONFIG_HOME = m_HOME + "/.config";
- }
- } catch(const std::exception &)
- {
- m_XDG_CONFIG_HOME = std::string();
- }
- }
- std::string Context::PathToPosix(mpt::PathString windowsPath)
- {
- std::string result;
- if(windowsPath.empty())
- {
- return result;
- }
- if(windowsPath.Length() >= 32000)
- {
- throw mpt::Wine::Exception("Path too long.");
- }
- LPSTR tmp = nullptr;
- tmp = wine_get_unix_file_name(windowsPath.ToWide().c_str());
- if(!tmp)
- {
- throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_unix_file_name failed.");
- }
- result = tmp;
- HeapFree(GetProcessHeap(), 0, tmp);
- tmp = nullptr;
- return result;
- }
- mpt::PathString Context::PathToWindows(std::string hostPath)
- {
- mpt::PathString result;
- if(hostPath.empty())
- {
- return result;
- }
- if(hostPath.length() >= 32000)
- {
- throw mpt::Wine::Exception("Path too long.");
- }
- LPWSTR tmp = nullptr;
- tmp = wine_get_dos_file_name(hostPath.c_str());
- if(!tmp)
- {
- throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_dos_file_name failed.");
- }
- result = mpt::PathString::FromWide(tmp);
- HeapFree(GetProcessHeap(), 0, tmp);
- tmp = nullptr;
- return result;
- }
- std::string Context::PathToPosixCanonical(mpt::PathString windowsPath)
- {
- std::string result;
- std::string hostPath = PathToPosix(windowsPath);
- if(hostPath.empty())
- {
- return result;
- }
- std::string output;
- std::string error;
- int exitcode = ExecutePosixShellCommand(std::string() + "readlink -f " + EscapePosixShell(hostPath), output, error);
- if(!error.empty())
- {
- throw mpt::Wine::Exception("Wine readlink failed: " + error);
- }
- if(exitcode != 0 && exitcode != 1)
- {
- throw mpt::Wine::Exception("Wine readlink failed.");
- }
- std::string trimmedOutput = mpt::trim(output, std::string("\r\n"));
- result = trimmedOutput;
- return result;
- }
- static void ExecutePosixCommandProgressDefault(void * /*userdata*/ )
- {
- ::Sleep(10);
- return;
- }
- static ExecuteProgressResult ExecutePosixShellScriptProgressDefault(void * /*userdata*/ )
- {
- ::Sleep(10);
- return ExecuteProgressContinueWaiting;
- }
- std::string Context::EscapePosixShell(std::string line)
- {
- const char escape_chars [] = { '|', '&', ';', '<', '>', '(', ')', '$', '`', '"', '\'', ' ', '\t' };
- const char maybe_escape_chars [] = { '*', '?', '[', '#', '~', '=', '%' };
- line = mpt::replace(line, std::string("\\"), std::string("\\\\"));
- for(char c : escape_chars)
- {
- line = mpt::replace(line, std::string(1, c), std::string("\\") + std::string(1, c));
- }
- for(char c : maybe_escape_chars)
- {
- line = mpt::replace(line, std::string(1, c), std::string("\\") + std::string(1, c));
- }
- return line;
- }
- ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet<ExecFlags> flags, std::map<std::string, std::vector<char> > filetree, std::string title, ExecutePosixCommandProgress progress, ExecutePosixShellScriptProgress progressCancel, void *userdata)
- {
- // Relevant documentation:
- // https://stackoverflow.com/questions/6004070/execute-shell-commands-from-program-running-in-wine
- // https://www.winehq.org/pipermail/wine-bugs/2014-January/374918.html
- // https://bugs.winehq.org/show_bug.cgi?id=34730
- if(!progress) progress = &ExecutePosixCommandProgressDefault;
- if(!progressCancel) progressCancel = &ExecutePosixShellScriptProgressDefault;
- if(flags[ExecFlagInteractive]) flags.reset(ExecFlagSilent);
- if(flags[ExecFlagSplitOutput]) flags.set(ExecFlagSilent);
- std::vector<mpt::PathString> tempfiles;
- progress(userdata);
- mpt::TempDirGuard dirWindowsTemp(mpt::CreateTempFileName());
- if(dirWindowsTemp.GetDirname().empty())
- {
- throw mpt::Wine::Exception("Creating temporary directoy failed.");
- }
- const std::string dirPosix = PathToPosix(dirWindowsTemp.GetDirname());
- if(dirPosix.empty())
- {
- throw mpt::Wine::Exception("mpt::Wine::ConvertWindowsPathToHost returned empty path.");
- }
- const std::string dirPosixEscape = EscapePosixShell(dirPosix);
- const mpt::PathString dirWindows = dirWindowsTemp.GetDirname();
- progress(userdata);
- // write the script to disk
- mpt::PathString scriptFilenameWindows = dirWindows + P_("script.sh");
- {
- mpt::ofstream tempfile(scriptFilenameWindows, std::ios::binary);
- tempfile << script;
- tempfile.flush();
- if(!tempfile)
- {
- throw mpt::Wine::Exception("Error writing script.sh.");
- }
- }
- const std::string scriptFilenamePosix = PathToPosix(scriptFilenameWindows);
- if(scriptFilenamePosix.empty())
- {
- throw mpt::Wine::Exception("Error converting script.sh path.");
- }
- const std::string scriptFilenamePosixEscape = EscapePosixShell(scriptFilenamePosix);
- progress(userdata);
- // create a wrapper that will call the script and gather result.
- mpt::PathString wrapperstarterFilenameWindows = dirWindows + P_("wrapperstarter.sh");
- {
- mpt::ofstream tempfile(wrapperstarterFilenameWindows, std::ios::binary);
- std::string wrapperstarterscript;
- wrapperstarterscript += std::string() + "#!/usr/bin/env sh" "\n";
- wrapperstarterscript += std::string() + "exec /usr/bin/env sh " + dirPosixEscape + "wrapper.sh" "\n";
- tempfile << wrapperstarterscript;
- tempfile.flush();
- if(!tempfile)
- {
- throw mpt::Wine::Exception("Error writing wrapper.sh.");
- }
- }
- mpt::PathString wrapperFilenameWindows = dirWindows + P_("wrapper.sh");
- std::string cleanupscript;
- {
- mpt::ofstream tempfile(wrapperFilenameWindows, std::ios::binary);
- std::string wrapperscript;
- if(!flags[ExecFlagSilent])
- {
- wrapperscript += "printf \"\\033]0;" + title + "\\a\"" "\n";
- }
- wrapperscript += "chmod u+x " + scriptFilenamePosixEscape + "\n";
- wrapperscript += "cd " + dirPosixEscape + "filetree" "\n";
- if(flags[ExecFlagInteractive])
- { // no stdout/stderr capturing for interactive scripts
- wrapperscript += scriptFilenamePosixEscape + "\n";
- wrapperscript += "MPT_RESULT=$?" "\n";
- wrapperscript += "echo ${MPT_RESULT} > " + dirPosixEscape + "exit" "\n";
- } else if(flags[ExecFlagSplitOutput])
- {
- wrapperscript += "(" + scriptFilenamePosixEscape + "; echo $? >&4) 4>" + dirPosixEscape + "exit 1>" + dirPosixEscape + "out 2>" + dirPosixEscape + "err" "\n";
- } else
- {
- wrapperscript += "(" + scriptFilenamePosixEscape + "; echo $? >&4) 2>&1 4>" + dirPosixEscape + "exit | tee " + dirPosixEscape + "out" "\n";
- }
- wrapperscript += "echo done > " + dirPosixEscape + "done" "\n";
- cleanupscript += "rm " + dirPosixEscape + "done" "\n";
- cleanupscript += "rm " + dirPosixEscape + "exit" "\n";
- if(flags[ExecFlagInteractive])
- {
- // nothing
- } else if(flags[ExecFlagSplitOutput])
- {
- cleanupscript += "rm " + dirPosixEscape + "out" "\n";
- cleanupscript += "rm " + dirPosixEscape + "err" "\n";
- } else
- {
- cleanupscript += "rm " + dirPosixEscape + "out" "\n";
- }
- cleanupscript += "rm -r " + dirPosixEscape + "filetree" "\n";
- cleanupscript += "rm " + dirPosixEscape + "script.sh" "\n";
- cleanupscript += "rm " + dirPosixEscape + "wrapper.sh" "\n";
- cleanupscript += "rm " + dirPosixEscape + "wrapperstarter.sh" "\n";
- cleanupscript += "rm " + dirPosixEscape + "terminal.sh" "\n";
- if(flags[ExecFlagAsync])
- {
- wrapperscript += cleanupscript;
- cleanupscript.clear();
- }
- tempfile << wrapperscript;
- tempfile.flush();
- if(!tempfile)
- {
- throw mpt::Wine::Exception("Error writing wrapper.sh.");
- }
- }
- progress(userdata);
- ::CreateDirectory((dirWindows + P_("filetree")).AsNative().c_str(), nullptr);
- for(const auto &file : filetree)
- {
- std::vector<mpt::ustring> path = mpt::String::Split<mpt::ustring>(mpt::ToUnicode(mpt::Charset::UTF8, file.first), U_("/"));
- mpt::PathString combinedPath = dirWindows + P_("filetree") + P_("\\");
- if(path.size() > 1)
- {
- for(std::size_t singlepath = 0; singlepath < path.size() - 1; ++singlepath)
- {
- if(path[singlepath].empty())
- {
- continue;
- }
- combinedPath += mpt::PathString::FromUnicode(path[singlepath]);
- if(!combinedPath.IsDirectory())
- {
- if(::CreateDirectory(combinedPath.AsNative().c_str(), nullptr) == 0)
- {
- throw mpt::Wine::Exception("Error writing filetree.");
- }
- }
- combinedPath += P_("\\");
- }
- }
- try
- {
- mpt::LazyFileRef out(dirWindows + P_("filetree") + P_("\\") + mpt::PathString::FromUTF8(mpt::replace(file.first, std::string("/"), std::string("\\"))));
- out = file.second;
- } catch(std::exception &)
- {
- throw mpt::Wine::Exception("Error writing filetree.");
- }
- }
- progress(userdata);
- // create a wrapper that will find a suitable terminal and run the wrapper script in the terminal window.
- mpt::PathString terminalWrapperFilenameWindows = dirWindows + P_("terminal.sh");
- {
- mpt::ofstream tempfile(terminalWrapperFilenameWindows, std::ios::binary);
- // NOTE:
- // Modern terminals detach themselves from the invoking shell if another instance is already present.
- // This means we cannot rely on terminal invocation being syncronous.
- static constexpr const char * terminals[] =
- {
- "x-terminal-emulator",
- "konsole",
- "mate-terminal",
- "xfce4-terminal",
- "gnome-terminal",
- "uxterm",
- "xterm",
- "rxvt",
- };
- std::string terminalscript = "\n";
- for(const std::string terminal : terminals)
- {
- // mate-terminal on Debian 8 cannot execute commands with arguments,
- // thus we use a separate script that requires no arguments to execute.
- terminalscript += "if command -v " + terminal + " 2>/dev/null 1>/dev/null ; then" "\n";
- terminalscript += " chmod u+x " + dirPosixEscape + "wrapperstarter.sh" "\n";
- terminalscript += " exec `command -v " + terminal + "` -e \"" + dirPosixEscape + "wrapperstarter.sh\"" "\n";
- terminalscript += "fi" "\n";
- }
- tempfile << terminalscript;
- tempfile.flush();
- if(!tempfile)
- {
- return ExecResult::Error();
- }
- }
- progress(userdata);
- // build unix command line
- std::string unixcommand;
- bool createProcessSuccess = false;
- if(!createProcessSuccess)
- {
- if(flags[ExecFlagSilent])
- {
- unixcommand = "/usr/bin/env sh \"" + dirPosixEscape + "wrapper.sh\"";
- } else
- {
- unixcommand = "/usr/bin/env sh \"" + dirPosixEscape + "terminal.sh\"";
- }
- progress(userdata);
- std::wstring unixcommandW = mpt::ToWide(mpt::Charset::UTF8, unixcommand);
- std::wstring titleW = mpt::ToWide(mpt::Charset::UTF8, title);
- STARTUPINFOW startupInfo = {};
- startupInfo.lpTitle = titleW.data();
- startupInfo.cb = sizeof(startupInfo);
- PROCESS_INFORMATION processInformation = {};
- progress(userdata);
- BOOL success = FALSE;
- if(flags[ExecFlagSilent])
- {
- success = CreateProcessW(NULL, unixcommandW.data(), NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &startupInfo, &processInformation);
- } else
- {
- success = CreateProcessW(NULL, unixcommandW.data(), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInformation);
- }
- progress(userdata);
- createProcessSuccess = (success != FALSE);
- progress(userdata);
- if(success)
- {
- if(!flags[ExecFlagAsync])
- {
- // note: execution is not syncronous with all Wine versions,
- // we additionally explicitly wait for "done" later
- while(WaitForSingleObject(processInformation.hProcess, 0) == WAIT_TIMEOUT)
- { // wait
- if(progressCancel(userdata) != ExecuteProgressContinueWaiting)
- {
- CloseHandle(processInformation.hThread);
- CloseHandle(processInformation.hProcess);
- throw mpt::Wine::Exception("Canceled.");
- }
- }
- }
- progress(userdata);
- CloseHandle(processInformation.hThread);
- CloseHandle(processInformation.hProcess);
- }
- }
- progress(userdata);
- // Work around Wine being unable to execute PIE binaries on Debian 9.
- // Luckily, /bin/bash is still non-PIE on Debian 9.
- if(!createProcessSuccess)
- {
- if(flags[ExecFlagSilent])
- {
- unixcommand = "/bin/bash \"" + dirPosixEscape + "wrapper.sh\"";
- } else
- {
- unixcommand = "/bin/bash \"" + dirPosixEscape + "terminal.sh\"";
- }
- progress(userdata);
- std::wstring unixcommandW = mpt::ToWide(mpt::Charset::UTF8, unixcommand);
- std::wstring titleW = mpt::ToWide(mpt::Charset::UTF8, title);
- STARTUPINFOW startupInfo = {};
- startupInfo.lpTitle = titleW.data();
- startupInfo.cb = sizeof(startupInfo);
- PROCESS_INFORMATION processInformation = {};
- progress(userdata);
- BOOL success = FALSE;
- if(flags[ExecFlagSilent])
- {
- success = CreateProcessW(NULL, unixcommandW.data(), NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &startupInfo, &processInformation);
- } else
- {
- success = CreateProcessW(NULL, unixcommandW.data(), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInformation);
- }
- progress(userdata);
- createProcessSuccess = (success != FALSE);
- progress(userdata);
- if(success)
- {
- if(!flags[ExecFlagAsync])
- {
- // note: execution is not syncronous with all Wine versions,
- // we additionally explicitly wait for "done" later
- while(WaitForSingleObject(processInformation.hProcess, 0) == WAIT_TIMEOUT)
- { // wait
- if(progressCancel(userdata) != ExecuteProgressContinueWaiting)
- {
- CloseHandle(processInformation.hThread);
- CloseHandle(processInformation.hProcess);
- throw mpt::Wine::Exception("Canceled.");
- }
- }
- }
- progress(userdata);
- CloseHandle(processInformation.hThread);
- CloseHandle(processInformation.hProcess);
- }
- }
- progress(userdata);
- if(!createProcessSuccess)
- {
- throw mpt::Wine::Exception("CreateProcess failed.");
- }
- progress(userdata);
- if(flags[ExecFlagAsync])
- {
- ExecResult result;
- result.exitcode = 0;
- return result;
- }
- while(!(dirWindows + P_("done")).IsFile())
- { // wait
- if(progressCancel(userdata) != ExecuteProgressContinueWaiting)
- {
- throw mpt::Wine::Exception("Canceled.");
- }
- }
- progress(userdata);
- int exitCode = 0;
- {
- mpt::ifstream exitFile(dirWindows + P_("exit"), std::ios::binary);
- if(!exitFile)
- {
- throw mpt::Wine::Exception("Script .exit file not found.");
- }
- std::string exitString;
- exitFile >> exitString;
- if(exitString.empty())
- {
- throw mpt::Wine::Exception("Script .exit file empty.");
- }
- exitCode = ConvertStrTo<int>(exitString);
- }
- progress(userdata);
- std::string outputString;
- if(!flags[ExecFlagInteractive])
- {
- mpt::ifstream outputFile(dirWindows + P_("out"), std::ios::binary);
- if(outputFile)
- {
- outputFile.seekg(0, std::ios::end);
- std::streampos outputFileSize = outputFile.tellg();
- outputFile.seekg(0, std::ios::beg);
- std::vector<char> outputFileBuf(mpt::saturate_cast<std::size_t>(static_cast<std::streamoff>(outputFileSize)));
- outputFile.read(&outputFileBuf[0], outputFileBuf.size());
- outputString = mpt::buffer_cast<std::string>(outputFileBuf);
- }
- }
- progress(userdata);
- std::string errorString;
- if(flags[ExecFlagSplitOutput])
- {
- mpt::ifstream errorFile(dirWindows + P_("err"), std::ios::binary);
- if(errorFile)
- {
- errorFile.seekg(0, std::ios::end);
- std::streampos errorFileSize = errorFile.tellg();
- errorFile.seekg(0, std::ios::beg);
- std::vector<char> errorFileBuf(mpt::saturate_cast<std::size_t>(static_cast<std::streamoff>(errorFileSize)));
- errorFile.read(&errorFileBuf[0], errorFileBuf.size());
- errorString = mpt::buffer_cast<std::string>(errorFileBuf);
- }
- }
- progress(userdata);
- ExecResult result;
- result.exitcode = exitCode;
- result.output = outputString;
- result.error = errorString;
- std::deque<mpt::PathString> paths;
- paths.push_back(dirWindows + P_("filetree"));
- mpt::PathString basePath = (dirWindows + P_("filetree")).EnsureTrailingSlash();
- while(!paths.empty())
- {
- mpt::PathString path = paths.front();
- paths.pop_front();
- path.EnsureTrailingSlash();
- HANDLE hFind = NULL;
- WIN32_FIND_DATA wfd = {};
- hFind = FindFirstFile((path + P_("*.*")).AsNative().c_str(), &wfd);
- if(hFind != NULL && hFind != INVALID_HANDLE_VALUE)
- {
- do
- {
- mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName);
- if(filename != P_(".") && filename != P_(".."))
- {
- filename = path + filename;
- filetree[filename.ToUTF8()] = std::vector<char>();
- if(filename.IsDirectory())
- {
- paths.push_back(filename);
- } else if(filename.IsFile())
- {
- try
- {
- mpt::LazyFileRef f(filename);
- std::vector<char> buf = f;
- mpt::PathString treeFilename = mpt::PathString::FromNative(filename.AsNative().substr(basePath.AsNative().length()));
- result.filetree[treeFilename.ToUTF8()] = buf;
- } catch (std::exception &)
- {
- // nothing?!
- }
- }
- }
- } while(FindNextFile(hFind, &wfd));
- FindClose(hFind);
- }
- }
- mpt::DeleteWholeDirectoryTree(dirWindows);
- return result;
- }
- int Context::ExecutePosixShellCommand(std::string command, std::string & output, std::string & error)
- {
- std::string script;
- script += "#!/usr/bin/env sh" "\n";
- script += "exec " + command + "\n";
- mpt::Wine::ExecResult execResult = ExecutePosixShellScript
- ( script
- , mpt::Wine::ExecFlagSilent | mpt::Wine::ExecFlagSplitOutput, std::map<std::string, std::vector<char> >()
- , std::string()
- , nullptr
- , nullptr
- , nullptr
- );
- output = execResult.output;
- error = execResult.error;
- return execResult.exitcode;
- }
- std::string Context::GetPosixEnvVar(std::string var, std::string def)
- {
- // We cannot use std::getenv here because Wine overrides SOME env vars,
- // in particular, HOME is unset in the Wine environment.
- // Instead, we just spawn a shell that will catch up a sane environment on
- // its own.
- std::string output;
- std::string error;
- int exitcode = ExecutePosixShellCommand(std::string() + "echo $" + var, output, error);
- if(!error.empty())
- {
- throw mpt::Wine::Exception("Wine echo $var failed: " + error);
- }
- if(exitcode != 0)
- {
- throw mpt::Wine::Exception("Wine echo $var failed.");
- }
- std::string result = mpt::trim_right(output, std::string("\r\n"));
- if(result.empty())
- {
- result = def;
- }
- return result;
- }
- } // namespace Wine
- } // namespace mpt
- #else // !MPT_OS_WINDOWS
- MPT_MSVC_WORKAROUND_LNK4221(mptWine)
- #endif // MPT_OS_WINDOWS
- OPENMPT_NAMESPACE_END
|