#include "support/os.h"
#include "support/Timeout.h"
-#include <boost/bind.hpp>
+#include "support/bind.h"
#include <cerrno>
+#include <cstring>
+#include <list>
#include <queue>
#include <sstream>
#include <utility>
# define SIGKILL 9
# include <windows.h>
# include <process.h>
+# undef max
#else
# include <csignal>
# include <cstdlib>
# include <unistd.h>
# endif
# include <sys/wait.h>
-# ifndef CXX_GLOBAL_CSTD
- using std::signal;
- using std::strerror;
-# endif
#endif
-using boost::bind;
-
-using std::endl;
-using std::equal_to;
-using std::find_if;
-using std::string;
-using std::vector;
+using namespace std;
namespace lyx {
//
/////////////////////////////////////////////////////////////////////
-class Murder : public boost::signals::trackable {
+class Murder {
public:
//
static void killItDead(int secs, pid_t pid)
{
- if (secs > 0) {
+ if (secs > 0)
new Murder(secs, pid);
- } else if (pid != 0) {
+ else if (pid != 0)
support::kill(pid, SIGKILL);
- }
}
//
{
if (pid_ != 0)
support::kill(pid_, SIGKILL);
- lyxerr << "Killed " << pid_ << std::endl;
+ lyxerr << "Killed " << pid_ << endl;
delete this;
}
private:
//
Murder(int secs, pid_t pid)
- : timeout_(0), pid_(pid)
+ : timeout_(1000*secs, Timeout::ONETIME), pid_(pid)
{
- timeout_ = new Timeout(1000*secs, Timeout::ONETIME);
- timeout_->timeout.connect(boost::bind(&Murder::kill, this));
- timeout_->start();
+ // Connection is closed with this.
+ timeout_.timeout.connect([this](){ kill(); });
+ timeout_.start();
}
//
- ~Murder()
- {
- delete timeout_;
- }
- //
- Timeout * timeout_;
+ Timeout timeout_;
//
pid_t pid_;
};
-} // namespace anon
+} // namespace
/////////////////////////////////////////////////////////////////////
{}
+bool ForkedProcess::IAmAChild = false;
+
+
void ForkedProcess::emitSignal()
{
- if (signal_.get()) {
+ if (signal_) {
signal_->operator()(pid_, retval_);
}
}
// Spawn the child process
int ForkedProcess::run(Starttype type)
{
- retval_ = 0;
+ retval_ = 0;
pid_ = generateChild();
if (pid_ <= 0) { // child or fork failed.
retval_ = 1;
+ if (pid_ == 0)
+ //we also do this in fork(), too, but maybe someone will try
+ //to bypass that
+ IAmAChild = true;
return retval_;
}
bool ForkedProcess::running() const
{
- if (!pid())
+ if (pid() <= 0)
return false;
#if !defined (_WIN32)
void ForkedProcess::kill(int tol)
{
lyxerr << "ForkedProcess::kill(" << tol << ')' << endl;
- if (pid() == 0) {
+ if (pid() <= 0) {
lyxerr << "Can't kill non-existent process!" << endl;
return;
}
- // The weird (std::max)(a,b) signature prevents expansion
- // of an evil MSVC macro.
- int const tolerance = (std::max)(0, tol);
+ int const tolerance = max(0, tol);
if (tolerance == 0) {
// Kill it dead NOW!
Murder::killItDead(0, pid());
-
} else {
int ret = support::kill(pid(), SIGHUP);
// The process is already dead if wait_for_death is false
bool const wait_for_death = (ret == 0 && errno != ESRCH);
- if (wait_for_death) {
+ if (wait_for_death)
Murder::killItDead(tolerance, pid());
- }
}
}
+pid_t ForkedProcess::fork() {
+#if !defined (HAVE_FORK)
+ return -1;
+#else
+ pid_t pid = ::fork();
+ if (pid == 0)
+ IAmAChild = true;
+ return pid;
+#endif
+}
+
+
// Wait for child process to finish. Returns returncode from child.
int ForkedProcess::waitForChild()
{
DWORD exit_code = 0;
if (!GetExitCodeProcess(hProcess, &exit_code)) {
lyxerr << "GetExitCodeProcess failed waiting for child\n"
- << getChildErrorMessage() << std::endl;
+ << getChildErrorMessage() << endl;
} else
retval_ = exit_code;
break;
}
case WAIT_FAILED:
lyxerr << "WaitForSingleObject failed waiting for child\n"
- << getChildErrorMessage() << std::endl;
+ << getChildErrorMessage() << endl;
break;
}
//
/////////////////////////////////////////////////////////////////////
+ForkedCall::ForkedCall(string const & path, string const & lpath)
+ : cmd_prefix_(to_filesystem8bit(from_utf8(latexEnvCmdPrefix(path, lpath))))
+{}
+
int ForkedCall::startScript(Starttype wait, string const & what)
{
if (wait != Wait) {
- retval_ = startScript(what, SignalTypePtr());
+ retval_ = startScript(what, sigPtr());
return retval_;
}
- command_ = what;
+ command_ = commandPrep(trim(what));
signal_.reset();
return run(Wait);
}
-int ForkedCall::startScript(string const & what, SignalTypePtr signal)
+int ForkedCall::startScript(string const & what, sigPtr signal)
{
- command_ = what;
+ command_ = commandPrep(trim(what));
signal_ = signal;
return run(DontWait);
// generate child in background
int ForkedCall::generateChild()
{
- string line = trim(command_);
- if (line.empty())
+ if (command_.empty())
return 1;
+ string const prefixed_command = cmd_prefix_ + command_;
+
+#if !defined (_WIN32)
+ // POSIX
+
// Split the input command up into an array of words stored
// in a contiguous block of memory. The array contains pointers
// to each word.
// Don't forget the terminating `\0' character.
- char const * const c_str = line.c_str();
- vector<char> vec(c_str, c_str + line.size() + 1);
+ char const * const c_str = prefixed_command.c_str();
+ vector<char> vec(c_str, c_str + prefixed_command.size() + 1);
// Splitting the command up into an array of words means replacing
// the whitespace between words with '\0'. Life is complicated
// 2. If we are inside quotes, then don't replace the white space
// but do remove the quotes themselves. We do this naively by
// replacing the quote with '\0' which is fine if quotes
- // delimit the entire word.
+ // delimit the entire word. However, if quotes do not delimit the
+ // entire word (i.e., open quote is inside word), simply discard
+ // them such as not to break the current word.
char inside_quote = 0;
+ char c_before_open_quote = ' ';
vector<char>::iterator it = vec.begin();
+ vector<char>::iterator itc = vec.begin();
vector<char>::iterator const end = vec.end();
- for (; it != end; ++it) {
+ for (; it != end; ++it, ++itc) {
char const c = *it;
if (!inside_quote) {
- if (c == ' ')
- *it = '\0';
- else if (c == '\'' || c == '"') {
-#if defined (_WIN32)
- // How perverse!
- // spawnvp *requires* the quotes or it will
- // split the arg at the internal whitespace!
- // Make shure the quote is a DOS-style one.
- *it = '"';
-#else
- *it = '\0';
-#endif
+ if (c == '\'' || c == '"') {
+ if (c_before_open_quote == ' ')
+ *itc = '\0';
+ else
+ --itc;
inside_quote = c;
+ } else {
+ if (c == ' ')
+ *itc = '\0';
+ else
+ *itc = c;
+ c_before_open_quote = c;
}
} else if (c == inside_quote) {
-#if defined (_WIN32)
- *it = '"';
-#else
- *it = '\0';
-#endif
+ if (c_before_open_quote == ' ')
+ *itc = '\0';
+ else
+ --itc;
inside_quote = 0;
- }
+ } else
+ *itc = c;
}
+ // Clear what remains.
+ for (; itc != end; ++itc)
+ *itc = '\0';
+
// Build an array of pointers to each word.
it = vec.begin();
vector<char *> argv;
argv.push_back(&*it);
prev = *it;
}
- argv.push_back(0);
+ argv.push_back(nullptr);
// Debug output.
if (lyxerr.debugging(Debug::FILES)) {
vector<char *>::iterator ait = argv.begin();
vector<char *>::iterator const aend = argv.end();
- lyxerr << "<command>\n\t" << line
- << "\n\tInterpretted as:\n\n";
+ lyxerr << "<command>\n\t" << prefixed_command
+ << "\n\tInterpreted as:\n\n";
for (; ait != aend; ++ait)
if (*ait)
lyxerr << '\t'<< *ait << '\n';
- lyxerr << "</command>" << std::endl;
+ lyxerr << "</command>" << endl;
}
-#ifdef _WIN32
- pid_t const cpid = spawnvp(_P_NOWAIT, argv[0], &*argv.begin());
-#else // POSIX
pid_t const cpid = ::fork();
if (cpid == 0) {
// Child
<< strerror(errno) << endl;
_exit(1);
}
+#else
+ // Windows
+
+ pid_t cpid = -1;
+
+ STARTUPINFO startup;
+ PROCESS_INFORMATION process;
+
+ memset(&startup, 0, sizeof(STARTUPINFO));
+ memset(&process, 0, sizeof(PROCESS_INFORMATION));
+
+ startup.cb = sizeof(STARTUPINFO);
+
+ if (CreateProcess(0, (LPSTR)command_.c_str(), 0, 0, FALSE,
+ CREATE_NO_WINDOW, 0, 0, &startup, &process)) {
+ CloseHandle(process.hThread);
+ cpid = (pid_t)process.hProcess;
+ }
#endif
if (cpid < 0) {
namespace ForkedCallQueue {
/// A process in the queue
-typedef std::pair<std::string, ForkedCall::SignalTypePtr> Process;
-/** Add a process to the queue. Processes are forked sequentially
- * only one is running at a time.
- * Connect to the returned signal and you'll be informed when
- * the process has ended.
- */
-ForkedCall::SignalTypePtr add(std::string const & process);
+typedef pair<string, ForkedCall::sigPtr> Process;
/// in-progress queue
-static std::queue<Process> callQueue_;
+static queue<Process> callQueue_;
/// flag whether queue is running
-static bool running_ = 0;
+static bool running_ = false;
///
void startCaller();
///
void callback(pid_t, int);
-ForkedCall::SignalTypePtr add(string const & process)
+/** Add a process to the queue. Processes are forked sequentially
+ * only one is running at a time.
+ * Connect to the returned signal and you'll be informed when
+ * the process has ended.
+ */
+ForkedCall::sigPtr add(string const & process)
{
- ForkedCall::SignalTypePtr ptr;
- ptr.reset(new ForkedCall::SignalType);
+ ForkedCall::sigPtr ptr;
+ ptr.reset(new ForkedCall::sig);
callQueue_.push(Process(process, ptr));
if (!running_)
startCaller();
Process pro = callQueue_.front();
callQueue_.pop();
// Bind our chain caller
- pro.second->connect(boost::bind(&ForkedCallQueue::callback, _1, _2));
+ pro.second->connect(callback);
ForkedCall call;
- // If we fail to fork the process, then emit the signal
- // to tell the outside world that it failed.
+ //If we fail to fork the process, then emit the signal
+ //to tell the outside world that it failed.
if (call.startScript(pro.first, pro.second) > 0)
pro.second->operator()(0,1);
}
return running_;
}
-} // namespace ForkedCallsQueue
-
+} // namespace ForkedCallQueue
/////////////////////////////////////////////////////////////////////
(LPTSTR) &t_message, 0, 0
) != 0;
- std::ostringstream ss;
+ ostringstream ss;
ss << "LyX: Error waiting for child: " << error_code;
if (ok) {
namespace ForkedCallsController {
-typedef boost::shared_ptr<ForkedProcess> ForkedProcessPtr;
-typedef std::list<ForkedProcessPtr> ListType;
+typedef shared_ptr<ForkedProcess> ForkedProcessPtr;
+typedef list<ForkedProcessPtr> ListType;
typedef ListType::iterator iterator;
iterator find_pid(pid_t pid)
{
return find_if(forkedCalls.begin(), forkedCalls.end(),
- bind(equal_to<pid_t>(),
- bind(&ForkedCall::pid, _1),
+ lyx::bind(equal_to<pid_t>(),
+ lyx::bind(&ForkedCall::pid, _1),
pid));
}
DWORD exit_code = 0;
if (!GetExitCodeProcess(hProcess, &exit_code)) {
lyxerr << "GetExitCodeProcess failed waiting for child\n"
- << getChildErrorMessage() << std::endl;
+ << getChildErrorMessage() << endl;
// Child died, so pretend it returned 1
actCall->setRetValue(1);
} else {
actCall->setRetValue(exit_code);
}
+ CloseHandle(hProcess);
remove_it = true;
break;
}
case WAIT_FAILED:
lyxerr << "WaitForSingleObject failed waiting for child\n"
- << getChildErrorMessage() << std::endl;
+ << getChildErrorMessage() << endl;
actCall->setRetValue(1);
+ CloseHandle(hProcess);
remove_it = true;
break;
}
forkedCalls.erase(it);
actCall->emitSignal();
- /* start all over: emiting the signal can result
+ /* start all over: emitting the signal can result
* in changing the list (Ab)
*/
it = forkedCalls.begin();