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"
41 #include "LyXAction.h"
43 #include "ModuleList.h"
46 #include "ServerSocket.h"
49 #include "frontends/alert.h"
50 #include "frontends/Application.h"
52 #include "support/ConsoleApplication.h"
53 #include "support/convert.h"
54 #include "support/lassert.h"
55 #include "support/debug.h"
56 #include "support/environment.h"
57 #include "support/ExceptionMessage.h"
58 #include "support/filetools.h"
59 #include "support/gettext.h"
60 #include "support/lstrings.h"
61 #include "support/Messages.h"
62 #include "support/os.h"
63 #include "support/Package.h"
64 #include "support/unique_ptr.h"
74 #include <qglobal.h> // For QT_VERSION
77 using namespace lyx::support;
79 #if defined (USE_MACOSX_PACKAGING)
80 #include <crt_externs.h>
85 namespace Alert = frontend::Alert;
86 namespace os = support::os;
90 // Are we using the GUI at all? We default to true and this is changed
91 // to false when the export feature is used.
96 // Report on the terminal about spawned commands. The default is false
97 // and can be changed with the option -v (--verbose).
102 // Do not treat the "missing glyphs" warning of fontspec as an error message.
103 // The default is false and can be changed with the option
104 // --ignore-error-message missing_glyphs
105 // This is used in automated testing.
106 bool ignore_missing_glyphs = false;
109 // We default to open documents in an already running instance, provided that
110 // the lyxpipe has been setup. This can be overridden either on the command
111 // line or through preference settings.
113 RunMode run_mode = PREFERRED;
116 // Tell what files can be silently overwritten during batch export.
117 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
118 // Unless specified on command line (through the -f switch) or through the
119 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
121 OverwriteFiles force_overwrite = UNSPECIFIED;
124 // Scale the GUI by this factor. This works whether we have a HiDpi screen
125 // or not and scales everything, also fonts. Can only be changed by setting
126 // the QT_SCALE_FACTOR environment variable before launching LyX and only
127 // works properly with Qt 5.6 or higher.
129 double qt_scale_factor = 1.0;
134 // Filled with the command line arguments "foo" of "-sysdir foo" or
136 string cl_system_support;
137 string cl_user_support;
141 LyX * singleton_ = nullptr;
143 void showFileError(string const & error)
145 Alert::warning(_("Could not read configuration file"),
146 bformat(_("Error while reading the configuration file\n%1$s.\n"
147 "Please check your installation."), from_utf8(error)));
152 /// The main application class private implementation.
155 : latexfonts_(nullptr), spell_checker_(nullptr),
156 apple_spell_checker_(nullptr), aspell_checker_(nullptr),
157 enchant_checker_(nullptr), hunspell_checker_(nullptr)
163 delete apple_spell_checker_;
164 delete aspell_checker_;
165 delete enchant_checker_;
166 delete hunspell_checker_;
170 BufferList buffer_list_;
172 KeyMap toplevel_keymap_;
174 CmdDef toplevel_cmddef_;
176 unique_ptr<Server> lyx_server_;
178 unique_ptr<ServerSocket> lyx_socket_;
180 unique_ptr<frontend::Application> application_;
181 /// lyx session, containing lastfiles, lastfilepos, and lastopened
182 unique_ptr<Session> session_;
184 /// Files to load at start.
185 vector<string> files_to_load_;
187 /// The messages translators.
188 map<string, Messages> messages_;
190 /// The file converters.
191 Converters converters_;
192 /// The system converters after reading lyxrc.defaults.
193 Converters system_converters_;
195 /// Global format information
197 /// The system formats after reading lyxrc.defaults.
198 Formats system_formats_;
204 Movers system_movers_;
206 /// the parsed command line batch command if any
207 vector<string> batch_commands;
210 LaTeXFonts * latexfonts_;
213 SpellChecker * spell_checker_;
215 SpellChecker * apple_spell_checker_;
217 SpellChecker * aspell_checker_;
219 SpellChecker * enchant_checker_;
221 SpellChecker * hunspell_checker_;
225 /// The main application class for console mode
226 class LyXConsoleApp : public ConsoleApplication
229 LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
230 : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
231 argc_(argc), argv_(argv)
234 void doExec() override
236 int const exit_status = lyx_->execWithoutGui(argc_, argv_);
247 frontend::Application * theApp()
250 return singleton_->pimpl_->application_.get();
259 singleton_ = nullptr;
263 void lyx_exit(int exit_code)
266 // Something wrong happened so better save everything, just in
271 // Properly crash in debug mode in order to get a useful backtrace.
275 // In release mode, try to exit gracefully.
277 theApp()->exit(exit_code);
291 Messages & LyX::messages(string const & language)
293 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
295 if (it != pimpl_->messages_.end())
298 pair<map<string, Messages>::iterator, bool> result =
299 pimpl_->messages_.insert(make_pair(language, Messages(language)));
301 LATTEST(result.second);
302 return result.first->second;
306 int LyX::exec(int & argc, char * argv[])
308 // Minimal setting of locale before parsing command line
310 init_package(os::utf8_argv(0), string(), string());
311 // we do not get to this point when init_package throws an exception
313 } catch (ExceptionMessage const & message) {
314 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
317 // Here we need to parse the command line. At least
318 // we need to parse for "-dbg" and "-help"
319 easyParse(argc, argv);
321 #if QT_VERSION >= 0x050600
322 // Check whether Qt will scale all GUI elements and accordingly
323 // set the scale factor so that to avoid blurred images and text
324 char const * const scale_factor = getenv("QT_SCALE_FACTOR");
326 qt_scale_factor = convert<double>(scale_factor);
327 if (qt_scale_factor < 1.0)
328 qt_scale_factor = 1.0;
333 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
334 } catch (ExceptionMessage const & message) {
335 if (message.type_ == ErrorException) {
336 Alert::error(message.title_, message.details_);
338 } else if (message.type_ == WarningException) {
339 Alert::warning(message.title_, message.details_);
343 // Reinit the messages machinery in case package() knows
344 // something interesting about the locale directory.
348 LyXConsoleApp app(this, argc, argv);
350 // Reestablish our defaults, as Qt overwrites them
351 // after creating app
357 // Let the frontend parse and remove all arguments that it knows
358 pimpl_->application_.reset(createApplication(argc, argv));
360 // Reestablish our defaults, as Qt overwrites them
361 // after createApplication()
364 // Parse and remove all known arguments in the LyX singleton
365 // Give an error for all remaining ones.
366 int exit_status = init(argc, argv);
368 // Kill the application object before exiting.
369 pimpl_->application_.reset();
375 // If not otherwise specified by a command line option or
376 // by preferences, we default to reuse a running instance.
377 if (run_mode == PREFERRED)
378 run_mode = USE_REMOTE;
381 /* Create a CoreApplication class that will provide the main event loop
382 * and the socket callback registering. With Qt, only QtCore
383 * library would be needed.
384 * When this is done, a server_mode could be created and the following two
385 * line would be moved out from here.
386 * However, note that the first of the two lines below triggers the
387 * "single instance" behavior, which should occur right at this point.
389 // Note: socket callback must be registered after init(argc, argv)
390 // such that package().temp_dir() is properly initialized.
391 pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
392 pimpl_->lyx_socket_.reset(new ServerSocket(
393 FileName(package().temp_dir().absFileName() + "/lyxsocket")));
395 // Start the real execution loop.
396 if (!pimpl_->lyx_server_->deferredLoadingToOtherInstance())
397 exit_status = pimpl_->application_->exec();
398 else if (!pimpl_->files_to_load_.empty()) {
399 lyxerr << _("The following files could not be loaded:") << endl;
400 for (auto const & f : pimpl_->files_to_load_)
410 void LyX::prepareExit()
412 // Clear the clipboard and selection stack:
413 cap::clearCutStack();
414 cap::clearSelection();
416 // Write the index file of the converter cache
417 ConverterCache::get().writeIndex();
419 // closing buffer may throw exceptions, but we ignore them since we
422 // close buffers first
423 pimpl_->buffer_list_.closeAll();
424 } catch (ExceptionMessage const &) {}
426 // register session changes and shutdown server and socket
428 if (pimpl_->session_)
429 pimpl_->session_->writeFile();
430 pimpl_->session_.reset();
431 pimpl_->lyx_server_.reset();
432 pimpl_->lyx_socket_.reset();
435 // do any other cleanup procedures now
436 if (package().temp_dir() != package().system_temp_dir()) {
437 string const abs_tmpdir = package().temp_dir().absFileName();
438 if (!contains(abs_tmpdir, "lyx_tmpdir")) {
439 docstring const msg =
440 bformat(_("%1$s does not appear like a LyX created temporary directory."),
441 from_utf8(abs_tmpdir));
442 Alert::warning(_("Cannot remove temporary directory"), msg);
444 LYXERR(Debug::INFO, "Deleting tmp dir " << abs_tmpdir);
445 if (!package().temp_dir().destroyDirectory()) {
446 LYXERR0(bformat(_("Unable to remove the temporary directory %1$s"),
447 from_utf8(abs_tmpdir)));
452 // Kill the application object before exiting. This avoids crashes
453 // when exiting on Linux.
454 pimpl_->application_.reset();
458 void LyX::earlyExit(int status)
460 LATTEST(pimpl_->application_.get());
461 // LyX::pimpl_::application_ is not initialised at this
462 // point so it's safe to just exit after some cleanup.
468 int LyX::init(int & argc, char * argv[])
470 // check for any spurious extra arguments
471 // other than documents
472 for (int argi = 1; argi < argc ; ++argi) {
473 if (argv[argi][0] == '-') {
475 bformat(_("Wrong command line option `%1$s'. Exiting."),
476 from_utf8(os::utf8_argv(argi)))) << endl;
481 // Initialization of LyX (reads lyxrc and more)
482 LYXERR(Debug::INIT, "Initializing LyX::init...");
483 bool success = init();
484 LYXERR(Debug::INIT, "Initializing LyX::init...done");
488 // Remaining arguments are assumed to be files to load.
489 for (int argi = 1; argi < argc; ++argi)
490 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
492 if (!use_gui && pimpl_->files_to_load_.empty()) {
493 lyxerr << to_utf8(_("Missing filename for this operation.")) << endl;
498 pimpl_->files_to_load_.push_back(
499 i18nLibFileSearch("examples", "Welcome.lyx").absFileName());
506 int LyX::execWithoutGui(int & argc, char * argv[])
508 int exit_status = init(argc, argv);
514 // Used to keep track of which buffers were explicitly loaded by user request.
515 // This is necessary because master and child document buffers are loaded, even
516 // if they were not named on the command line. We do not want to dispatch to
518 vector<Buffer *> command_line_buffers;
520 // Load the files specified on the command line
521 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
522 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
523 for (; it != end; ++it) {
524 // get absolute path of file and add ".lyx" to the filename if necessary
525 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
531 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName());
532 LYXERR(Debug::FILES, "Loading " << fname);
533 if (buf && buf->loadLyXFile() == Buffer::ReadSuccess) {
534 ErrorList const & el = buf->errorList("Parse");
535 for(ErrorItem const & e : el)
537 command_line_buffers.push_back(buf);
540 pimpl_->buffer_list_.release(buf);
541 docstring const error_message =
542 bformat(_("LyX failed to load the following file: %1$s"),
543 from_utf8(fname.absFileName()));
544 lyxerr << to_utf8(error_message) << endl;
545 exit_status = 1; // failed
549 if (exit_status || pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
554 // Iterate through the buffers that were specified on the command line
555 bool final_success = false;
556 vector<Buffer *>::iterator buf_it = command_line_buffers.begin();
557 for (; buf_it != command_line_buffers.end(); ++buf_it) {
558 Buffer * buf = *buf_it;
559 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
560 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
562 for (; bcit != bcend; ++bcit) {
563 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
564 buf->dispatch(*bcit, dr);
565 final_success |= !dr.error();
569 return !final_success;
573 void execBatchCommands()
576 singleton_->execCommands();
580 void LyX::execCommands()
582 // The advantage of doing this here is that the event loop
583 // is already started. So any need for interaction will be
586 // if reconfiguration is needed.
587 if (LayoutFileList::get().empty()) {
588 switch (Alert::prompt(
589 _("No textclass is found"),
590 _("LyX will only have minimal functionality because no textclasses "
591 "have been found. You can either try to reconfigure LyX normally, "
592 "try to reconfigure without checking your LaTeX installation, or continue."),
599 // regular reconfigure
600 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
603 // reconfigure --without-latex-config
604 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
605 " --without-latex-config"));
612 // create the first main window
613 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
615 if (!pimpl_->files_to_load_.empty()) {
616 // if some files were specified at command-line we assume that the
617 // user wants to edit *these* files and not to restore the session.
618 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
620 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
622 // clear this list to save a few bytes of RAM
623 pimpl_->files_to_load_.clear();
625 pimpl_->application_->restoreGuiSession();
627 // Execute batch commands if available
628 if (pimpl_->batch_commands.empty())
631 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
632 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
633 for (; bcit != bcend; ++bcit) {
634 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
635 lyx::dispatch(lyxaction.lookupFunc(*bcit));
643 The SIGHUP signal does not exist on Windows and does not need to be handled.
645 Windows handles SIGFPE and SIGSEGV signals as expected.
647 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
648 cause a new thread to be spawned. This may well result in unexpected
649 behaviour by the single-threaded LyX.
651 SIGTERM signals will come only from another process actually sending
652 that signal using 'raise' in Windows' POSIX compatability layer. It will
653 not come from the general "terminate process" methods that everyone
654 actually uses (and which can't be trapped). Killing an app 'politely' on
655 Windows involves first sending a WM_CLOSE message, something that is
656 caught already by the Qt frontend.
658 For more information see:
660 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
661 ...signals are mostly useless on Windows for a variety of reasons that are
664 'UNIX Application Migration Guide, Chapter 9'
665 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
667 'How To Terminate an Application "Cleanly" in Win32'
668 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
672 static void error_handler(int err_sig)
674 // Throw away any signals other than the first one received.
675 static sig_atomic_t handling_error = false;
678 handling_error = true;
680 // We have received a signal indicating a fatal error, so
681 // try and save the data ASAP.
684 // These lyxerr calls may or may not work:
686 // Signals are asynchronous, so the main program may be in a very
687 // fragile state when a signal is processed and thus while a signal
688 // handler function executes.
689 // In general, therefore, we should avoid performing any
690 // I/O operations or calling most library and system functions from
693 // This shouldn't matter here, however, as we've already invoked
699 msg = _("SIGHUP signal caught!\nBye.");
703 msg = _("SIGFPE signal caught!\nBye.");
706 msg = _("SIGSEGV signal caught!\n"
707 "Sorry, you have found a bug in LyX, "
708 "hope you have not lost any data.\n"
709 "Please read the bug-reporting instructions "
710 "in 'Help->Introduction' and send us a bug report, "
711 "if necessary. Thanks!\nBye.");
720 lyxerr << "\nlyx: " << msg << endl;
721 // try to make a GUI message
722 Alert::error(_("LyX crashed!"), msg, true);
725 // Deinstall the signal handlers
727 signal(SIGHUP, SIG_DFL);
729 signal(SIGINT, SIG_DFL);
730 signal(SIGFPE, SIG_DFL);
731 signal(SIGSEGV, SIG_DFL);
732 signal(SIGTERM, SIG_DFL);
735 if (err_sig == SIGSEGV ||
736 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
738 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
741 // with abort() it crashes again.
754 void LyX::printError(ErrorItem const & ei)
756 docstring tmp = _("LyX: ") + ei.error + char_type(':')
758 cerr << to_utf8(tmp) << endl;
761 #if defined (USE_MACOSX_PACKAGING)
763 // Unexposed--extract an environment variable name from its NAME=VALUE
765 std::string varname(const char* line)
767 size_t nameLen = strcspn(line, "=");
768 if (nameLen == strlen(line)) {
769 return std::string();
771 return std::string(line, nameLen);
776 void cleanDuplicateEnvVars()
778 std::set<std::string> seen;
779 std::set<std::string> dupes;
781 // Create a list of the environment variables that appear more than once
782 for (char **read = *_NSGetEnviron(); *read; read++) {
783 std::string name = varname(*read);
784 if (name.size() == 0) {
787 if (seen.find(name) != seen.end()) {
794 // Loop over the list of duplicated variables
795 std::set<std::string>::iterator dupe = dupes.begin();
796 std::set<std::string>::iterator const dend = dupes.end();
797 for (; dupe != dend; ++dupe) {
798 const char *name = (*dupe).c_str();
799 char *val = getenv(name);
801 LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
802 // unsetenv removes *all* instances of the variable from the environment
805 // replace with the value from getenv (in practice appears to be the
806 // first value in the list)
807 setenv(name, val, 0);
814 static void initTemplatePath()
816 FileName const package_template_path =
817 FileName(addName(package().system_support().absFileName(), "templates"));
819 if (lyxrc.template_path.empty()) {
820 lyxrc.template_path = package_template_path.absFileName();
822 #if defined (USE_MACOSX_PACKAGING)
823 FileName const user_template_path =
824 FileName(addName(package().user_support().absFileName(), "templates"));
826 if (package_template_path != FileName(lyxrc.template_path) &&
827 user_template_path != FileName(lyxrc.template_path))
831 FileName const user_template_link =
832 FileName(addName(user_template_path.absFileName(),"SystemTemplates"));
833 if (user_template_link.isSymLink() && !equivalent(user_template_link, package_template_path)) {
834 user_template_link.removeFile();
836 if (!user_template_link.exists()) {
837 if (!package_template_path.link(user_template_link)) {
838 FileName const user_support = package().user_support();
839 if (user_support.exists() && user_support.isDirectory()) {
840 LYXERR(Debug::INIT, "Cannot create symlink " + user_template_link.absFileName());
841 lyxrc.template_path = package_template_path.absFileName();
845 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
847 lyxrc.template_path = user_template_path.absFileName();
855 signal(SIGHUP, error_handler);
857 signal(SIGFPE, error_handler);
858 signal(SIGSEGV, error_handler);
859 signal(SIGINT, error_handler);
860 signal(SIGTERM, error_handler);
861 // SIGPIPE can be safely ignored.
863 #if defined (USE_MACOSX_PACKAGING)
864 cleanDuplicateEnvVars();
867 lyxrc.tempdir_path = package().temp_dir().absFileName();
868 lyxrc.document_path = package().document_dir().absFileName();
870 if (lyxrc.example_path.empty()) {
871 lyxrc.example_path = addPath(package().system_support().absFileName(),
876 // init LyXDir environment variable
877 string const lyx_dir = package().lyx_dir().absFileName();
878 LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
879 if (!setEnv("LyXDir", lyx_dir))
880 LYXERR(Debug::INIT, "\t... failed!");
882 if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
883 // -userdir was given on the command line.
884 // Make it available to child processes, otherwise tex2lyx
885 // would not find all layout files, and other converters might
887 string const user_dir = package().user_support().absFileName();
888 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
890 if (!setEnv(LYX_USERDIR_VER, user_dir))
891 LYXERR(Debug::INIT, "\t... failed!");
895 // Read configuration files
898 // This one may have been distributed along with LyX.
899 if (!readRcFile("lyxrc.dist"))
902 // Set the PATH correctly.
903 #if !defined (USE_POSIX_PACKAGING) && !defined (USE_HAIKU_PACKAGING)
904 // Add the directory containing the LyX executable to the path
905 // so that LyX can find things like tex2lyx.
906 if (package().build_support().empty())
907 prependEnvPath("PATH", package().binary_dir().absFileName());
910 // Add the directory containing the dt2dv and dv2dt executables to the path
912 if (!package().build_support().empty()) {
913 // dtl executables should be in the same dir ar tex2lyx
914 dtldir = package().binary_dir();
917 dtldir = FileName(addName(package().system_support().absFileName(), "extratools"));
920 string dtlexe = "dt2dv.exe";
922 string dtlexe = "dt2dv";
924 FileName const dt2dv = FileName(addName(dtldir.absFileName(), dtlexe));
926 prependEnvPath("PATH", dtldir.absFileName());
928 if (!lyxrc.path_prefix.empty())
929 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
931 // Check that user LyX directory is ok.
933 string const lock_file = package().getConfigureLockName();
934 int fd = fileLock(lock_file.c_str());
936 if (queryUserLyXDir(package().explicit_user_support())) {
937 package().reconfigureUserLyXDir("");
938 // Now the user directory is present on first start.
941 fileUnlock(fd, lock_file.c_str());
945 // No need for a splash when there is no GUI
947 // Default is to overwrite the main file during export, unless
948 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
949 if (force_overwrite == UNSPECIFIED) {
950 string const what = getEnv("LYX_FORCE_OVERWRITE");
952 force_overwrite = ALL_FILES;
953 else if (what == "none")
954 force_overwrite = NO_FILES;
956 force_overwrite = MAIN_FILE;
960 // This one is generated in user_support directory by lib/configure.py.
961 if (!readRcFile("lyxrc.defaults"))
964 // Query the OS to know what formats are viewed natively
965 theFormats().setAutoOpen();
967 // Read lyxrc.dist again to be able to override viewer auto-detection.
968 readRcFile("lyxrc.dist");
970 system_lyxrc = lyxrc;
971 theSystemFormats() = theFormats();
972 pimpl_->system_converters_ = pimpl_->converters_;
973 pimpl_->system_movers_ = pimpl_->movers_;
974 system_lcolor = lcolor;
976 // This one is edited through the preferences dialog.
977 if (!readRcFile("preferences", true))
980 // The language may have been set to someting useful through prefs
983 if (!readEncodingsFile("encodings", "unicodesymbols"))
985 if (!readLanguagesFile("languages"))
988 LYXERR(Debug::INIT, "Reading layouts...");
990 LayoutFileList::get().read();
992 theModuleList.read();
993 //... and the cite engines
994 theCiteEnginesList.read();
996 // read keymap and ui files in batch mode as well
997 // because InsetInfo needs to know these to produce
998 // the correct output
1000 // Set up command definitions
1001 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
1005 pimpl_->toplevel_keymap_.read("site");
1006 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
1007 // load user bind file user.bind
1008 pimpl_->toplevel_keymap_.read("user", nullptr, KeyMap::MissingOK);
1010 if (lyxerr.debugging(Debug::LYXRC))
1013 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
1014 // Prepend path prefix a second time to take the user preferences into a account
1015 if (!lyxrc.path_prefix.empty())
1016 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
1018 FileName const document_path(lyxrc.document_path);
1019 if (document_path.exists() && document_path.isDirectory())
1020 package().document_dir() = document_path;
1022 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
1023 if (package().temp_dir().empty()) {
1024 Alert::error(_("Could not create temporary directory"),
1025 bformat(_("Could not create a temporary directory in\n"
1027 "Make sure that this path exists and is writable and try again."),
1028 from_utf8(lyxrc.tempdir_path)));
1029 // createLyXTmpDir() tries sufficiently hard to create a
1030 // usable temp dir, so the probability to come here is
1031 // close to zero. We therefore don't try to overcome this
1032 // problem with e.g. asking the user for a new path and
1033 // trying again but simply exit.
1037 LYXERR(Debug::INIT, "LyX tmp dir: `"
1038 << package().temp_dir().absFileName() << '\'');
1040 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
1041 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1043 // This must happen after package initialization and after lyxrc is
1044 // read, therefore it can't be done by a static object.
1045 ConverterCache::init();
1051 void emergencyCleanup()
1053 // what to do about tmpfiles is non-obvious. we would
1054 // like to delete any we find, but our lyxdir might
1055 // contain documents etc. which might be helpful on
1058 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1060 if (singleton_->pimpl_->lyx_server_)
1061 singleton_->pimpl_->lyx_server_->emergencyCleanup();
1062 singleton_->pimpl_->lyx_server_.reset();
1063 singleton_->pimpl_->lyx_socket_.reset();
1068 bool LyX::queryUserLyXDir(bool explicit_userdir)
1070 // Does user directory exist?
1071 FileName const sup = package().user_support();
1072 if (sup.exists() && sup.isDirectory()) {
1073 first_start = false;
1075 return configFileNeedsUpdate("lyxrc.defaults")
1076 || configFileNeedsUpdate("lyxmodules.lst")
1077 || configFileNeedsUpdate("textclass.lst")
1078 || configFileNeedsUpdate("packages.lst")
1079 || configFileNeedsUpdate("lyxciteengines.lst")
1080 || configFileNeedsUpdate("xtemplates.lst");
1083 first_start = !explicit_userdir;
1085 // If the user specified explicitly a directory, ask whether
1086 // to create it. If the user says "no", then exit.
1087 if (explicit_userdir &&
1089 _("Missing user LyX directory"),
1090 bformat(_("You have specified a non-existent user "
1091 "LyX directory, %1$s.\n"
1092 "It is needed to keep your own configuration."),
1093 from_utf8(package().user_support().absFileName())),
1095 _("&Create directory"),
1097 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1098 earlyExit(EXIT_FAILURE);
1101 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1102 from_utf8(sup.absFileName()))) << endl;
1104 if (!sup.createDirectory(0755)) {
1105 // Failed, so let's exit.
1106 lyxerr << to_utf8(_("Failed to create directory. Perhaps wrong -userdir option?\nExiting."))
1108 earlyExit(EXIT_FAILURE);
1115 bool LyX::readRcFile(string const & name, bool check_format)
1117 LYXERR(Debug::INIT, "About to read " << name << "... ");
1119 FileName const lyxrc_path = libFileSearch(string(), name);
1120 if (lyxrc_path.empty()) {
1121 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1123 // This was the previous logic, but can it be right??
1126 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1127 bool const success = lyxrc.read(lyxrc_path, check_format);
1129 showFileError(name);
1133 // Read the languages file `name'
1134 bool LyX::readLanguagesFile(string const & name)
1136 LYXERR(Debug::INIT, "About to read " << name << "...");
1138 FileName const lang_path = libFileSearch(string(), name);
1139 if (lang_path.empty()) {
1140 showFileError(name);
1143 languages.read(lang_path);
1148 // Read the encodings file `name'
1149 bool LyX::readEncodingsFile(string const & enc_name,
1150 string const & symbols_name)
1152 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1153 << symbols_name << "...");
1155 FileName const symbols_path = libFileSearch(string(), symbols_name);
1156 if (symbols_path.empty()) {
1157 showFileError(symbols_name);
1161 FileName const enc_path = libFileSearch(string(), enc_name);
1162 if (enc_path.empty()) {
1163 showFileError(enc_name);
1166 encodings.read(enc_path, symbols_path);
1173 /// return the the number of arguments consumed
1174 typedef function<int(string const &, string const &, string &)> cmd_helper;
1176 int parse_dbg(string const & arg, string const &, string &)
1179 cout << to_utf8(_("List of supported debug flags:")) << endl;
1180 Debug::showTags(cout);
1183 string bad = Debug::badValue(arg);
1185 lyxerr.setLevel(Debug::value(arg));
1186 Debug::showLevel(lyxerr, lyxerr.level());
1188 cout << to_utf8(bformat(_("Bad debug value `%1$s'. Exiting."),
1189 from_utf8(bad))) << endl;
1196 int parse_help(string const &, string const &, string &)
1199 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1200 "Command line switches (case sensitive):\n"
1201 "\t-help summarize LyX usage\n"
1202 "\t-userdir dir set user directory to dir\n"
1203 "\t-sysdir dir set system directory to dir\n"
1204 "\t-geometry WxH+X+Y set geometry of the main window\n"
1205 "\t-dbg feature[,feature]...\n"
1206 " select the features to debug.\n"
1207 " Type `lyx -dbg' to see the list of features\n"
1208 "\t-x [--execute] command\n"
1209 " where command is a lyx command.\n"
1210 "\t-e [--export] fmt\n"
1211 " where fmt is the export format of choice. Look in\n"
1212 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1213 " to see which parameter (which differs from the format name\n"
1214 " in the File->Export menu) should be passed. To export to\n"
1215 " the document's default output format, use 'default'.\n"
1216 " Note that the order of -e and -x switches matters.\n"
1217 "\t-E [--export-to] fmt filename\n"
1218 " where fmt is the export format of choice (see --export),\n"
1219 " and filename is the destination filename.\n"
1220 "\t-i [--import] fmt file.xxx\n"
1221 " where fmt is the import format of choice\n"
1222 " and file.xxx is the file to be imported.\n"
1223 "\t-f [--force-overwrite] what\n"
1224 " where what is either `all', `main' or `none',\n"
1225 " specifying whether all files, main file only, or no files,\n"
1226 " respectively, are to be overwritten during a batch export.\n"
1227 " Anything else is equivalent to `all', but is not consumed.\n"
1228 "\t--ignore-error-message which\n"
1229 " allows you to ignore specific LaTeX error messages.\n"
1230 " Do not use for final documents! Currently supported values:\n"
1231 " * missing_glyphs: Fontspec `missing glyphs' error.\n"
1232 "\t-n [--no-remote]\n"
1233 " open documents in a new instance\n"
1235 " open documents in an already running instance\n"
1236 " (a working lyxpipe is needed)\n"
1237 "\t-v [--verbose]\n"
1238 " report on terminal about spawned commands.\n"
1239 "\t-batch execute commands without launching GUI and exit.\n"
1240 "\t-version summarize version and build info\n"
1241 "Check the LyX man page for more details.")) << endl;
1247 int parse_version(string const &, string const &, string &)
1249 cout << "LyX " << lyx_version
1250 << " (" << lyx_release_date << ")" << endl;
1251 if (string(lyx_git_commit_hash) != "none")
1252 cout << to_utf8(_(" Git commit hash "))
1253 << string(lyx_git_commit_hash).substr(0,8) << endl;
1254 cout << lyx_version_info << endl;
1260 int parse_sysdir(string const & arg, string const &, string &)
1263 Alert::error(_("No system directory"),
1264 _("Missing directory for -sysdir switch"));
1267 cl_system_support = arg;
1272 int parse_userdir(string const & arg, string const &, string &)
1275 Alert::error(_("No user directory"),
1276 _("Missing directory for -userdir switch"));
1279 cl_user_support = arg;
1284 int parse_execute(string const & arg, string const &, string & batch)
1287 Alert::error(_("Incomplete command"),
1288 _("Missing command string after --execute switch"));
1296 int parse_export_to(string const & type, string const & output_file, string & batch)
1299 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1300 "--export-to switch")) << endl;
1303 if (output_file.empty()) {
1304 lyxerr << to_utf8(_("Missing destination filename after "
1305 "--export-to switch")) << endl;
1308 batch = "buffer-export " + type + " " + output_file;
1314 int parse_export(string const & type, string const &, string & batch)
1317 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1318 "--export switch")) << endl;
1321 batch = "buffer-export " + type;
1327 int parse_import(string const & type, string const & file, string & batch)
1330 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1331 "--import switch")) << endl;
1335 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1338 batch = "buffer-import " + type + ' ' + file;
1343 int parse_geometry(string const & arg1, string const &, string &)
1346 // don't remove "-geometry", it will be pruned out later in the
1347 // frontend if need be.
1352 int parse_batch(string const &, string const &, string &)
1359 int parse_noremote(string const &, string const &, string &)
1361 run_mode = NEW_INSTANCE;
1366 int parse_remote(string const &, string const &, string &)
1368 run_mode = USE_REMOTE;
1373 int parse_verbose(string const &, string const &, string &)
1380 int parse_ignore_error_message(string const & arg1, string const &, string &)
1382 if (arg1 == "missing_glyphs") {
1383 ignore_missing_glyphs = true;
1390 int parse_force(string const & arg, string const &, string &)
1393 force_overwrite = ALL_FILES;
1395 } else if (arg == "main") {
1396 force_overwrite = MAIN_FILE;
1398 } else if (arg == "none") {
1399 force_overwrite = NO_FILES;
1402 force_overwrite = ALL_FILES;
1410 void LyX::easyParse(int & argc, char * argv[])
1412 map<string, cmd_helper> cmdmap;
1414 cmdmap["-dbg"] = parse_dbg;
1415 cmdmap["-help"] = parse_help;
1416 cmdmap["--help"] = parse_help;
1417 cmdmap["-version"] = parse_version;
1418 cmdmap["--version"] = parse_version;
1419 cmdmap["-sysdir"] = parse_sysdir;
1420 cmdmap["-userdir"] = parse_userdir;
1421 cmdmap["-x"] = parse_execute;
1422 cmdmap["--execute"] = parse_execute;
1423 cmdmap["-e"] = parse_export;
1424 cmdmap["--export"] = parse_export;
1425 cmdmap["-E"] = parse_export_to;
1426 cmdmap["--export-to"] = parse_export_to;
1427 cmdmap["-i"] = parse_import;
1428 cmdmap["--import"] = parse_import;
1429 cmdmap["-geometry"] = parse_geometry;
1430 cmdmap["-batch"] = parse_batch;
1431 cmdmap["-f"] = parse_force;
1432 cmdmap["--force-overwrite"] = parse_force;
1433 cmdmap["-n"] = parse_noremote;
1434 cmdmap["--no-remote"] = parse_noremote;
1435 cmdmap["-r"] = parse_remote;
1436 cmdmap["--remote"] = parse_remote;
1437 cmdmap["-v"] = parse_verbose;
1438 cmdmap["--verbose"] = parse_verbose;
1439 cmdmap["--ignore-error-message"] = parse_ignore_error_message;
1441 for (int i = 1; i < argc; ++i) {
1442 map<string, cmd_helper>::const_iterator it
1443 = cmdmap.find(argv[i]);
1445 // don't complain if not found - may be parsed later
1446 if (it == cmdmap.end())
1450 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1452 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1455 int const remove = 1 + it->second(arg, arg2, batch);
1457 pimpl_->batch_commands.push_back(batch);
1459 // Now, remove used arguments by shifting
1460 // the following ones remove places down.
1462 os::remove_internal_args(i, remove);
1464 for (int j = i; j < argc; ++j)
1465 argv[j] = argv[j + remove];
1472 FuncStatus getStatus(FuncRequest const & action)
1475 return theApp()->getStatus(action);
1479 DispatchResult const & dispatch(FuncRequest const & action)
1482 return theApp()->dispatch(action);
1486 void dispatch(FuncRequest const & action, DispatchResult & dr)
1489 theApp()->dispatch(action, dr);
1493 vector<string> & theFilesToLoad()
1495 LAPPERR(singleton_);
1496 return singleton_->pimpl_->files_to_load_;
1500 BufferList & theBufferList()
1502 LAPPERR(singleton_);
1503 return singleton_->pimpl_->buffer_list_;
1507 Server & theServer()
1509 // FIXME: this should not be use_gui dependent
1511 LAPPERR(singleton_);
1512 return *singleton_->pimpl_->lyx_server_;
1516 ServerSocket & theServerSocket()
1518 // FIXME: this should not be use_gui dependent
1520 LAPPERR(singleton_);
1521 return *singleton_->pimpl_->lyx_socket_;
1525 KeyMap & theTopLevelKeymap()
1527 LAPPERR(singleton_);
1528 return singleton_->pimpl_->toplevel_keymap_;
1532 Formats & theFormats()
1534 LAPPERR(singleton_);
1535 return singleton_->pimpl_->formats_;
1539 Formats & theSystemFormats()
1541 LAPPERR(singleton_);
1542 return singleton_->pimpl_->system_formats_;
1546 Converters & theConverters()
1548 LAPPERR(singleton_);
1549 return singleton_->pimpl_->converters_;
1553 Converters & theSystemConverters()
1555 LAPPERR(singleton_);
1556 return singleton_->pimpl_->system_converters_;
1560 Movers & theMovers()
1562 LAPPERR(singleton_);
1563 return singleton_->pimpl_->movers_;
1567 Mover const & getMover(string const & fmt)
1569 LAPPERR(singleton_);
1570 return singleton_->pimpl_->movers_(fmt);
1574 void setMover(string const & fmt, string const & command)
1576 LAPPERR(singleton_);
1577 singleton_->pimpl_->movers_.set(fmt, command);
1581 Movers & theSystemMovers()
1583 LAPPERR(singleton_);
1584 return singleton_->pimpl_->system_movers_;
1588 Messages const & getMessages(string const & language)
1590 LAPPERR(singleton_);
1591 return singleton_->messages(language);
1595 Messages const & getGuiMessages()
1597 LAPPERR(singleton_);
1598 return singleton_->messages(Messages::guiLanguage());
1602 Session & theSession()
1604 LAPPERR(singleton_);
1605 return *singleton_->pimpl_->session_.get();
1609 LaTeXFonts & theLaTeXFonts()
1611 LAPPERR(singleton_);
1612 if (!singleton_->pimpl_->latexfonts_)
1613 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1614 return *singleton_->pimpl_->latexfonts_;
1618 CmdDef & theTopLevelCmdDef()
1620 LAPPERR(singleton_);
1621 return singleton_->pimpl_->toplevel_cmddef_;
1625 SpellChecker * theSpellChecker()
1627 if (!singleton_->pimpl_->spell_checker_)
1629 return singleton_->pimpl_->spell_checker_;
1633 void setSpellChecker()
1635 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1636 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1638 if (lyxrc.spellchecker == "native") {
1639 #if defined(USE_MACOSX_PACKAGING)
1640 if (!singleton_->pimpl_->apple_spell_checker_)
1641 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1642 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1644 singleton_->pimpl_->spell_checker_ = nullptr;
1646 } else if (lyxrc.spellchecker == "aspell") {
1647 #if defined(USE_ASPELL)
1648 if (!singleton_->pimpl_->aspell_checker_)
1649 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1650 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1652 singleton_->pimpl_->spell_checker_ = nullptr;
1654 } else if (lyxrc.spellchecker == "enchant") {
1655 #if defined(USE_ENCHANT)
1656 if (!singleton_->pimpl_->enchant_checker_)
1657 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1658 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1660 singleton_->pimpl_->spell_checker_ = nullptr;
1662 } else if (lyxrc.spellchecker == "hunspell") {
1663 #if defined(USE_HUNSPELL)
1664 if (!singleton_->pimpl_->hunspell_checker_)
1665 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1666 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1668 singleton_->pimpl_->spell_checker_ = nullptr;
1671 singleton_->pimpl_->spell_checker_ = nullptr;
1673 if (singleton_->pimpl_->spell_checker_) {
1674 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1675 singleton_->pimpl_->spell_checker_->advanceChangeNumber();