3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
10 * \author André Pönitz
12 * Full author contact details are available in file CREDITS.
20 #include "AppleSpellChecker.h"
21 #include "AspellChecker.h"
23 #include "BufferList.h"
25 #include "CiteEnginesList.h"
27 #include "ConverterCache.h"
28 #include "Converter.h"
29 #include "CutAndPaste.h"
30 #include "DispatchResult.h"
31 #include "EnchantChecker.h"
33 #include "ErrorList.h"
35 #include "FuncStatus.h"
36 #include "HunspellChecker.h"
39 #include "LaTeXFonts.h"
40 #include "LayoutFile.h"
43 #include "LyXAction.h"
45 #include "ModuleList.h"
48 #include "ServerSocket.h"
52 #include "frontends/alert.h"
53 #include "frontends/Application.h"
55 #include "support/ConsoleApplication.h"
56 #include "support/convert.h"
57 #include "support/lassert.h"
58 #include "support/debug.h"
59 #include "support/environment.h"
60 #include "support/ExceptionMessage.h"
61 #include "support/filetools.h"
62 #include "support/gettext.h"
63 #include "support/lstrings.h"
64 #include "support/Messages.h"
65 #include "support/os.h"
66 #include "support/Package.h"
67 #include "support/unique_ptr.h"
78 #include <qglobal.h> // For QT_VERSION
81 using namespace lyx::support;
83 #if defined (USE_MACOSX_PACKAGING)
84 #include <crt_externs.h>
89 namespace Alert = frontend::Alert;
90 namespace os = support::os;
94 // Are we using the GUI at all? We default to true and this is changed
95 // to false when the export feature is used.
100 // Report on the terminal about spawned commands. The default is false
101 // and can be changed with the option -v (--verbose).
103 bool verbose = false;
106 // Do not treat the "missing glyphs" warning of fontspec as an error message.
107 // The default is false and can be changed with the option
108 // --ignore-error-message missing_glyphs
109 // This is used in automated testing.
110 bool ignore_missing_glyphs = false;
113 // We default to open documents in an already running instance, provided that
114 // the lyxpipe has been setup. This can be overridden either on the command
115 // line or through preference settings.
117 RunMode run_mode = PREFERRED;
120 // Tell what files can be silently overwritten during batch export.
121 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
122 // Unless specified on command line (through the -f switch) or through the
123 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
125 OverwriteFiles force_overwrite = UNSPECIFIED;
128 // Scale the GUI by this factor. This works whether we have a HiDpi screen
129 // or not and scales everything, also fonts. Can only be changed by setting
130 // the QT_SCALE_FACTOR environment variable before launching LyX and only
131 // works properly with Qt 5.6 or higher.
133 double qt_scale_factor = 1.0;
138 // Filled with the command line arguments "foo" of "-sysdir foo" or
140 string cl_system_support;
141 string cl_user_support;
145 LyX * singleton_ = nullptr;
147 void showFileError(string const & error)
149 Alert::warning(_("Could not read configuration file"),
150 bformat(_("Error while reading the configuration file\n%1$s.\n"
151 "Please check your installation."), from_utf8(error)));
156 /// The main application class private implementation.
159 : latexfonts_(nullptr), spell_checker_(nullptr),
160 apple_spell_checker_(nullptr), aspell_checker_(nullptr),
161 enchant_checker_(nullptr), hunspell_checker_(nullptr)
167 delete apple_spell_checker_;
168 delete aspell_checker_;
169 delete enchant_checker_;
170 delete hunspell_checker_;
174 BufferList buffer_list_;
176 KeyMap toplevel_keymap_;
178 CmdDef toplevel_cmddef_;
180 unique_ptr<Server> lyx_server_;
182 unique_ptr<ServerSocket> lyx_socket_;
184 unique_ptr<frontend::Application> application_;
185 /// lyx session, containing lastfiles, lastfilepos, and lastopened
186 unique_ptr<Session> session_;
188 /// Files to load at start.
189 vector<string> files_to_load_;
191 /// The messages translators.
192 map<string, Messages> messages_;
194 /// The file converters.
195 Converters converters_;
196 /// The system converters after reading lyxrc.defaults.
197 Converters system_converters_;
199 /// Global format information
201 /// The system formats after reading lyxrc.defaults.
202 Formats system_formats_;
208 Movers system_movers_;
210 /// the parsed command line batch command if any
211 vector<string> batch_commands;
214 LaTeXFonts * latexfonts_;
217 SpellChecker * spell_checker_;
219 SpellChecker * apple_spell_checker_;
221 SpellChecker * aspell_checker_;
223 SpellChecker * enchant_checker_;
225 SpellChecker * hunspell_checker_;
229 /// The main application class for console mode
230 class LyXConsoleApp : public ConsoleApplication
233 LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
234 : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
235 argc_(argc), argv_(argv)
238 void doExec() override
240 int const exit_status = lyx_->execWithoutGui(argc_, argv_);
251 frontend::Application * theApp()
254 return singleton_->pimpl_->application_.get();
263 singleton_ = nullptr;
267 void lyx_exit(int exit_code)
270 // Something wrong happened so better save everything, just in
275 // Properly crash in debug mode in order to get a useful backtrace.
279 // In release mode, try to exit gracefully.
281 theApp()->exit(exit_code);
295 Messages & LyX::messages(string const & language)
297 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
299 if (it != pimpl_->messages_.end())
302 pair<map<string, Messages>::iterator, bool> result =
303 pimpl_->messages_.insert(make_pair(language, Messages(language)));
305 LATTEST(result.second);
306 return result.first->second;
310 int LyX::exec(int & argc, char * argv[])
312 // Minimal setting of locale before parsing command line
314 init_package(os::utf8_argv(0), string(), string());
315 // we do not get to this point when init_package throws an exception
317 } catch (ExceptionMessage const & message) {
318 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
321 // Here we need to parse the command line. At least
322 // we need to parse for "-dbg" and "-help"
323 easyParse(argc, argv);
325 #if QT_VERSION >= 0x050600
326 // Check whether Qt will scale all GUI elements and accordingly
327 // set the scale factor so that to avoid blurred images and text
328 char const * const scale_factor = getenv("QT_SCALE_FACTOR");
330 qt_scale_factor = convert<double>(scale_factor);
331 if (qt_scale_factor < 1.0)
332 qt_scale_factor = 1.0;
337 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
338 } catch (ExceptionMessage const & message) {
339 if (message.type_ == ErrorException) {
340 Alert::error(message.title_, message.details_);
342 } else if (message.type_ == WarningException) {
343 Alert::warning(message.title_, message.details_);
347 // Reinit the messages machinery in case package() knows
348 // something interesting about the locale directory.
352 LyXConsoleApp app(this, argc, argv);
354 // Reestablish our defaults, as Qt overwrites them
355 // after creating app
361 // Let the frontend parse and remove all arguments that it knows
362 pimpl_->application_.reset(createApplication(argc, argv));
364 // Reestablish our defaults, as Qt overwrites them
365 // after createApplication()
368 // Parse and remove all known arguments in the LyX singleton
369 // Give an error for all remaining ones.
370 int exit_status = init(argc, argv);
372 // Kill the application object before exiting.
373 pimpl_->application_.reset();
379 // If not otherwise specified by a command line option or
380 // by preferences, we default to reuse a running instance.
381 if (run_mode == PREFERRED)
382 run_mode = USE_REMOTE;
385 /* Create a CoreApplication class that will provide the main event loop
386 * and the socket callback registering. With Qt, only QtCore
387 * library would be needed.
388 * When this is done, a server_mode could be created and the following two
389 * line would be moved out from here.
390 * However, note that the first of the two lines below triggers the
391 * "single instance" behavior, which should occur right at this point.
393 // Note: socket callback must be registered after init(argc, argv)
394 // such that package().temp_dir() is properly initialized.
395 pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
396 pimpl_->lyx_socket_.reset(new ServerSocket(
397 FileName(package().temp_dir().absFileName() + "/lyxsocket")));
399 // Start the real execution loop.
400 if (!pimpl_->lyx_server_->deferredLoadingToOtherInstance())
401 exit_status = pimpl_->application_->exec();
402 else if (!pimpl_->files_to_load_.empty()) {
403 lyxerr << _("The following files could not be loaded:") << endl;
404 for (auto const & f : pimpl_->files_to_load_)
414 void LyX::prepareExit()
416 // Clear the clipboard and selection stack:
417 cap::clearCutStack();
418 cap::clearSelection();
420 // Write the index file of the converter cache
421 ConverterCache::get().writeIndex();
423 // closing buffer may throw exceptions, but we ignore them since we
426 // close buffers first
427 pimpl_->buffer_list_.closeAll();
428 } catch (ExceptionMessage const &) {}
430 // register session changes and shutdown server and socket
432 if (pimpl_->session_)
433 pimpl_->session_->writeFile();
434 pimpl_->session_.reset();
435 pimpl_->lyx_server_.reset();
436 pimpl_->lyx_socket_.reset();
439 // do any other cleanup procedures now
440 if (package().temp_dir() != package().system_temp_dir()) {
441 string const abs_tmpdir = package().temp_dir().absFileName();
442 if (!contains(abs_tmpdir, "lyx_tmpdir")) {
443 docstring const msg =
444 bformat(_("%1$s does not appear like a LyX created temporary directory."),
445 from_utf8(abs_tmpdir));
446 Alert::warning(_("Cannot remove temporary directory"), msg);
448 LYXERR(Debug::INFO, "Deleting tmp dir " << abs_tmpdir);
449 if (!package().temp_dir().destroyDirectory()) {
450 LYXERR0(bformat(_("Unable to remove the temporary directory %1$s"),
451 from_utf8(abs_tmpdir)));
456 // Kill the application object before exiting. This avoids crashes
457 // when exiting on Linux.
458 pimpl_->application_.reset();
462 void LyX::earlyExit(int status)
464 LATTEST(pimpl_->application_.get());
465 // LyX::pimpl_::application_ is not initialised at this
466 // point so it's safe to just exit after some cleanup.
472 int LyX::init(int & argc, char * argv[])
474 // check for any spurious extra arguments
475 // other than documents
476 for (int argi = 1; argi < argc ; ++argi) {
477 if (argv[argi][0] == '-') {
479 bformat(_("Wrong command line option `%1$s'. Exiting."),
480 from_utf8(os::utf8_argv(argi)))) << endl;
485 // Initialization of LyX (reads lyxrc and more)
486 LYXERR(Debug::INIT, "Initializing LyX::init...");
487 bool success = init();
488 LYXERR(Debug::INIT, "Initializing LyX::init...done");
492 // Remaining arguments are assumed to be files to load.
493 for (int argi = 1; argi < argc; ++argi)
494 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
496 if (!use_gui && pimpl_->files_to_load_.empty()) {
497 lyxerr << to_utf8(_("Missing filename for this operation.")) << endl;
502 pimpl_->files_to_load_.push_back(
503 i18nLibFileSearch("examples", "Welcome.lyx").absFileName());
510 int LyX::execWithoutGui(int & argc, char * argv[])
512 int exit_status = init(argc, argv);
518 // Used to keep track of which buffers were explicitly loaded by user request.
519 // This is necessary because master and child document buffers are loaded, even
520 // if they were not named on the command line. We do not want to dispatch to
522 vector<Buffer *> command_line_buffers;
524 // Load the files specified on the command line
525 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
526 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
527 for (; it != end; ++it) {
528 // get absolute path of file and add ".lyx" to the filename if necessary
529 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
535 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName());
536 LYXERR(Debug::FILES, "Loading " << fname);
537 if (buf && buf->loadLyXFile() == Buffer::ReadSuccess) {
538 ErrorList const & el = buf->errorList("Parse");
539 for(ErrorItem const & e : el)
541 command_line_buffers.push_back(buf);
544 pimpl_->buffer_list_.release(buf);
545 docstring const error_message =
546 bformat(_("LyX failed to load the following file: %1$s"),
547 from_utf8(fname.absFileName()));
548 lyxerr << to_utf8(error_message) << endl;
549 exit_status = 1; // failed
553 if (exit_status || pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
558 // Iterate through the buffers that were specified on the command line
559 bool final_success = false;
560 vector<Buffer *>::iterator buf_it = command_line_buffers.begin();
561 for (; buf_it != command_line_buffers.end(); ++buf_it) {
562 Buffer * buf = *buf_it;
563 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
564 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
566 for (; bcit != bcend; ++bcit) {
567 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
568 buf->dispatch(*bcit, dr);
569 final_success |= !dr.error();
573 return !final_success;
577 void execBatchCommands()
580 singleton_->execCommands();
584 void LyX::execCommands()
586 // The advantage of doing this here is that the event loop
587 // is already started. So any need for interaction will be
590 // if reconfiguration is needed.
591 if (LayoutFileList::get().empty()) {
592 switch (Alert::prompt(
593 _("No textclass is found"),
594 _("LyX will only have minimal functionality because no textclasses "
595 "have been found. You can either try to reconfigure LyX normally, "
596 "try to reconfigure without checking your LaTeX installation, or continue."),
603 // regular reconfigure
604 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
607 // reconfigure --without-latex-config
608 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
609 " --without-latex-config"));
616 // create the first main window
617 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
619 if (!pimpl_->files_to_load_.empty()) {
620 // if some files were specified at command-line we assume that the
621 // user wants to edit *these* files and not to restore the session.
622 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
624 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
626 // clear this list to save a few bytes of RAM
627 pimpl_->files_to_load_.clear();
629 pimpl_->application_->restoreGuiSession();
631 // Execute batch commands if available
632 if (pimpl_->batch_commands.empty())
635 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
636 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
637 for (; bcit != bcend; ++bcit) {
638 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
639 lyx::dispatch(lyxaction.lookupFunc(*bcit));
647 The SIGHUP signal does not exist on Windows and does not need to be handled.
649 Windows handles SIGFPE and SIGSEGV signals as expected.
651 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
652 cause a new thread to be spawned. This may well result in unexpected
653 behaviour by the single-threaded LyX.
655 SIGTERM signals will come only from another process actually sending
656 that signal using 'raise' in Windows' POSIX compatability layer. It will
657 not come from the general "terminate process" methods that everyone
658 actually uses (and which can't be trapped). Killing an app 'politely' on
659 Windows involves first sending a WM_CLOSE message, something that is
660 caught already by the Qt frontend.
662 For more information see:
664 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
665 ...signals are mostly useless on Windows for a variety of reasons that are
668 'UNIX Application Migration Guide, Chapter 9'
669 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
671 'How To Terminate an Application "Cleanly" in Win32'
672 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
676 static void error_handler(int err_sig)
678 // Throw away any signals other than the first one received.
679 static sig_atomic_t handling_error = false;
682 handling_error = true;
684 // We have received a signal indicating a fatal error, so
685 // try and save the data ASAP.
688 // These lyxerr calls may or may not work:
690 // Signals are asynchronous, so the main program may be in a very
691 // fragile state when a signal is processed and thus while a signal
692 // handler function executes.
693 // In general, therefore, we should avoid performing any
694 // I/O operations or calling most library and system functions from
697 // This shouldn't matter here, however, as we've already invoked
703 msg = _("SIGHUP signal caught!\nBye.");
707 msg = _("SIGFPE signal caught!\nBye.");
710 msg = _("SIGSEGV signal caught!\n"
711 "Sorry, you have found a bug in LyX, "
712 "hope you have not lost any data.\n"
713 "Please read the bug-reporting instructions "
714 "in 'Help->Introduction' and send us a bug report, "
715 "if necessary. Thanks!\nBye.");
724 lyxerr << "\nlyx: " << msg << endl;
725 // try to make a GUI message
726 Alert::error(_("LyX crashed!"), msg, true);
729 // Deinstall the signal handlers
731 signal(SIGHUP, SIG_DFL);
733 signal(SIGINT, SIG_DFL);
734 signal(SIGFPE, SIG_DFL);
735 signal(SIGSEGV, SIG_DFL);
736 signal(SIGTERM, SIG_DFL);
739 if (err_sig == SIGSEGV ||
740 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
742 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
745 // with abort() it crashes again.
758 void LyX::printError(ErrorItem const & ei)
760 docstring tmp = _("LyX: ") + ei.error + char_type(':')
762 cerr << to_utf8(tmp) << endl;
765 #if defined (USE_MACOSX_PACKAGING)
767 // Unexposed--extract an environment variable name from its NAME=VALUE
769 std::string varname(const char* line)
771 size_t nameLen = strcspn(line, "=");
772 if (nameLen == strlen(line)) {
773 return std::string();
775 return std::string(line, nameLen);
780 void cleanDuplicateEnvVars()
782 std::set<std::string> seen;
783 std::set<std::string> dupes;
785 // Create a list of the environment variables that appear more than once
786 for (char **read = *_NSGetEnviron(); *read; read++) {
787 std::string name = varname(*read);
788 if (name.size() == 0) {
791 if (seen.find(name) != seen.end()) {
798 // Loop over the list of duplicated variables
799 std::set<std::string>::iterator dupe = dupes.begin();
800 std::set<std::string>::iterator const dend = dupes.end();
801 for (; dupe != dend; ++dupe) {
802 const char *name = (*dupe).c_str();
803 char *val = getenv(name);
805 LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
806 // unsetenv removes *all* instances of the variable from the environment
809 // replace with the value from getenv (in practice appears to be the
810 // first value in the list)
811 setenv(name, val, 0);
818 static void initTemplatePath()
820 FileName const package_template_path =
821 FileName(addName(package().system_support().absFileName(), "templates"));
823 if (lyxrc.template_path.empty()) {
824 lyxrc.template_path = package_template_path.absFileName();
826 #if defined (USE_MACOSX_PACKAGING)
827 FileName const user_template_path =
828 FileName(addName(package().user_support().absFileName(), "templates"));
830 if (package_template_path != FileName(lyxrc.template_path) &&
831 user_template_path != FileName(lyxrc.template_path))
835 FileName const user_template_link =
836 FileName(addName(user_template_path.absFileName(),"SystemTemplates"));
837 if (user_template_link.isSymLink() && !equivalent(user_template_link, package_template_path)) {
838 user_template_link.removeFile();
840 if (!user_template_link.exists()) {
841 if (!package_template_path.link(user_template_link)) {
842 FileName const user_support = package().user_support();
843 if (user_support.exists() && user_support.isDirectory()) {
844 LYXERR(Debug::INIT, "Cannot create symlink " + user_template_link.absFileName());
845 lyxrc.template_path = package_template_path.absFileName();
849 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
851 lyxrc.template_path = user_template_path.absFileName();
859 signal(SIGHUP, error_handler);
861 signal(SIGFPE, error_handler);
862 signal(SIGSEGV, error_handler);
863 signal(SIGINT, error_handler);
864 signal(SIGTERM, error_handler);
865 // SIGPIPE can be safely ignored.
867 #if defined (USE_MACOSX_PACKAGING)
868 cleanDuplicateEnvVars();
871 lyxrc.tempdir_path = package().temp_dir().absFileName();
872 lyxrc.document_path = package().document_dir().absFileName();
874 if (lyxrc.example_path.empty()) {
875 lyxrc.example_path = addPath(package().system_support().absFileName(),
880 // init LyXDir environment variable
881 string const lyx_dir = package().lyx_dir().absFileName();
882 LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
883 if (!setEnv("LyXDir", lyx_dir))
884 LYXERR(Debug::INIT, "\t... failed!");
886 if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
887 // -userdir was given on the command line.
888 // Make it available to child processes, otherwise tex2lyx
889 // would not find all layout files, and other converters might
891 string const user_dir = package().user_support().absFileName();
892 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
894 if (!setEnv(LYX_USERDIR_VER, user_dir))
895 LYXERR(Debug::INIT, "\t... failed!");
899 // Read configuration files
902 // This one may have been distributed along with LyX.
903 if (!readRcFile("lyxrc.dist"))
906 // Set the PATH correctly.
907 #if !defined (USE_POSIX_PACKAGING) && !defined (USE_HAIKU_PACKAGING)
908 // Add the directory containing the LyX executable to the path
909 // so that LyX can find things like tex2lyx.
910 if (package().build_support().empty())
911 prependEnvPath("PATH", package().binary_dir().absFileName());
914 // Add the directory containing the dt2dv and dv2dt executables to the path
916 if (!package().build_support().empty()) {
917 // dtl executables should be in the same dir ar tex2lyx
918 dtldir = package().binary_dir();
921 dtldir = FileName(addName(package().system_support().absFileName(), "extratools"));
924 string dtlexe = "dt2dv.exe";
926 string dtlexe = "dt2dv";
928 FileName const dt2dv = FileName(addName(dtldir.absFileName(), dtlexe));
930 prependEnvPath("PATH", dtldir.absFileName());
932 if (!lyxrc.path_prefix.empty())
933 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
935 // Check that user LyX directory is ok.
937 string const lock_file = package().getConfigureLockName();
938 int fd = fileLock(lock_file.c_str());
940 if (queryUserLyXDir(package().explicit_user_support())) {
941 package().reconfigureUserLyXDir("");
942 // Now the user directory is present on first start.
945 fileUnlock(fd, lock_file.c_str());
949 // No need for a splash when there is no GUI
951 // Default is to overwrite the main file during export, unless
952 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
953 if (force_overwrite == UNSPECIFIED) {
954 string const what = getEnv("LYX_FORCE_OVERWRITE");
956 force_overwrite = ALL_FILES;
957 else if (what == "none")
958 force_overwrite = NO_FILES;
960 force_overwrite = MAIN_FILE;
964 // This one is generated in user_support directory by lib/configure.py.
965 if (!readRcFile("lyxrc.defaults"))
968 // Query the OS to know what formats are viewed natively
969 theFormats().setAutoOpen();
971 // Read lyxrc.dist again to be able to override viewer auto-detection.
972 readRcFile("lyxrc.dist");
974 system_lyxrc = lyxrc;
975 theSystemFormats() = theFormats();
976 pimpl_->system_converters_ = pimpl_->converters_;
977 pimpl_->system_movers_ = pimpl_->movers_;
978 system_lcolor = lcolor;
980 // This one is edited through the preferences dialog.
981 if (!readRcFile("preferences", true))
984 // The language may have been set to someting useful through prefs
987 if (!readEncodingsFile("encodings", "unicodesymbols"))
989 if (!readLanguagesFile("languages"))
992 LYXERR(Debug::INIT, "Reading layouts...");
994 LayoutFileList::get().read();
996 theModuleList.read();
997 //... and the cite engines
998 theCiteEnginesList.read();
1000 // read keymap and ui files in batch mode as well
1001 // because InsetInfo needs to know these to produce
1002 // the correct output
1004 // Set up command definitions
1005 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
1009 pimpl_->toplevel_keymap_.read("site");
1010 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
1011 // load user bind file user.bind
1012 pimpl_->toplevel_keymap_.read("user", nullptr, KeyMap::MissingOK);
1014 if (lyxerr.debugging(Debug::LYXRC))
1017 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
1018 // Prepend path prefix a second time to take the user preferences into a account
1019 if (!lyxrc.path_prefix.empty())
1020 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
1022 FileName const document_path(lyxrc.document_path);
1023 if (document_path.exists() && document_path.isDirectory())
1024 package().document_dir() = document_path;
1026 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
1027 if (package().temp_dir().empty()) {
1028 Alert::error(_("Could not create temporary directory"),
1029 bformat(_("Could not create a temporary directory in\n"
1031 "Make sure that this path exists and is writable and try again."),
1032 from_utf8(lyxrc.tempdir_path)));
1033 // createLyXTmpDir() tries sufficiently hard to create a
1034 // usable temp dir, so the probability to come here is
1035 // close to zero. We therefore don't try to overcome this
1036 // problem with e.g. asking the user for a new path and
1037 // trying again but simply exit.
1041 LYXERR(Debug::INIT, "LyX tmp dir: `"
1042 << package().temp_dir().absFileName() << '\'');
1044 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
1045 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1047 // This must happen after package initialization and after lyxrc is
1048 // read, therefore it can't be done by a static object.
1049 ConverterCache::init();
1055 void emergencyCleanup()
1057 // what to do about tmpfiles is non-obvious. we would
1058 // like to delete any we find, but our lyxdir might
1059 // contain documents etc. which might be helpful on
1062 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1064 if (singleton_->pimpl_->lyx_server_)
1065 singleton_->pimpl_->lyx_server_->emergencyCleanup();
1066 singleton_->pimpl_->lyx_server_.reset();
1067 singleton_->pimpl_->lyx_socket_.reset();
1072 bool LyX::queryUserLyXDir(bool explicit_userdir)
1074 // Does user directory exist?
1075 FileName const sup = package().user_support();
1076 if (sup.exists() && sup.isDirectory()) {
1077 first_start = false;
1079 return configFileNeedsUpdate("lyxrc.defaults")
1080 || configFileNeedsUpdate("lyxmodules.lst")
1081 || configFileNeedsUpdate("textclass.lst")
1082 || configFileNeedsUpdate("packages.lst")
1083 || configFileNeedsUpdate("lyxciteengines.lst")
1084 || configFileNeedsUpdate("xtemplates.lst");
1087 first_start = !explicit_userdir;
1089 // If the user specified explicitly a directory, ask whether
1090 // to create it. If the user says "no", then exit.
1091 if (explicit_userdir &&
1093 _("Missing user LyX directory"),
1094 bformat(_("You have specified a non-existent user "
1095 "LyX directory, %1$s.\n"
1096 "It is needed to keep your own configuration."),
1097 from_utf8(package().user_support().absFileName())),
1099 _("&Create directory"),
1101 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1102 earlyExit(EXIT_FAILURE);
1105 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1106 from_utf8(sup.absFileName()))) << endl;
1108 if (!sup.createDirectory(0755)) {
1109 // Failed, so let's exit.
1110 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1112 earlyExit(EXIT_FAILURE);
1119 bool LyX::readRcFile(string const & name, bool check_format)
1121 LYXERR(Debug::INIT, "About to read " << name << "... ");
1123 FileName const lyxrc_path = libFileSearch(string(), name);
1124 if (lyxrc_path.empty()) {
1125 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1127 // This was the previous logic, but can it be right??
1130 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1131 bool const success = lyxrc.read(lyxrc_path, check_format);
1133 showFileError(name);
1137 // Read the languages file `name'
1138 bool LyX::readLanguagesFile(string const & name)
1140 LYXERR(Debug::INIT, "About to read " << name << "...");
1142 FileName const lang_path = libFileSearch(string(), name);
1143 if (lang_path.empty()) {
1144 showFileError(name);
1147 languages.read(lang_path);
1152 // Read the encodings file `name'
1153 bool LyX::readEncodingsFile(string const & enc_name,
1154 string const & symbols_name)
1156 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1157 << symbols_name << "...");
1159 FileName const symbols_path = libFileSearch(string(), symbols_name);
1160 if (symbols_path.empty()) {
1161 showFileError(symbols_name);
1165 FileName const enc_path = libFileSearch(string(), enc_name);
1166 if (enc_path.empty()) {
1167 showFileError(enc_name);
1170 encodings.read(enc_path, symbols_path);
1177 /// return the the number of arguments consumed
1178 typedef function<int(string const &, string const &, string &)> cmd_helper;
1180 int parse_dbg(string const & arg, string const &, string &)
1183 cout << to_utf8(_("List of supported debug flags:")) << endl;
1184 Debug::showTags(cout);
1187 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1189 lyxerr.setLevel(Debug::value(arg));
1190 Debug::showLevel(lyxerr, lyxerr.level());
1195 int parse_help(string const &, string const &, string &)
1198 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1199 "Command line switches (case sensitive):\n"
1200 "\t-help summarize LyX usage\n"
1201 "\t-userdir dir set user directory to dir\n"
1202 "\t-sysdir dir set system directory to dir\n"
1203 "\t-geometry WxH+X+Y set geometry of the main window\n"
1204 "\t-dbg feature[,feature]...\n"
1205 " select the features to debug.\n"
1206 " Type `lyx -dbg' to see the list of features\n"
1207 "\t-x [--execute] command\n"
1208 " where command is a lyx command.\n"
1209 "\t-e [--export] fmt\n"
1210 " where fmt is the export format of choice. Look in\n"
1211 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1212 " to see which parameter (which differs from the format name\n"
1213 " in the File->Export menu) should be passed. To export to\n"
1214 " the document's default output format, use 'default'.\n"
1215 " Note that the order of -e and -x switches matters.\n"
1216 "\t-E [--export-to] fmt filename\n"
1217 " where fmt is the export format of choice (see --export),\n"
1218 " and filename is the destination filename.\n"
1219 "\t-i [--import] fmt file.xxx\n"
1220 " where fmt is the import format of choice\n"
1221 " and file.xxx is the file to be imported.\n"
1222 "\t-f [--force-overwrite] what\n"
1223 " where what is either `all', `main' or `none',\n"
1224 " specifying whether all files, main file only, or no files,\n"
1225 " respectively, are to be overwritten during a batch export.\n"
1226 " Anything else is equivalent to `all', but is not consumed.\n"
1227 "\t--ignore-error-message which\n"
1228 " allows you to ignore specific LaTeX error messages.\n"
1229 " Do not use for final documents! Currently supported values:\n"
1230 " * missing_glyphs: Fontspec `missing glyphs' error.\n"
1231 "\t-n [--no-remote]\n"
1232 " open documents in a new instance\n"
1234 " open documents in an already running instance\n"
1235 " (a working lyxpipe is needed)\n"
1236 "\t-v [--verbose]\n"
1237 " report on terminal about spawned commands.\n"
1238 "\t-batch execute commands without launching GUI and exit.\n"
1239 "\t-version summarize version and build info\n"
1240 "Check the LyX man page for more details.")) << endl;
1246 int parse_version(string const &, string const &, string &)
1248 cout << "LyX " << lyx_version
1249 << " (" << lyx_release_date << ")" << endl;
1250 if (string(lyx_git_commit_hash) != "none")
1251 cout << to_utf8(_(" Git commit hash "))
1252 << string(lyx_git_commit_hash).substr(0,8) << endl;
1253 cout << lyx_version_info << endl;
1259 int parse_sysdir(string const & arg, string const &, string &)
1262 Alert::error(_("No system directory"),
1263 _("Missing directory for -sysdir switch"));
1266 cl_system_support = arg;
1271 int parse_userdir(string const & arg, string const &, string &)
1274 Alert::error(_("No user directory"),
1275 _("Missing directory for -userdir switch"));
1278 cl_user_support = arg;
1283 int parse_execute(string const & arg, string const &, string & batch)
1286 Alert::error(_("Incomplete command"),
1287 _("Missing command string after --execute switch"));
1295 int parse_export_to(string const & type, string const & output_file, string & batch)
1298 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1299 "--export-to switch")) << endl;
1302 if (output_file.empty()) {
1303 lyxerr << to_utf8(_("Missing destination filename after "
1304 "--export-to switch")) << endl;
1307 batch = "buffer-export " + type + " " + output_file;
1313 int parse_export(string const & type, string const &, string & batch)
1316 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1317 "--export switch")) << endl;
1320 batch = "buffer-export " + type;
1326 int parse_import(string const & type, string const & file, string & batch)
1329 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1330 "--import switch")) << endl;
1334 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1337 batch = "buffer-import " + type + ' ' + file;
1342 int parse_geometry(string const & arg1, string const &, string &)
1345 // don't remove "-geometry", it will be pruned out later in the
1346 // frontend if need be.
1351 int parse_batch(string const &, string const &, string &)
1358 int parse_noremote(string const &, string const &, string &)
1360 run_mode = NEW_INSTANCE;
1365 int parse_remote(string const &, string const &, string &)
1367 run_mode = USE_REMOTE;
1372 int parse_verbose(string const &, string const &, string &)
1379 int parse_ignore_error_message(string const & arg1, string const &, string &)
1381 if (arg1 == "missing_glyphs") {
1382 ignore_missing_glyphs = true;
1389 int parse_force(string const & arg, string const &, string &)
1392 force_overwrite = ALL_FILES;
1394 } else if (arg == "main") {
1395 force_overwrite = MAIN_FILE;
1397 } else if (arg == "none") {
1398 force_overwrite = NO_FILES;
1401 force_overwrite = ALL_FILES;
1409 void LyX::easyParse(int & argc, char * argv[])
1411 map<string, cmd_helper> cmdmap;
1413 cmdmap["-dbg"] = parse_dbg;
1414 cmdmap["-help"] = parse_help;
1415 cmdmap["--help"] = parse_help;
1416 cmdmap["-version"] = parse_version;
1417 cmdmap["--version"] = parse_version;
1418 cmdmap["-sysdir"] = parse_sysdir;
1419 cmdmap["-userdir"] = parse_userdir;
1420 cmdmap["-x"] = parse_execute;
1421 cmdmap["--execute"] = parse_execute;
1422 cmdmap["-e"] = parse_export;
1423 cmdmap["--export"] = parse_export;
1424 cmdmap["-E"] = parse_export_to;
1425 cmdmap["--export-to"] = parse_export_to;
1426 cmdmap["-i"] = parse_import;
1427 cmdmap["--import"] = parse_import;
1428 cmdmap["-geometry"] = parse_geometry;
1429 cmdmap["-batch"] = parse_batch;
1430 cmdmap["-f"] = parse_force;
1431 cmdmap["--force-overwrite"] = parse_force;
1432 cmdmap["-n"] = parse_noremote;
1433 cmdmap["--no-remote"] = parse_noremote;
1434 cmdmap["-r"] = parse_remote;
1435 cmdmap["--remote"] = parse_remote;
1436 cmdmap["-v"] = parse_verbose;
1437 cmdmap["--verbose"] = parse_verbose;
1438 cmdmap["--ignore-error-message"] = parse_ignore_error_message;
1440 for (int i = 1; i < argc; ++i) {
1441 map<string, cmd_helper>::const_iterator it
1442 = cmdmap.find(argv[i]);
1444 // don't complain if not found - may be parsed later
1445 if (it == cmdmap.end())
1449 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1451 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1454 int const remove = 1 + it->second(arg, arg2, batch);
1456 pimpl_->batch_commands.push_back(batch);
1458 // Now, remove used arguments by shifting
1459 // the following ones remove places down.
1461 os::remove_internal_args(i, remove);
1463 for (int j = i; j < argc; ++j)
1464 argv[j] = argv[j + remove];
1471 FuncStatus getStatus(FuncRequest const & action)
1474 return theApp()->getStatus(action);
1478 DispatchResult const & dispatch(FuncRequest const & action)
1481 return theApp()->dispatch(action);
1485 void dispatch(FuncRequest const & action, DispatchResult & dr)
1488 theApp()->dispatch(action, dr);
1492 vector<string> & theFilesToLoad()
1494 LAPPERR(singleton_);
1495 return singleton_->pimpl_->files_to_load_;
1499 BufferList & theBufferList()
1501 LAPPERR(singleton_);
1502 return singleton_->pimpl_->buffer_list_;
1506 Server & theServer()
1508 // FIXME: this should not be use_gui dependent
1510 LAPPERR(singleton_);
1511 return *singleton_->pimpl_->lyx_server_;
1515 ServerSocket & theServerSocket()
1517 // FIXME: this should not be use_gui dependent
1519 LAPPERR(singleton_);
1520 return *singleton_->pimpl_->lyx_socket_;
1524 KeyMap & theTopLevelKeymap()
1526 LAPPERR(singleton_);
1527 return singleton_->pimpl_->toplevel_keymap_;
1531 Formats & theFormats()
1533 LAPPERR(singleton_);
1534 return singleton_->pimpl_->formats_;
1538 Formats & theSystemFormats()
1540 LAPPERR(singleton_);
1541 return singleton_->pimpl_->system_formats_;
1545 Converters & theConverters()
1547 LAPPERR(singleton_);
1548 return singleton_->pimpl_->converters_;
1552 Converters & theSystemConverters()
1554 LAPPERR(singleton_);
1555 return singleton_->pimpl_->system_converters_;
1559 Movers & theMovers()
1561 LAPPERR(singleton_);
1562 return singleton_->pimpl_->movers_;
1566 Mover const & getMover(string const & fmt)
1568 LAPPERR(singleton_);
1569 return singleton_->pimpl_->movers_(fmt);
1573 void setMover(string const & fmt, string const & command)
1575 LAPPERR(singleton_);
1576 singleton_->pimpl_->movers_.set(fmt, command);
1580 Movers & theSystemMovers()
1582 LAPPERR(singleton_);
1583 return singleton_->pimpl_->system_movers_;
1587 Messages const & getMessages(string const & language)
1589 LAPPERR(singleton_);
1590 return singleton_->messages(language);
1594 Messages const & getGuiMessages()
1596 LAPPERR(singleton_);
1597 return singleton_->messages(Messages::guiLanguage());
1601 Session & theSession()
1603 LAPPERR(singleton_);
1604 return *singleton_->pimpl_->session_.get();
1608 LaTeXFonts & theLaTeXFonts()
1610 LAPPERR(singleton_);
1611 if (!singleton_->pimpl_->latexfonts_)
1612 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1613 return *singleton_->pimpl_->latexfonts_;
1617 CmdDef & theTopLevelCmdDef()
1619 LAPPERR(singleton_);
1620 return singleton_->pimpl_->toplevel_cmddef_;
1624 SpellChecker * theSpellChecker()
1626 if (!singleton_->pimpl_->spell_checker_)
1628 return singleton_->pimpl_->spell_checker_;
1632 void setSpellChecker()
1634 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1635 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1637 if (lyxrc.spellchecker == "native") {
1638 #if defined(USE_MACOSX_PACKAGING)
1639 if (!singleton_->pimpl_->apple_spell_checker_)
1640 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1641 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1643 singleton_->pimpl_->spell_checker_ = nullptr;
1645 } else if (lyxrc.spellchecker == "aspell") {
1646 #if defined(USE_ASPELL)
1647 if (!singleton_->pimpl_->aspell_checker_)
1648 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1649 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1651 singleton_->pimpl_->spell_checker_ = nullptr;
1653 } else if (lyxrc.spellchecker == "enchant") {
1654 #if defined(USE_ENCHANT)
1655 if (!singleton_->pimpl_->enchant_checker_)
1656 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1657 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1659 singleton_->pimpl_->spell_checker_ = nullptr;
1661 } else if (lyxrc.spellchecker == "hunspell") {
1662 #if defined(USE_HUNSPELL)
1663 if (!singleton_->pimpl_->hunspell_checker_)
1664 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1665 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1667 singleton_->pimpl_->spell_checker_ = nullptr;
1670 singleton_->pimpl_->spell_checker_ = nullptr;
1672 if (singleton_->pimpl_->spell_checker_) {
1673 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1674 singleton_->pimpl_->spell_checker_->advanceChangeNumber();