return;
}
- /* FIXME */
- extern void reapSpellchecker(void);
- reapSpellchecker();
-
if (!bv_->theLockingInset()) {
screen().cursorToggle(bv_);
} else {
+2002-10-25 Angus Leeming <leeming@lyx.org>
+
+ * BufferView_pimpl.C (cursorToggle): remove reapSpellchecker hack.
+
+ * ispell.[Ch] (setError): new method.
+ * ispell.C (c-tor): move out child process into new class LaunchIspell.
+ Use setError() insetead of goto END.
+
+ * lyx_cb.C (AutoSave): move out child process into new class
+ AutoSaveBuffer.
+
2002-10-30 John Levon <levon@movementarian.org>
* text3.C: make start appendix undoable
+2002-10-25 Angus Leeming <leeming@lyx.org>
+
+ * GraphicsConverter.C (Impl::converted)
+ * PreviewLoader.C (Impl::finishedGenerating): no longer receives a
+ string as first arg, reflecting change in ForkedCall interface.
+
+ * PreviewLoader.C: use pid rather than command as identifier in
+ InProgress map.
+
2002-10-18 Angus Leeming <leeming@lyx.org>
* GraphicsCacheItem.C (findTargetFormat): add debug message.
* Cleans-up the temporary files, emits the finishedConversion
* signal and removes the Converter from the list of all processes.
*/
- void converted(string const & cmd, pid_t pid, int retval);
+ void converted(pid_t pid, int retval);
/** At the end of the conversion process inform the outside world
* by emitting a signal.
void Converter::Impl::startConversion()
{
if (!valid_process_) {
- converted(string(), 0, 1);
+ converted(0, 1);
return;
}
convert_ptr.reset(new Forkedcall::SignalType);
convert_ptr->connect(
- boost::bind(&Impl::converted, this, _1, _2, _3));
+ boost::bind(&Impl::converted, this, _1, _2));
Forkedcall call;
int retval = call.startscript(script_command_, convert_ptr);
if (retval > 0) {
// Unable to even start the script, so clean-up the mess!
- converted(string(), 0, 1);
+ converted(0, 1);
}
}
-void Converter::Impl::converted(string const & /* cmd */,
- pid_t /* pid */, int retval)
+void Converter::Impl::converted(pid_t /* pid */, int retval)
{
if (finished_)
// We're done already!
///
pid_t pid;
///
+ string command;
+ ///
string metrics_file;
///
BitmapFile snippets;
};
-typedef map<string, InProgress> InProgressProcesses;
+typedef map<pid_t, InProgress> InProgressProcesses;
typedef InProgressProcesses::value_type InProgressProcess;
private:
/// Called by the Forkedcall process that generated the bitmap files.
- void finishedGenerating(string const &, pid_t, int);
+ void finishedGenerating(pid_t, int);
///
void dumpPreamble(ostream &) const;
///
// Initiate the conversion from LaTeX to bitmap images files.
Forkedcall::SignalTypePtr convert_ptr(new Forkedcall::SignalType);
convert_ptr->connect(
- boost::bind(&Impl::finishedGenerating, this, _1, _2, _3));
+ boost::bind(&Impl::finishedGenerating, this, _1, _2));
Forkedcall call;
int ret = call.startscript(command, convert_ptr);
// Store the generation process in a list of all such processes
inprogress.pid = call.pid();
- in_progress_[command] = inprogress;
+ inprogress.command = command;
+ in_progress_[inprogress.pid] = inprogress;
}
-void PreviewLoader::Impl::finishedGenerating(string const & command,
- pid_t /* pid */, int retval)
+void PreviewLoader::Impl::finishedGenerating(pid_t pid, int retval)
{
+ // Paranoia check!
+ InProgressProcesses::iterator git = in_progress_.find(pid);
+ if (git == in_progress_.end()) {
+ lyxerr << "PreviewLoader::finishedGenerating(): unable to find "
+ "data for PID " << pid << endl;
+ return;
+ }
+
+ string const command = git->second.command;
string const status = retval > 0 ? "failed" : "succeeded";
lyxerr[Debug::GRAPHICS] << "PreviewLoader::finishedInProgress("
<< retval << "): processing " << status
if (retval > 0)
return;
- // Paranoia check!
- InProgressProcesses::iterator git = in_progress_.find(command);
- if (git == in_progress_.end()) {
- lyxerr << "PreviewLoader::finishedGenerating(): unable to find "
- "data for\n"
- << command << "!" << endl;
- return;
- }
-
// Read the metrics file, if it exists
vector<double> ascent_fractions(git->second.snippets.size());
setAscentFractions(ascent_fractions, git->second.metrics_file);
#endif
#include "LString.h"
-#include "support/lstrings.h"
#include "lyxrc.h"
#include "language.h"
#include "debug.h"
#include "ispell.h"
#include "WordLangTuple.h"
+#include "support/forkedcall.h"
+#include "support/lstrings.h"
+
#ifndef CXX_GLOBAL_CSTD
using std::strcpy;
using std::strlen;
using std::endl;
namespace {
- /// pid for the `ispell' process.
- pid_t isp_pid = -1;
+
+/// pid for the `ispell' process.
+pid_t isp_pid = -1;
+
+class LaunchIspell : public ForkedProcess {
+public:
+ ///
+ LaunchIspell(BufferParams const & p, string const & l,
+ int * in, int * out)
+ : params(p), lang(l), pipein(in), pipeout(out) {}
+ ///
+ virtual ForkedProcess * clone() const {
+ return new LaunchIspell(*this);
+ }
+ ///
+ int start();
+private:
+ ///
+ virtual int generateChild();
+
+ ///
+ BufferParams const & params;
+ string const & lang;
+ int * const pipein;
+ int * const pipeout;
+};
+
+
+int LaunchIspell::start()
+{
+ command_ = "ispell";
+ return runNonBlocking();
+}
+
+
+int LaunchIspell::generateChild()
+{
+ isp_pid = fork();
+
+ if (isp_pid != 0) {
+ // failed (-1) or parent process (>0)
+ return isp_pid;
+ }
+
+ // child process
+ dup2(pipein[0], STDIN_FILENO);
+ dup2(pipeout[1], STDOUT_FILENO);
+ ::close(pipein[0]);
+ ::close(pipein[1]);
+ ::close(pipeout[0]);
+ ::close(pipeout[1]);
+
+ char * argv[14];
+ int argc = 0;
+
+ char * tmp = new char[lyxrc.isp_command.length() + 1];
+ lyxrc.isp_command.copy(tmp, lyxrc.isp_command.length());
+ tmp[lyxrc.isp_command.length()] = '\0';
+ argv[argc++] = tmp;
+ tmp = new char[3];
+ string("-a").copy(tmp, 2); tmp[2] = '\0'; // pipe mode
+ argv[argc++] = tmp;
+
+ if (lang != "default") {
+ tmp = new char[3];
+ string("-d").copy(tmp, 2); tmp[2] = '\0'; // Dictionary file
+ argv[argc++] = tmp;
+ tmp = new char[lang.length() + 1];
+ lang.copy(tmp, lang.length()); tmp[lang.length()] = '\0';
+ argv[argc++] = tmp;
+ }
+
+ if (lyxrc.isp_accept_compound) {
+ // Consider run-together words as legal compounds
+ tmp = new char[3];
+ string("-C").copy(tmp, 2); tmp[2] = '\0';
+ argv[argc++] = tmp;
+ } else {
+ // Report run-together words with
+ // missing blanks as errors
+ tmp = new char[3];
+ string("-B").copy(tmp, 2); tmp[2] = '\0';
+ argv[argc++] = tmp;
+ }
+ if (lyxrc.isp_use_esc_chars) {
+ // Specify additional characters that
+ // can be part of a word
+ tmp = new char[3];
+ string("-w").copy(tmp, 2); tmp[2] = '\0';
+ argv[argc++] = tmp;
+ // Put the escape chars in ""s
+ string tms = "\"" + lyxrc.isp_esc_chars + "\"";
+ tmp = new char[tms.length() + 1];
+ tms.copy(tmp, tms.length()); tmp[tms.length()] = '\0';
+ argv[argc++] = tmp;
+ }
+ if (lyxrc.isp_use_pers_dict) {
+ // Specify an alternate personal dictionary
+ tmp = new char[3];
+ string("-p").copy(tmp, 2);
+ tmp[2]= '\0';
+ argv[argc++] = tmp;
+ tmp = new char[lyxrc.isp_pers_dict.length() + 1];
+ lyxrc.isp_pers_dict.copy(tmp, lyxrc.isp_pers_dict.length());
+ tmp[lyxrc.isp_pers_dict.length()] = '\0';
+ argv[argc++] = tmp;
+ }
+ if (lyxrc.isp_use_input_encoding &&
+ params.inputenc != "default") {
+ string enc = (params.inputenc == "auto")
+ ? params.language->encoding()->LatexName()
+ : params.inputenc;
+ string::size_type n = enc.length();
+ tmp = new char[3];
+ string("-T").copy(tmp, 2);
+ tmp[2] = '\0';
+ argv[argc++] = tmp; // Input encoding
+ tmp = new char[n + 1];
+ enc.copy(tmp, n);
+ tmp[n] = '\0';
+ argv[argc++] = tmp;
+ }
+
+ argv[argc++] = 0;
+
+ execvp(argv[0], const_cast<char * const *>(argv));
+
+ // free the memory used by string::copy in the
+ // setup of argv
+ for (int i = 0; i < argc - 1; ++i)
+ delete[] argv[i];
+
+ lyxerr << "LyX: Failed to start ispell!" << endl;
+ _exit(0);
}
+} // namespace anon
+
+
ISpell::ISpell(BufferParams const & params, string const & lang)
: str(0)
{
static char o_buf[BUFSIZ]; // jc: it could be smaller
int pipein[2];
int pipeout[2];
- char * argv[14];
- int argc;
isp_pid = -1;
if (pipe(pipein) == -1 || pipe(pipeout) == -1) {
lyxerr << "LyX: Can't create pipe for spellchecker!" << endl;
- goto END;
+ setError();
+ return;
}
if ((out = fdopen(pipein[1], "w")) == 0) {
lyxerr << "LyX: Can't create stream for pipe for spellchecker!"
<< endl;
- goto END;
+ setError();
+ return;
}
if ((in = fdopen(pipeout[0], "r")) == 0) {
lyxerr <<"LyX: Can't create stream for pipe for spellchecker!"
<< endl;
- goto END;
+ setError();
+ return;
}
setvbuf(out, o_buf, _IOLBF, BUFSIZ);
isp_fd = pipeout[0];
- isp_pid = fork();
-
+ LaunchIspell childprocess(params, lang, pipein, pipeout);
+ isp_pid = childprocess.start();
if (isp_pid == -1) {
lyxerr << "LyX: Can't create child process for spellchecker!"
<< endl;
- goto END;
+ setError();
+ return;
}
- if (isp_pid == 0) {
- /* child process */
- dup2(pipein[0], STDIN_FILENO);
- dup2(pipeout[1], STDOUT_FILENO);
- ::close(pipein[0]);
- ::close(pipein[1]);
+ setError();
+ /* Parent process: Read ispells identification message */
+ // Hmm...what are we using this id msg for? Nothing? (Lgb)
+ // Actually I used it to tell if it's truly Ispell or if it's
+ // aspell -- (kevinatk@home.com)
+ // But no code actually used the results for anything useful
+ // so I removed it again. Perhaps we can remove this code too.
+ // - jbl
+ char buf[2048];
+ fd_set infds;
+ struct timeval tv;
+ int retval = 0;
+ FD_ZERO(&infds);
+ FD_SET(pipeout[0], &infds);
+ tv.tv_sec = 15; // fifteen second timeout. Probably too much,
+ // but it can't really hurt.
+ tv.tv_usec = 0;
+
+ // Configure provides us with macros which are supposed to do
+ // the right typecast.
+ retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1),
+ SELECT_TYPE_ARG234 (&infds),
+ 0,
+ 0,
+ SELECT_TYPE_ARG5 (&tv));
+
+ if (retval > 0) {
+ // Ok, do the reading. We don't have to FD_ISSET since
+ // there is only one fd in infds.
+ fgets(buf, 2048, in);
+
+ fputs("!\n", out); // Set terse mode (silently accept correct words)
+
+ } else if (retval == 0) {
+ // timeout. Give nice message to user.
+ lyxerr << "Ispell read timed out, what now?" << endl;
+ // This probably works but could need some thought
+ isp_pid = -1;
::close(pipeout[0]);
::close(pipeout[1]);
+ ::close(pipein[0]);
+ ::close(pipein[1]);
+ isp_fd = -1;
+ } else {
+ // Select returned error
+ lyxerr << "Select on ispell returned error, what now?" << endl;
+ }
+}
- argc = 0;
- char * tmp = new char[lyxrc.isp_command.length() + 1];
- lyxrc.isp_command.copy(tmp, lyxrc.isp_command.length());
- tmp[lyxrc.isp_command.length()] = '\0';
- argv[argc++] = tmp;
- tmp = new char[3];
- string("-a").copy(tmp, 2); tmp[2] = '\0'; // pipe mode
- argv[argc++] = tmp;
- if (lang != "default") {
- tmp = new char[3];
- string("-d").copy(tmp, 2); tmp[2] = '\0'; // Dictionary file
- argv[argc++] = tmp;
- tmp = new char[lang.length() + 1];
- lang.copy(tmp, lang.length()); tmp[lang.length()] = '\0';
- argv[argc++] = tmp;
- }
-
- if (lyxrc.isp_accept_compound) {
- // Consider run-together words as legal compounds
- tmp = new char[3];
- string("-C").copy(tmp, 2); tmp[2] = '\0';
- argv[argc++] = tmp;
- } else {
- // Report run-together words with
- // missing blanks as errors
- tmp = new char[3];
- string("-B").copy(tmp, 2); tmp[2] = '\0';
- argv[argc++] = tmp;
- }
- if (lyxrc.isp_use_esc_chars) {
- // Specify additional characters that
- // can be part of a word
- tmp = new char[3];
- string("-w").copy(tmp, 2); tmp[2] = '\0';
- argv[argc++] = tmp;
- // Put the escape chars in ""s
- string tms = "\"" + lyxrc.isp_esc_chars + "\"";
- tmp = new char[tms.length() + 1];
- tms.copy(tmp, tms.length()); tmp[tms.length()] = '\0';
- argv[argc++] = tmp;
- }
- if (lyxrc.isp_use_pers_dict) {
- // Specify an alternate personal dictionary
- tmp = new char[3];
- string("-p").copy(tmp, 2);
- tmp[2]= '\0';
- argv[argc++] = tmp;
- tmp = new char[lyxrc.isp_pers_dict.length() + 1];
- lyxrc.isp_pers_dict.copy(tmp, lyxrc.isp_pers_dict.length());
- tmp[lyxrc.isp_pers_dict.length()] = '\0';
- argv[argc++] = tmp;
- }
- if (lyxrc.isp_use_input_encoding &&
- params.inputenc != "default") {
- string enc = (params.inputenc == "auto")
- ? params.language->encoding()->LatexName()
- : params.inputenc;
- string::size_type n = enc.length();
- tmp = new char[3];
- string("-T").copy(tmp, 2);
- tmp[2] = '\0';
- argv[argc++] = tmp; // Input encoding
- tmp = new char[n + 1];
- enc.copy(tmp, n);
- tmp[n] = '\0';
- argv[argc++] = tmp;
- }
-
- argv[argc++] = 0;
-
- execvp(argv[0], const_cast<char * const *>(argv));
-
- // free the memory used by string::copy in the
- // setup of argv
- for (int i = 0; i < argc - 1; ++i)
- delete[] argv[i];
-
- lyxerr << "LyX: Failed to start ispell!" << endl;
- _exit(0);
- }
- {
- /* Parent process: Read ispells identification message */
- // Hmm...what are we using this id msg for? Nothing? (Lgb)
- // Actually I used it to tell if it's truly Ispell or if it's
- // aspell -- (kevinatk@home.com)
- // But no code actually used the results for anything useful
- // so I removed it again. Perhaps we can remove this code too.
- // - jbl
- char buf[2048];
- fd_set infds;
- struct timeval tv;
- int retval = 0;
- FD_ZERO(&infds);
- FD_SET(pipeout[0], &infds);
- tv.tv_sec = 15; // fifteen second timeout. Probably too much,
- // but it can't really hurt.
- tv.tv_usec = 0;
-
- // Configure provides us with macros which are supposed to do
- // the right typecast.
- retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1),
- SELECT_TYPE_ARG234 (&infds),
- 0,
- 0,
- SELECT_TYPE_ARG5 (&tv));
-
- if (retval > 0) {
- // Ok, do the reading. We don't have to FD_ISSET since
- // there is only one fd in infds.
- fgets(buf, 2048, in);
-
- fputs("!\n", out); // Set terse mode (silently accept correct words)
-
- } else if (retval == 0) {
- // timeout. Give nice message to user.
- lyxerr << "Ispell read timed out, what now?" << endl;
- // This probably works but could need some thought
- isp_pid = -1;
- ::close(pipeout[0]);
- ::close(pipeout[1]);
- ::close(pipein[0]);
- ::close(pipein[1]);
- isp_fd = -1;
- } else {
- // Select returned error
- lyxerr << "Select on ispell returned error, what now?" << endl;
- }
- }
- END:
+ISpell::~ISpell()
+{
+ delete[] str;
+}
+
+
+void ISpell::setError()
+{
if (isp_pid == -1) {
error_ =
"\n\n"
}
-ISpell::~ISpell()
-{
- delete[] str;
-}
-
-
-/* FIXME: this is a minimalist solution until the above
- * code is able to work with forkedcall.h. We only need
- * to reap the zombies here.
- */
-void reapSpellchecker(void)
-{
- if (isp_pid == -1)
- return;
-
- waitpid(isp_pid, 0, WNOHANG);
-}
-
-
string const ISpell::nextMiss()
{
if (str == 0 || *(e+1) == '\0')
virtual string const error();
private:
+ ///
+ void setError();
+
/// instream to communicate with ispell
FILE * in;
/// outstream to communicate with ispell
#include "support/FileInfo.h"
#include "support/filetools.h"
+#include "support/forkedcall.h"
#include "support/path.h"
#include "support/systemcall.h"
#include "support/lstrings.h"
}
+namespace {
-void AutoSave(BufferView * bv)
- // should probably be moved into BufferList (Lgb)
- // Perfect target for a thread...
-{
- if (!bv->available())
- return;
-
- if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
- // We don't save now, but we'll try again later
- bv->owner()->resetAutosaveTimer();
- return;
+class AutoSaveBuffer : public ForkedProcess {
+public:
+ ///
+ AutoSaveBuffer(BufferView & bv, string const & fname)
+ : bv_(bv), fname_(fname) {}
+ ///
+ virtual ForkedProcess * clone() const {
+ return new AutoSaveBuffer(*this);
}
+ ///
+ int start();
+private:
+ ///
+ virtual int generateChild();
+ ///
+ BufferView & bv_;
+ string fname_;
+};
+
+
+int AutoSaveBuffer::start()
+{
+ command_ = _("Auto-saving $$f");
+ command_ = subst(command_, "$$f", fname_);
+ return runNonBlocking();
+}
- bv->owner()->message(_("Autosaving current document..."));
-
- // create autosave filename
- string fname = bv->buffer()->filePath();
- fname += "#";
- fname += OnlyFilename(bv->buffer()->fileName());
- fname += "#";
+int AutoSaveBuffer::generateChild()
+{
// tmp_ret will be located (usually) in /tmp
// will that be a problem?
pid_t const pid = fork(); // If you want to debug the autosave
string const tmp_ret = lyx::tempName(string(), "lyxauto");
if (!tmp_ret.empty()) {
- bv->buffer()->writeFile(tmp_ret);
+ bv_.buffer()->writeFile(tmp_ret);
// assume successful write of tmp_ret
- if (!lyx::rename(tmp_ret, fname)) {
+ if (!lyx::rename(tmp_ret, fname_)) {
failed = true;
// most likely couldn't move between filesystems
// unless write of tmp_ret failed
if (failed) {
// failed to write/rename tmp_ret so try writing direct
- if (!bv->buffer()->writeFile(fname)) {
+ if (!bv_.buffer()->writeFile(fname_)) {
// It is dangerous to do this in the child,
// but safe in the parent, so...
if (pid == -1)
- bv->owner()->message(_("Autosave failed!"));
+ bv_.owner()->message(_("Autosave failed!"));
}
}
if (pid == 0) { // we are the child so...
_exit(0);
}
}
+ return pid;
+}
+
+} // namespace anon
+
+
+void AutoSave(BufferView * bv)
+ // should probably be moved into BufferList (Lgb)
+ // Perfect target for a thread...
+{
+ if (!bv->available())
+ return;
+
+ if (bv->buffer()->isBakClean() || bv->buffer()->isReadonly()) {
+ // We don't save now, but we'll try again later
+ bv->owner()->resetAutosaveTimer();
+ return;
+ }
+
+ bv->owner()->message(_("Autosaving current document..."));
+
+ // create autosave filename
+ string fname = bv->buffer()->filePath();
+ fname += "#";
+ fname += OnlyFilename(bv->buffer()->fileName());
+ fname += "#";
+ AutoSaveBuffer autosave(*bv, fname);
+ autosave.start();
+
bv->buffer()->markBakClean();
bv->owner()->resetAutosaveTimer();
}
+2002-10-25 Angus Leeming <leeming@lyx.org>
+
+ * forkedcall.[Ch]: split ForkedCall up into a base class ForkedProcess
+ and a minimal ForkedCall daughter class.
+
+ * forkedcontr.[Ch]: minimal changes reflecting the use of a
+ ForkedProcess base class responsible for launching all child proceses.
+
2002-09-25 Angus Leeming <leeming@lyx.org>
* LIstream.h:
#endif
-Forkedcall::Forkedcall()
- : pid_(0), retval_(0)
-{}
-
-
-int Forkedcall::startscript(Starttype wait, string const & what)
-{
- if (wait == Wait) {
- command_ = what;
- retval_ = 0;
-
- pid_ = generateChild();
- if (pid_ <= 0) { // child or fork failed.
- retval_ = 1;
- } else {
- retval_ = waitForChild();
- }
-
- return retval_;
- }
-
- // DontWait
- retval_ = startscript(what, SignalTypePtr());
- return retval_;
-}
-
-
-int Forkedcall::startscript(string const & what, SignalTypePtr signal)
-{
- command_ = what;
- signal_ = signal;
- retval_ = 0;
-
- pid_ = generateChild();
- if (pid_ <= 0) { // child or fork failed.
- retval_ = 1;
- return retval_;
- }
-
- // Non-blocking execution.
- // Integrate into the Controller
- ForkedcallsController & contr = ForkedcallsController::get();
- contr.addCall(*this);
-
- return retval_;
-}
-
-
-void Forkedcall::emitSignal()
-{
- if (signal_.get()) {
- signal_->operator()(command_, pid_, retval_);
- }
-}
-
-
namespace {
class Murder : public boost::signals::trackable {
} // namespace anon
-void Forkedcall::kill(int tol)
+ForkedProcess::ForkedProcess()
+ : pid_(0), retval_(0)
+{}
+
+
+void ForkedProcess::emitSignal()
+{
+ if (signal_.get()) {
+ signal_->operator()(pid_, retval_);
+ }
+}
+
+
+// Wait for child process to finish.
+int ForkedProcess::runBlocking()
+{
+ retval_ = 0;
+ pid_ = generateChild();
+ if (pid_ <= 0) { // child or fork failed.
+ retval_ = 1;
+ return retval_;
+ }
+
+ retval_ = waitForChild();
+ return retval_;
+}
+
+
+// Do not wait for child process to finish.
+int ForkedProcess::runNonBlocking()
{
- lyxerr << "Forkedcall::kill(" << tol << ")" << std::endl;
+ retval_ = 0;
+ pid_ = generateChild();
+ if (pid_ <= 0) { // child or fork failed.
+ retval_ = 1;
+ return retval_;
+ }
+
+ // Non-blocking execution.
+ // Integrate into the Controller
+ ForkedcallsController & contr = ForkedcallsController::get();
+ contr.addCall(*this);
+
+ return retval_;
+}
+
+void ForkedProcess::kill(int tol)
+{
+ lyxerr << "ForkedProcess::kill(" << tol << ")" << std::endl;
if (pid() == 0) {
lyxerr << "Can't kill non-existent process!" << endl;
return;
// Wait for child process to finish. Returns returncode from child.
-int Forkedcall::waitForChild() {
+int ForkedProcess::waitForChild()
+{
// We'll pretend that the child returns 1 on all error conditions.
retval_ = 1;
int status;
}
+int Forkedcall::startscript(Starttype wait, string const & what)
+{
+ if (wait != Wait) {
+ retval_ = startscript(what, SignalTypePtr());
+ return retval_;
+ }
+
+ command_ = what;
+ signal_.reset();
+ return runBlocking();
+}
+
+
+int Forkedcall::startscript(string const & what, SignalTypePtr signal)
+{
+ command_ = what;
+ signal_ = signal;
+
+ return runNonBlocking();
+}
+
+
// generate child in background
-pid_t Forkedcall::generateChild()
+int Forkedcall::generateChild()
{
const int MAX_ARGV = 255;
char *syscmd = 0;
#ifndef __EMX__
pid_t cpid = ::fork();
- if (cpid == 0) { // child
+ if (cpid == 0) {
+ // Child
execvp(syscmd, argv);
// If something goes wrong, we end up here
string args;
args += string(" ") + argv[i++];
lyxerr << "execvp of \"" << syscmd << args << "\" failed: "
<< strerror(errno) << endl;
+ _exit(1);
}
#else
pid_t cpid = spawnvp(P_SESSION|P_DEFAULT|P_MINIMIZE|P_BACKGROUND,
syscmd, argv);
#endif
- if (cpid < 0) { // error
- lyxerr << "Could not fork: "
- << strerror(errno) << endl;
+ if (cpid < 0) {
+ // Error.
+ lyxerr << "Could not fork: " << strerror(errno) << endl;
+ }
+
+ // Clean-up.
+ delete [] syscmd;
+ for (int i = 0; i < MAX_ARGV; ++i) {
+ if (argv[i] == 0)
+ break;
+ delete [] argv[i];
}
return cpid;
#include "LString.h"
#include <boost/shared_ptr.hpp>
-#include <boost/signals/signal3.hpp>
+#include <boost/signals/signal2.hpp>
+#include <boost/function/function0.hpp>
#include <sys/types.h>
-class Forkedcall {
+class ForkedProcess {
public:
///
enum Starttype {
};
///
- Forkedcall();
-
- /** Start the child process.
- *
- * The command "what" is passed to fork() for execution.
- *
- * There are two startscript commands available. They differ in that
- * the second receives a signal that is executed on completion of
- * the command. This makes sense only for a command executed
- * in the background, ie DontWait.
- *
- * The other startscript command can be executed either blocking
- * or non-blocking, but no signal will be emitted on finishing.
- */
- int startscript(Starttype, string const & what);
+ ForkedProcess();
+ ///
+ virtual ~ForkedProcess() {}
+ ///
+ virtual ForkedProcess * clone() const = 0;
/** A SignalType signal is can be emitted once the forked process
* has finished. It passes:
- * the commandline string;
* the PID of the child and;
* the return value from the child.
*
* we can return easily to C++ methods, rather than just globally
* accessible functions.
*/
- typedef boost::signal3<void, string const &, pid_t, int> SignalType;
+ typedef boost::signal2<void, pid_t, int> SignalType;
/** The signal is connected in the calling routine to the desired
* slot. We pass a shared_ptr rather than a reference to the signal
*/
typedef boost::shared_ptr<SignalType> SignalTypePtr;
- ///
- int startscript(string const & what, SignalTypePtr);
-
/** Invoking the following methods makes sense only if the command
* is running asynchronously!
*/
*/
void setRetValue(int r) { retval_ = r; }
+ /// Returns the identifying command (for display in the GUI perhaps).
+ string const & command() const { return command_; }
+
/** Kill child prematurely.
* First, a SIGHUP is sent to the child.
* If that does not end the child process within "tolerance"
* When the child is dead, the callback is called.
*/
void kill(int tolerance = 5);
- ///
- string const & command() const { return command_; }
-private:
+protected:
+ /** Wait for child process to finish.
+ * Returns returncode from child.
+ */
+ int runBlocking();
+ /** Do not wait for child process to finish.
+ * Returns returncode from child.
+ */
+ int runNonBlocking();
+
/// Callback function
SignalTypePtr signal_;
- /// Commmand line
+ /// identifying command (for display in the GUI perhaps).
string command_;
/// Process ID of child
/// Return value from child
int retval_;
-
- ///
- pid_t generateChild();
+private:
+ /// generate child in background
+ virtual int generateChild() = 0;
/// Wait for child process to finish. Updates returncode from child.
int waitForChild();
};
+
+class Forkedcall : public ForkedProcess {
+public:
+ ///
+ virtual ForkedProcess * clone() const {
+ return new Forkedcall(*this);
+ }
+
+ /** Start the child process.
+ *
+ * The command "what" is passed to execvp() for execution.
+ *
+ * There are two startscript commands available. They differ in that
+ * the second receives a signal that is executed on completion of
+ * the command. This makes sense only for a command executed
+ * in the background, ie DontWait.
+ *
+ * The other startscript command can be executed either blocking
+ * or non-blocking, but no signal will be emitted on finishing.
+ */
+ int startscript(Starttype, string const & what);
+
+ ///
+ int startscript(string const & what, SignalTypePtr);
+
+private:
+ ///
+ virtual int generateChild();
+};
+
#endif // FORKEDCALL_H
}
-// Add child process information to the list of controlled processes
-void ForkedcallsController::addCall(Forkedcall const &newcall)
+void ForkedcallsController::addCall(ForkedProcess const & newcall)
{
if (!timeout_->running())
timeout_->start();
- Forkedcall * call = new Forkedcall(newcall);
- forkedCalls.push_back(call);
+ forkedCalls.push_back(newcall.clone());
childrenChanged();
}
for (ListType::iterator it = forkedCalls.begin();
it != forkedCalls.end(); ++it) {
- Forkedcall * actCall = *it;
+ ForkedProcess * actCall = *it;
pid_t pid = actCall->pid();
int stat_loc;
#pragma interface
#endif
-class Forkedcall;
+class ForkedProcess;
class Timeout;
class ForkedcallsController : public boost::signals::trackable {
static ForkedcallsController & get();
/// Add a new child process to the list of controlled processes.
- void addCall(Forkedcall const & newcall);
+ void addCall(ForkedProcess const &);
/** This method is connected to the timer. Every XX ms it is called
* so that we can check on the status of the children. Those that
ForkedcallsController(ForkedcallsController const &);
/// The child processes
- typedef std::list<Forkedcall *> ListType;
+ typedef std::list<ForkedProcess *> ListType;
///
ListType forkedCalls;