commit 7b48e2aac0d63fe23cbeadf6ddd50ab0c26c1b6e Author: Georg Baum <b...@lyx.org> Date: Sun Jan 11 14:06:17 2015 +0100
Start qt event loop for commandline usage This is needed since src/support calls lots of qt code, and some parts of it (e.g. QFileInfo) require a running event loop. This fixes bug #4917 which is a symptom of the problem. The fix is to create a QCoreApplication for tex2lyx, lyxclient and LyX running without GUI. In the future this could be extended, i.e. to split up the frontend Application class in a GUI and a console class, and having the console class use LyXConsoleApp in the same way as Application now uses GuiApplication. If that is done, one could also think of moving support/ConsoleApplication to frontend/console/ConsoleApplication. diff --git a/src/LyX.cpp b/src/LyX.cpp index 0a21b4b..8b75b44 100644 --- a/src/LyX.cpp +++ b/src/LyX.cpp @@ -50,6 +50,7 @@ #include "frontends/alert.h" #include "frontends/Application.h" +#include "support/ConsoleApplication.h" #include "support/lassert.h" #include "support/debug.h" #include "support/environment.h" @@ -195,6 +196,27 @@ struct LyX::Impl { }; +/// The main application class for console mode +class LyXConsoleApp : public ConsoleApplication +{ +public: + LyXConsoleApp(LyX * lyx, int & argc, char * argv[]) + : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx), + argc_(argc), argv_(argv) + { + } + void doExec() + { + int const exit_status = lyx_->execWithoutGui(argc_, argv_); + exit(exit_status); + } +private: + LyX * lyx_; + int & argc_; + char ** argv_; +}; + + /// frontend::Application * theApp() { @@ -287,39 +309,13 @@ int LyX::exec(int & argc, char * argv[]) setLocale(); if (!use_gui) { - // FIXME: create a ConsoleApplication - int exit_status = init(argc, argv); - if (exit_status) { - prepareExit(); - return exit_status; - } - - // this is correct, since return values are inverted. - exit_status = !loadFiles(); + LyXConsoleApp app(this, argc, argv); - if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) { - prepareExit(); - return exit_status; - } + // Reestablish our defaults, as Qt overwrites them + // after creating app + setLocale();//??? - BufferList::iterator begin = pimpl_->buffer_list_.begin(); - - bool final_success = false; - for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) { - Buffer * buf = *I; - if (buf != buf->masterBuffer()) - continue; - vector<string>::const_iterator bcit = pimpl_->batch_commands.begin(); - vector<string>::const_iterator bcend = pimpl_->batch_commands.end(); - DispatchResult dr; - for (; bcit != bcend; ++bcit) { - LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit); - buf->dispatch(*bcit, dr); - final_success |= !dr.error(); - } - } - prepareExit(); - return !final_success; + return app.exec(); } // Let the frontend parse and remove all arguments that it knows @@ -472,6 +468,43 @@ int LyX::init(int & argc, char * argv[]) } +int LyX::execWithoutGui(int & argc, char * argv[]) +{ + int exit_status = init(argc, argv); + if (exit_status) { + prepareExit(); + return exit_status; + } + + // this is correct, since return values are inverted. + exit_status = !loadFiles(); + + if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) { + prepareExit(); + return exit_status; + } + + BufferList::iterator begin = pimpl_->buffer_list_.begin(); + + bool final_success = false; + for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) { + Buffer * buf = *I; + if (buf != buf->masterBuffer()) + continue; + vector<string>::const_iterator bcit = pimpl_->batch_commands.begin(); + vector<string>::const_iterator bcend = pimpl_->batch_commands.end(); + DispatchResult dr; + for (; bcit != bcend; ++bcit) { + LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit); + buf->dispatch(*bcit, dr); + final_success |= !dr.error(); + } + } + prepareExit(); + return !final_success; +} + + bool LyX::loadFiles() { LATTEST(!use_gui); diff --git a/src/LyX.h b/src/LyX.h index 70b8b7e..ccce7a3 100644 --- a/src/LyX.h +++ b/src/LyX.h @@ -60,8 +60,8 @@ class Application; /// initial startup class LyX { + friend class LyXConsoleApp; public: - LyX(); ~LyX(); @@ -85,6 +85,9 @@ private: */ int init(int & argc, char * argv[]); + /// Execute commandline commands if no GUI was requested. + int execWithoutGui(int & argc, char * argv[]); + /// Execute batch commands if available. void execCommands(); diff --git a/src/client/client.cpp b/src/client/client.cpp index c781bc6..4d7eea5 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -12,6 +12,7 @@ #include <config.h> +#include "support/ConsoleApplication.h" #include "support/debug.h" #include "support/FileName.h" #include "support/FileNameList.h" @@ -54,6 +55,7 @@ #include <cerrno> #include <cstdio> #include <cstdlib> +#include <exception> #include <string> #include <vector> #include <map> @@ -427,6 +429,16 @@ namespace cmdline { docstring mainTmp(from_ascii("/tmp")); +class StopException : public exception +{ +public: + StopException(int status) : status_(status) {} + int status() const { return status_; } +private: + int status_; +}; + + void usage() { cerr << @@ -453,7 +465,7 @@ void usage() int h(vector<docstring> const &) { usage(); - exit(0); + throw StopException(EXIT_SUCCESS); } @@ -548,15 +560,38 @@ int p(vector<docstring> const & arg) } // namespace cmdline -} // namespace lyx - -int main(int argc, char * argv[]) +/// The main application class +class LyXClientApp : public ConsoleApplication { - using namespace lyx; - lyxerr.setStream(cerr); +public: + LyXClientApp(int & argc, char * argv[]) + : ConsoleApplication("client" PROGRAM_SUFFIX, argc, argv), + argc_(argc), argv_(argv) + { + } + void doExec() + { + try { + int const exit_status = run(); + exit(exit_status); + } + catch (cmdline::StopException & e) { + exit(e.status()); + } + } +private: + int run(); + int & argc_; + char ** argv_; +}; +int LyXClientApp::run() +{ + // qt changes this, and our numeric conversions require the C locale + setlocale(LC_NUMERIC, "C"); + // Set defaults char const * const lyxsocket = getenv("LYXSOCKET"); if (lyxsocket) @@ -576,11 +611,11 @@ int main(int argc, char * argv[]) args.helper["-p"] = cmdline::p; // Command line failure conditions: - if ((!args.parse(argc, argv)) + if ((!args.parse(argc_, argv_)) || (args.isset["-c"] && args.isset["-g"]) || (args.isset["-a"] && args.isset["-p"])) { cmdline::usage(); - return 1; + return EXIT_FAILURE; } scoped_ptr<LyXDataSocket> server; @@ -677,6 +712,17 @@ int main(int argc, char * argv[]) return EXIT_SUCCESS; } +} // namespace lyx + + +int main(int argc, char * argv[]) +{ + lyx::lyxerr.setStream(cerr); + + lyx::LyXClientApp app(argc, argv); + return app.exec(); +} + namespace boost { diff --git a/src/support/ConsoleApplication.cpp b/src/support/ConsoleApplication.cpp new file mode 100644 index 0000000..ace4fc5 --- /dev/null +++ b/src/support/ConsoleApplication.cpp @@ -0,0 +1,51 @@ +/** + * \file ConsoleApplication.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Georg Baum + * + * Full author contact details are available in file CREDITS. + */ + +#include <config.h> + +#include "support/ConsoleApplication.h" + +#include "support/ConsoleApplicationPrivate.h" + + +namespace lyx { + +namespace support { + + +ConsoleApplication::~ConsoleApplication() +{ + delete d; +} + + +ConsoleApplication::ConsoleApplication(std::string const & app, + int & argc, char ** argv) + : d(new ConsoleApplicationPrivate(this, app, argc, argv)) +{ +} + + +int ConsoleApplication::exec() +{ + return d->execute(); +} + + +void ConsoleApplication::exit(int status) +{ + d->exit(status); +} + + +} // namespace support +} // namespace lyx + +#include "moc_ConsoleApplicationPrivate.cpp" diff --git a/src/support/ConsoleApplication.h b/src/support/ConsoleApplication.h new file mode 100644 index 0000000..bfb5d07 --- /dev/null +++ b/src/support/ConsoleApplication.h @@ -0,0 +1,61 @@ +/** + * \file ConsoleApplication.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Georg Baum + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef CONSOPLEAPPLICATION_H +#define CONSOPLEAPPLICATION_H + +#include "support/strfwd.h" + +namespace lyx { + +namespace support { + +class ConsoleApplicationPrivate; + +/// The main application class for console mode. +/** +There should be only one instance of this class. No Qt object +initialisation should be done before the instantiation of this class. +This class could be moved to src/frontends/console in the future. +This would make sense if more console app related code is created. +*/ +class ConsoleApplication +{ + friend class ConsoleApplicationPrivate; +public: + ConsoleApplication(std::string const & app, int & argc, char ** argv); + virtual ~ConsoleApplication(); + + /// Start the event loop and execute the application + int exec(); + +protected: + /// Do the real work. + /// This is called after the event loop was started. + virtual void doExec() = 0; + /** + * Exit the application with status \p status. + * This must be called from doExec(), otherwise the application runs + * forever. + * Note that in contrast to the ISO C function ::exit() this method does + * return. It only registers that the program is to be stopped with the + * given status code, and this happens the next time the event loop is + * processed. + */ + void exit(int status); + +private: + ConsoleApplicationPrivate * const d; +}; + +} // namespace support +} // namespace lyx + +#endif // CONSOPLEAPPLICATION_H diff --git a/src/support/ConsoleApplicationPrivate.h b/src/support/ConsoleApplicationPrivate.h new file mode 100644 index 0000000..74aa161 --- /dev/null +++ b/src/support/ConsoleApplicationPrivate.h @@ -0,0 +1,64 @@ +/** + * \file ConsoleApplicationPrivate.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Georg Baum + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef CONSOPLEAPPLICATIONPRIVATE_H +#define CONSOPLEAPPLICATIONPRIVATE_H + +#include "support/qstring_helpers.h" + +#include <QCoreApplication> +#include <QDateTime> +#include <QTimer> + +#include <string> + + +namespace lyx { + +namespace support { + +class ConsoleApplication; + +class ConsoleApplicationPrivate : public QCoreApplication +{ + Q_OBJECT +public: + ConsoleApplicationPrivate(ConsoleApplication * owner, + std::string const & app, int & argc, char ** argv) + : QCoreApplication(argc, argv), owner_(owner) + { + setOrganizationName("LyX"); + setOrganizationDomain("lyx.org"); + setApplicationName(toqstr(app)); + + qsrand(QDateTime::currentDateTime().toTime_t()); + } + int execute() + { + // set timer to do the work asynchronously after the event + // loop was started + QTimer::singleShot(0, this, SLOT(doExec())); + // start event loop + return exec(); + } +private Q_SLOTS: + void doExec() + { + owner_->doExec(); + } +private: + ConsoleApplication * owner_; +}; + + +} // namespace support +} // namespace lyx + +#endif // CONSOPLEAPPLICATIONPRIVATE_H diff --git a/src/support/Makefile.am b/src/support/Makefile.am index fe7be80..b843b51 100644 --- a/src/support/Makefile.am +++ b/src/support/Makefile.am @@ -13,7 +13,9 @@ BUILT_SOURCES = $(PCH_FILE) ######################### Qt stuff ############################# # -MOCHEADER = SystemcallPrivate.h +MOCHEADER = \ + ConsoleApplicationPrivate.h \ + SystemcallPrivate.h MOCEDFILES = $(MOCHEADER:%.h=moc_%.cpp) @@ -36,6 +38,9 @@ liblyxsupport_a_SOURCES = \ FileMonitor.cpp \ RandomAccessList.h \ bind.h \ + ConsoleApplication.cpp \ + ConsoleApplication.h \ + ConsoleApplicationPrivate.h \ convert.cpp \ convert.h \ copied_ptr.h \ diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp index dc46770..2104854 100644 --- a/src/tex2lyx/tex2lyx.cpp +++ b/src/tex2lyx/tex2lyx.cpp @@ -24,6 +24,7 @@ #include "Preamble.h" #include "TextClass.h" +#include "support/ConsoleApplication.h" #include "support/convert.h" #include "support/ExceptionMessage.h" #include "support/filetools.h" @@ -35,6 +36,7 @@ #include <cstdlib> #include <algorithm> +#include <exception> #include <iostream> #include <string> #include <sstream> @@ -531,6 +533,44 @@ int error_code = 0; typedef int (*cmd_helper)(string const &, string const &); +class StopException : public exception +{ + public: + StopException(int status) : status_(status) {} + int status() const { return status_; } + private: + int status_; +}; + + +/// The main application class +class TeX2LyXApp : public ConsoleApplication +{ +public: + TeX2LyXApp(int & argc, char * argv[]) + : ConsoleApplication("tex2lyx" PROGRAM_SUFFIX, argc, argv), + argc_(argc), argv_(argv) + { + } + void doExec() + { + try { + int const exit_status = run(); + exit(exit_status); + } + catch (StopException & e) { + exit(e.status()); + } + } +private: + void easyParse(); + /// Do the real work + int run(); + int & argc_; + char ** argv_; +}; + + int parse_help(string const &, string const &) { cout << "Usage: tex2lyx [options] infile.tex [outfile.lyx]\n" @@ -558,7 +598,7 @@ int parse_help(string const &, string const &) "\tand \"SYSDIR/layouts\" are searched for layout and module files.\n" "Check the tex2lyx man page for more details." << endl; - exit(error_code); + throw StopException(error_code); } @@ -570,14 +610,14 @@ int parse_version(string const &, string const &) << endl; cout << lyx_version_info << endl; - exit(error_code); + throw StopException(error_code); } void error_message(string const & message) { cerr << "tex2lyx: " << message << "\n\n"; - error_code = 1; + error_code = EXIT_FAILURE; parse_help(string(), string()); } @@ -687,7 +727,7 @@ int parse_copyfiles(string const &, string const &) } -void easyParse(int & argc, char * argv[]) +void TeX2LyXApp::easyParse() { map<string, cmd_helper> cmdmap; @@ -710,29 +750,29 @@ void easyParse(int & argc, char * argv[]) cmdmap["-roundtrip"] = parse_roundtrip; cmdmap["-copyfiles"] = parse_copyfiles; - for (int i = 1; i < argc; ++i) { + for (int i = 1; i < argc_; ++i) { map<string, cmd_helper>::const_iterator it - = cmdmap.find(argv[i]); + = cmdmap.find(argv_[i]); // don't complain if not found - may be parsed later if (it == cmdmap.end()) { - if (argv[i][0] == '-') - error_message(string("Unknown option `") + argv[i] + "'."); + if (argv_[i][0] == '-') + error_message(string("Unknown option `") + argv_[i] + "'."); else continue; } - string arg = (i + 1 < argc) ? os::utf8_argv(i + 1) : string(); - string arg2 = (i + 2 < argc) ? os::utf8_argv(i + 2) : string(); + string arg = (i + 1 < argc_) ? os::utf8_argv(i + 1) : string(); + string arg2 = (i + 2 < argc_) ? os::utf8_argv(i + 2) : string(); int const remove = 1 + it->second(arg, arg2); // Now, remove used arguments by shifting // the following ones remove places down. os::remove_internal_args(i, remove); - argc -= remove; - for (int j = i; j < argc; ++j) - argv[j] = argv[j + remove]; + argc_ -= remove; + for (int j = i; j < argc_; ++j) + argv_[j] = argv_[j + remove]; --i; } } @@ -960,18 +1000,13 @@ bool tex2tex(string const & infilename, FileName const & outfilename, return false; } -} // namespace lyx +namespace { -int main(int argc, char * argv[]) +int TeX2LyXApp::run() { - using namespace lyx; - - //setlocale(LC_CTYPE, ""); - - lyxerr.setStream(cerr); - - os::init(argc, argv); + // qt changes this, and our numeric conversions require the C locale + setlocale(LC_NUMERIC, "C"); try { init_package(internal_path(os::utf8_argv(0)), string(), string()); @@ -982,9 +1017,9 @@ int main(int argc, char * argv[]) return EXIT_FAILURE; } - easyParse(argc, argv); + easyParse(); - if (argc <= 1) + if (argc_ <= 1) error_message("Not enough arguments."); try { @@ -1017,7 +1052,7 @@ int main(int argc, char * argv[]) infilename = makeAbsPath(infilename).absFileName(); string outfilename; - if (argc > 2) { + if (argc_ > 2) { outfilename = internal_path(os::utf8_argv(2)); if (outfilename != "-") outfilename = makeAbsPath(outfilename).absFileName(); @@ -1107,11 +1142,27 @@ int main(int argc, char * argv[]) if (tex2tex(infilename, FileName(outfilename), default_encoding)) return EXIT_SUCCESS; } else { - if (tex2lyx(infilename, FileName(outfilename), default_encoding)) + if (lyx::tex2lyx(infilename, FileName(outfilename), default_encoding)) return EXIT_SUCCESS; } } return EXIT_FAILURE; } +} // anonymous namespace +} // namespace lyx + + +int main(int argc, char * argv[]) +{ + //setlocale(LC_CTYPE, ""); + + lyx::lyxerr.setStream(cerr); + + os::init(argc, argv); + + lyx::TeX2LyXApp app(argc, argv); + return app.exec(); +} + // }])