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 const bool noLayouts = LayoutFileList::get().empty();
588 if (noLayouts && os::hasPython()) {
589 switch (Alert::prompt(
590 _("No textclass is found"),
591 _("LyX will only have minimal functionality because no textclasses "
592 "have been found. You can either try to reconfigure LyX normally, "
593 "try to reconfigure without checking your LaTeX installation, or continue."),
601 // regular reconfigure
602 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
605 // reconfigure --without-latex-config
606 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
607 " --without-latex-config"));
610 lyx::dispatch(FuncRequest(LFUN_LYX_QUIT, ""));
615 } else if (noLayouts) {
616 switch (Alert::prompt(
617 _("No python is found"),
618 _("LyX will only have minimal functionality because no python interpreter "
619 "has been found. Consider installing python with your software manager "
620 "or from the python.org website."),
626 lyx::dispatch(FuncRequest(LFUN_LYX_QUIT, ""));
633 // create the first main window
634 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
636 if (!pimpl_->files_to_load_.empty()) {
637 // if some files were specified at command-line we assume that the
638 // user wants to edit *these* files and not to restore the session.
639 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
641 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
643 // clear this list to save a few bytes of RAM
644 pimpl_->files_to_load_.clear();
646 pimpl_->application_->restoreGuiSession();
648 // Execute batch commands if available
649 if (pimpl_->batch_commands.empty())
652 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
653 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
654 for (; bcit != bcend; ++bcit) {
655 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
656 lyx::dispatch(lyxaction.lookupFunc(*bcit));
664 The SIGHUP signal does not exist on Windows and does not need to be handled.
666 Windows handles SIGFPE and SIGSEGV signals as expected.
668 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
669 cause a new thread to be spawned. This may well result in unexpected
670 behaviour by the single-threaded LyX.
672 SIGTERM signals will come only from another process actually sending
673 that signal using 'raise' in Windows' POSIX compatability layer. It will
674 not come from the general "terminate process" methods that everyone
675 actually uses (and which can't be trapped). Killing an app 'politely' on
676 Windows involves first sending a WM_CLOSE message, something that is
677 caught already by the Qt frontend.
679 For more information see:
681 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
682 ...signals are mostly useless on Windows for a variety of reasons that are
685 'UNIX Application Migration Guide, Chapter 9'
686 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
688 'How To Terminate an Application "Cleanly" in Win32'
689 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
693 static void error_handler(int err_sig)
695 // Throw away any signals other than the first one received.
696 static sig_atomic_t handling_error = false;
699 handling_error = true;
701 // We have received a signal indicating a fatal error, so
702 // try and save the data ASAP.
705 // These lyxerr calls may or may not work:
707 // Signals are asynchronous, so the main program may be in a very
708 // fragile state when a signal is processed and thus while a signal
709 // handler function executes.
710 // In general, therefore, we should avoid performing any
711 // I/O operations or calling most library and system functions from
714 // This shouldn't matter here, however, as we've already invoked
720 msg = _("SIGHUP signal caught!\nBye.");
724 msg = _("SIGFPE signal caught!\nBye.");
727 msg = _("SIGSEGV signal caught!\n"
728 "Sorry, you have found a bug in LyX, "
729 "hope you have not lost any data.\n"
730 "Please read the bug-reporting instructions "
731 "in 'Help->Introduction' and send us a bug report, "
732 "if necessary. Thanks!\nBye.");
741 lyxerr << "\nlyx: " << msg << endl;
742 // try to make a GUI message
743 Alert::error(_("LyX crashed!"), msg, true);
746 // Deinstall the signal handlers
748 signal(SIGHUP, SIG_DFL);
750 signal(SIGINT, SIG_DFL);
751 signal(SIGFPE, SIG_DFL);
752 signal(SIGSEGV, SIG_DFL);
753 signal(SIGTERM, SIG_DFL);
756 if (err_sig == SIGSEGV ||
757 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
759 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
762 // with abort() it crashes again.
775 void LyX::printError(ErrorItem const & ei)
777 docstring tmp = _("LyX: ") + ei.error + char_type(':')
779 cerr << to_utf8(tmp) << endl;
782 #if defined (USE_MACOSX_PACKAGING)
784 // Unexposed--extract an environment variable name from its NAME=VALUE
786 std::string varname(const char* line)
788 size_t nameLen = strcspn(line, "=");
789 if (nameLen == strlen(line)) {
790 return std::string();
792 return std::string(line, nameLen);
797 void cleanDuplicateEnvVars()
799 std::set<std::string> seen;
800 std::set<std::string> dupes;
802 // Create a list of the environment variables that appear more than once
803 for (char **read = *_NSGetEnviron(); *read; read++) {
804 std::string name = varname(*read);
805 if (name.size() == 0) {
808 if (seen.find(name) != seen.end()) {
815 // Loop over the list of duplicated variables
816 std::set<std::string>::iterator dupe = dupes.begin();
817 std::set<std::string>::iterator const dend = dupes.end();
818 for (; dupe != dend; ++dupe) {
819 const char *name = (*dupe).c_str();
820 char *val = getenv(name);
822 LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
823 // unsetenv removes *all* instances of the variable from the environment
826 // replace with the value from getenv (in practice appears to be the
827 // first value in the list)
828 setenv(name, val, 0);
835 static void initTemplatePath()
837 FileName const package_template_path =
838 FileName(addName(package().system_support().absFileName(), "templates"));
840 if (lyxrc.template_path.empty()) {
841 lyxrc.template_path = package_template_path.absFileName();
843 #if defined (USE_MACOSX_PACKAGING)
844 FileName const user_template_path =
845 FileName(addName(package().user_support().absFileName(), "templates"));
847 if (package_template_path != FileName(lyxrc.template_path) &&
848 user_template_path != FileName(lyxrc.template_path))
852 FileName const user_template_link =
853 FileName(addName(user_template_path.absFileName(),"SystemTemplates"));
854 if (user_template_link.isSymLink() && !equivalent(user_template_link, package_template_path)) {
855 user_template_link.removeFile();
857 if (!user_template_link.exists()) {
858 if (!package_template_path.link(user_template_link)) {
859 FileName const user_support = package().user_support();
860 if (user_support.exists() && user_support.isDirectory()) {
861 LYXERR(Debug::INIT, "Cannot create symlink " + user_template_link.absFileName());
862 lyxrc.template_path = package_template_path.absFileName();
866 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
868 lyxrc.template_path = user_template_path.absFileName();
876 signal(SIGHUP, error_handler);
878 signal(SIGFPE, error_handler);
879 signal(SIGSEGV, error_handler);
880 signal(SIGINT, error_handler);
881 signal(SIGTERM, error_handler);
882 // SIGPIPE can be safely ignored.
884 #if defined (USE_MACOSX_PACKAGING)
885 cleanDuplicateEnvVars();
888 lyxrc.tempdir_path = package().temp_dir().absFileName();
889 lyxrc.document_path = package().document_dir().absFileName();
891 if (lyxrc.example_path.empty()) {
892 lyxrc.example_path = addPath(package().system_support().absFileName(),
897 // init LyXDir environment variable
898 string const lyx_dir = package().lyx_dir().absFileName();
899 LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
900 if (!setEnv("LyXDir", lyx_dir))
901 LYXERR(Debug::INIT, "\t... failed!");
903 if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
904 // -userdir was given on the command line.
905 // Make it available to child processes, otherwise tex2lyx
906 // would not find all layout files, and other converters might
908 string const user_dir = package().user_support().absFileName();
909 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
911 if (!setEnv(LYX_USERDIR_VER, user_dir))
912 LYXERR(Debug::INIT, "\t... failed!");
916 // Read configuration files
919 // This one may have been distributed along with LyX.
920 if (!readRcFile("lyxrc.dist"))
923 // Set the PATH correctly.
924 #if !defined (USE_POSIX_PACKAGING) && !defined (USE_HAIKU_PACKAGING)
925 // Add the directory containing the LyX executable to the path
926 // so that LyX can find things like tex2lyx.
927 if (package().build_support().empty())
928 prependEnvPath("PATH", package().binary_dir().absFileName());
931 // Add the directory containing the dt2dv and dv2dt executables to the path
933 if (!package().build_support().empty()) {
934 // dtl executables should be in the same dir ar tex2lyx
935 dtldir = package().binary_dir();
938 dtldir = FileName(addName(package().system_support().absFileName(), "extratools"));
941 string dtlexe = "dt2dv.exe";
943 string dtlexe = "dt2dv";
945 FileName const dt2dv = FileName(addName(dtldir.absFileName(), dtlexe));
947 prependEnvPath("PATH", dtldir.absFileName());
949 if (!lyxrc.path_prefix.empty())
950 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
952 // Check that user LyX directory is ok.
954 string const lock_file = package().getConfigureLockName();
955 int fd = fileLock(lock_file.c_str());
957 if (queryUserLyXDir(package().explicit_user_support())) {
958 package().reconfigureUserLyXDir("");
959 // Now the user directory is present on first start.
962 fileUnlock(fd, lock_file.c_str());
966 // No need for a splash when there is no GUI
968 // Default is to overwrite the main file during export, unless
969 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
970 if (force_overwrite == UNSPECIFIED) {
971 string const what = getEnv("LYX_FORCE_OVERWRITE");
973 force_overwrite = ALL_FILES;
974 else if (what == "none")
975 force_overwrite = NO_FILES;
977 force_overwrite = MAIN_FILE;
981 // This one is generated in user_support directory by lib/configure.py.
982 if (!readRcFile("lyxrc.defaults"))
985 // Query the OS to know what formats are viewed natively
986 theFormats().setAutoOpen();
988 // Read lyxrc.dist again to be able to override viewer auto-detection.
989 readRcFile("lyxrc.dist");
991 system_lyxrc = lyxrc;
992 theSystemFormats() = theFormats();
993 pimpl_->system_converters_ = pimpl_->converters_;
994 pimpl_->system_movers_ = pimpl_->movers_;
995 system_lcolor = lcolor;
997 // This one is edited through the preferences dialog.
998 if (!readRcFile("preferences", true))
1001 // The language may have been set to someting useful through prefs
1004 if (!readEncodingsFile("encodings", "unicodesymbols"))
1006 if (!readLanguagesFile("languages"))
1009 LYXERR(Debug::INIT, "Reading layouts...");
1011 LayoutFileList::get().read();
1013 theModuleList.read();
1014 //... and the cite engines
1015 theCiteEnginesList.read();
1017 // read keymap and ui files in batch mode as well
1018 // because InsetInfo needs to know these to produce
1019 // the correct output
1021 // Set up command definitions
1022 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
1026 pimpl_->toplevel_keymap_.read("site");
1027 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
1028 // load user bind file user.bind
1029 pimpl_->toplevel_keymap_.read("user", nullptr, KeyMap::MissingOK);
1031 if (lyxerr.debugging(Debug::LYXRC))
1034 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
1035 // Prepend path prefix a second time to take the user preferences into a account
1036 if (!lyxrc.path_prefix.empty())
1037 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
1039 FileName const document_path(lyxrc.document_path);
1040 if (document_path.exists() && document_path.isDirectory())
1041 package().document_dir() = document_path;
1043 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
1044 if (package().temp_dir().empty()) {
1045 Alert::error(_("Could not create temporary directory"),
1046 bformat(_("Could not create a temporary directory in\n"
1048 "Make sure that this path exists and is writable and try again."),
1049 from_utf8(lyxrc.tempdir_path)));
1050 // createLyXTmpDir() tries sufficiently hard to create a
1051 // usable temp dir, so the probability to come here is
1052 // close to zero. We therefore don't try to overcome this
1053 // problem with e.g. asking the user for a new path and
1054 // trying again but simply exit.
1058 LYXERR(Debug::INIT, "LyX tmp dir: `"
1059 << package().temp_dir().absFileName() << '\'');
1061 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
1062 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1064 // This must happen after package initialization and after lyxrc is
1065 // read, therefore it can't be done by a static object.
1066 ConverterCache::init();
1072 void emergencyCleanup()
1074 // what to do about tmpfiles is non-obvious. we would
1075 // like to delete any we find, but our lyxdir might
1076 // contain documents etc. which might be helpful on
1079 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1081 if (singleton_->pimpl_->lyx_server_)
1082 singleton_->pimpl_->lyx_server_->emergencyCleanup();
1083 singleton_->pimpl_->lyx_server_.reset();
1084 singleton_->pimpl_->lyx_socket_.reset();
1089 bool LyX::queryUserLyXDir(bool explicit_userdir)
1091 // Does user directory exist?
1092 FileName const sup = package().user_support();
1093 if (sup.exists() && sup.isDirectory()) {
1094 first_start = false;
1096 return configFileNeedsUpdate("lyxrc.defaults")
1097 || configFileNeedsUpdate("lyxmodules.lst")
1098 || configFileNeedsUpdate("textclass.lst")
1099 || configFileNeedsUpdate("packages.lst")
1100 || configFileNeedsUpdate("lyxciteengines.lst")
1101 || configFileNeedsUpdate("xtemplates.lst");
1104 first_start = !explicit_userdir;
1106 // If the user specified explicitly a directory, ask whether
1107 // to create it. If the user says "no", then exit.
1108 if (explicit_userdir &&
1110 _("Missing user LyX directory"),
1111 bformat(_("You have specified a non-existent user "
1112 "LyX directory, %1$s.\n"
1113 "It is needed to keep your own configuration."),
1114 from_utf8(package().user_support().absFileName())),
1116 _("&Create directory"),
1118 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1119 earlyExit(EXIT_FAILURE);
1122 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1123 from_utf8(sup.absFileName()))) << endl;
1125 if (!sup.createDirectory(0755)) {
1126 // Failed, so let's exit.
1127 lyxerr << to_utf8(_("Failed to create directory. Perhaps wrong -userdir option?\nExiting."))
1129 earlyExit(EXIT_FAILURE);
1136 bool LyX::readRcFile(string const & name, bool check_format)
1138 LYXERR(Debug::INIT, "About to read " << name << "... ");
1140 FileName const lyxrc_path = libFileSearch(string(), name);
1141 if (lyxrc_path.empty()) {
1142 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1144 // This was the previous logic, but can it be right??
1147 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1148 bool const success = lyxrc.read(lyxrc_path, check_format);
1150 showFileError(name);
1154 // Read the languages file `name'
1155 bool LyX::readLanguagesFile(string const & name)
1157 LYXERR(Debug::INIT, "About to read " << name << "...");
1159 FileName const lang_path = libFileSearch(string(), name);
1160 if (lang_path.empty()) {
1161 showFileError(name);
1164 languages.read(lang_path);
1169 // Read the encodings file `name'
1170 bool LyX::readEncodingsFile(string const & enc_name,
1171 string const & symbols_name)
1173 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1174 << symbols_name << "...");
1176 FileName const symbols_path = libFileSearch(string(), symbols_name);
1177 if (symbols_path.empty()) {
1178 showFileError(symbols_name);
1182 FileName const enc_path = libFileSearch(string(), enc_name);
1183 if (enc_path.empty()) {
1184 showFileError(enc_name);
1187 encodings.read(enc_path, symbols_path);
1194 /// return the the number of arguments consumed
1195 typedef function<int(string const &, string const &, string &)> cmd_helper;
1197 int parse_dbg(string const & arg, string const &, string &)
1200 cout << to_utf8(_("List of supported debug flags:")) << endl;
1201 Debug::showTags(cout);
1204 string bad = Debug::badValue(arg);
1206 lyxerr.setLevel(Debug::value(arg));
1207 Debug::showLevel(lyxerr, lyxerr.level());
1209 cout << to_utf8(bformat(_("Bad debug value `%1$s'. Exiting."),
1210 from_utf8(bad))) << endl;
1217 int parse_help(string const &, string const &, string &)
1220 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1221 "Command line switches (case sensitive):\n"
1222 "\t-help summarize LyX usage\n"
1223 "\t-userdir dir set user directory to dir\n"
1224 "\t-sysdir dir set system directory to dir\n"
1225 "\t-geometry WxH+X+Y set geometry of the main window\n"
1226 "\t-dbg feature[,feature]...\n"
1227 " select the features to debug.\n"
1228 " Type `lyx -dbg' to see the list of features\n"
1229 "\t-x [--execute] command\n"
1230 " where command is a lyx command.\n"
1231 "\t-e [--export] fmt\n"
1232 " where fmt is the export format of choice. Look in\n"
1233 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1234 " to see which parameter (which differs from the format name\n"
1235 " in the File->Export menu) should be passed. To export to\n"
1236 " the document's default output format, use 'default'.\n"
1237 " Note that the order of -e and -x switches matters.\n"
1238 "\t-E [--export-to] fmt filename\n"
1239 " where fmt is the export format of choice (see --export),\n"
1240 " and filename is the destination filename.\n"
1241 "\t-i [--import] fmt file.xxx\n"
1242 " where fmt is the import format of choice\n"
1243 " and file.xxx is the file to be imported.\n"
1244 "\t-f [--force-overwrite] what\n"
1245 " where what is either `all', `main' or `none',\n"
1246 " specifying whether all files, main file only, or no files,\n"
1247 " respectively, are to be overwritten during a batch export.\n"
1248 " Anything else is equivalent to `all', but is not consumed.\n"
1249 "\t--ignore-error-message which\n"
1250 " allows you to ignore specific LaTeX error messages.\n"
1251 " Do not use for final documents! Currently supported values:\n"
1252 " * missing_glyphs: Fontspec `missing glyphs' error.\n"
1253 "\t-n [--no-remote]\n"
1254 " open documents in a new instance\n"
1256 " open documents in an already running instance\n"
1257 " (a working lyxpipe is needed)\n"
1258 "\t-v [--verbose]\n"
1259 " report on terminal about spawned commands.\n"
1260 "\t-batch execute commands without launching GUI and exit.\n"
1261 "\t-version summarize version and build info\n"
1262 "Check the LyX man page for more details.")) << endl;
1268 int parse_version(string const &, string const &, string &)
1270 cout << "LyX " << lyx_version
1271 << " (" << lyx_release_date << ")" << endl;
1272 if (string(lyx_git_commit_hash) != "none")
1273 cout << to_utf8(_(" Git commit hash "))
1274 << string(lyx_git_commit_hash).substr(0,8) << endl;
1275 cout << lyx_version_info << endl;
1281 int parse_sysdir(string const & arg, string const &, string &)
1284 Alert::error(_("No system directory"),
1285 _("Missing directory for -sysdir switch"));
1288 cl_system_support = arg;
1293 int parse_userdir(string const & arg, string const &, string &)
1296 Alert::error(_("No user directory"),
1297 _("Missing directory for -userdir switch"));
1300 cl_user_support = arg;
1305 int parse_execute(string const & arg, string const &, string & batch)
1308 Alert::error(_("Incomplete command"),
1309 _("Missing command string after --execute switch"));
1317 int parse_export_to(string const & type, string const & output_file, string & batch)
1320 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1321 "--export-to switch")) << endl;
1324 if (output_file.empty()) {
1325 lyxerr << to_utf8(_("Missing destination filename after "
1326 "--export-to switch")) << endl;
1329 batch = "buffer-export " + type + " " + output_file;
1335 int parse_export(string const & type, string const &, string & batch)
1338 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1339 "--export switch")) << endl;
1342 batch = "buffer-export " + type;
1348 int parse_import(string const & type, string const & file, string & batch)
1351 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1352 "--import switch")) << endl;
1356 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1359 batch = "buffer-import " + type + ' ' + file;
1364 int parse_geometry(string const & arg1, string const &, string &)
1367 // don't remove "-geometry", it will be pruned out later in the
1368 // frontend if need be.
1373 int parse_batch(string const &, string const &, string &)
1380 int parse_noremote(string const &, string const &, string &)
1382 run_mode = NEW_INSTANCE;
1387 int parse_remote(string const &, string const &, string &)
1389 run_mode = USE_REMOTE;
1394 int parse_verbose(string const &, string const &, string &)
1401 int parse_ignore_error_message(string const & arg1, string const &, string &)
1403 if (arg1 == "missing_glyphs") {
1404 ignore_missing_glyphs = true;
1411 int parse_force(string const & arg, string const &, string &)
1414 force_overwrite = ALL_FILES;
1416 } else if (arg == "main") {
1417 force_overwrite = MAIN_FILE;
1419 } else if (arg == "none") {
1420 force_overwrite = NO_FILES;
1423 force_overwrite = ALL_FILES;
1431 void LyX::easyParse(int & argc, char * argv[])
1433 map<string, cmd_helper> cmdmap;
1435 cmdmap["-dbg"] = parse_dbg;
1436 cmdmap["-help"] = parse_help;
1437 cmdmap["--help"] = parse_help;
1438 cmdmap["-version"] = parse_version;
1439 cmdmap["--version"] = parse_version;
1440 cmdmap["-sysdir"] = parse_sysdir;
1441 cmdmap["-userdir"] = parse_userdir;
1442 cmdmap["-x"] = parse_execute;
1443 cmdmap["--execute"] = parse_execute;
1444 cmdmap["-e"] = parse_export;
1445 cmdmap["--export"] = parse_export;
1446 cmdmap["-E"] = parse_export_to;
1447 cmdmap["--export-to"] = parse_export_to;
1448 cmdmap["-i"] = parse_import;
1449 cmdmap["--import"] = parse_import;
1450 cmdmap["-geometry"] = parse_geometry;
1451 cmdmap["-batch"] = parse_batch;
1452 cmdmap["-f"] = parse_force;
1453 cmdmap["--force-overwrite"] = parse_force;
1454 cmdmap["-n"] = parse_noremote;
1455 cmdmap["--no-remote"] = parse_noremote;
1456 cmdmap["-r"] = parse_remote;
1457 cmdmap["--remote"] = parse_remote;
1458 cmdmap["-v"] = parse_verbose;
1459 cmdmap["--verbose"] = parse_verbose;
1460 cmdmap["--ignore-error-message"] = parse_ignore_error_message;
1462 for (int i = 1; i < argc; ++i) {
1463 map<string, cmd_helper>::const_iterator it
1464 = cmdmap.find(argv[i]);
1466 // don't complain if not found - may be parsed later
1467 if (it == cmdmap.end())
1471 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1473 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1476 int const remove = 1 + it->second(arg, arg2, batch);
1478 pimpl_->batch_commands.push_back(batch);
1480 // Now, remove used arguments by shifting
1481 // the following ones remove places down.
1483 os::remove_internal_args(i, remove);
1485 for (int j = i; j < argc; ++j)
1486 argv[j] = argv[j + remove];
1493 FuncStatus getStatus(FuncRequest const & action)
1496 return theApp()->getStatus(action);
1500 DispatchResult const & dispatch(FuncRequest const & action)
1503 return theApp()->dispatch(action);
1507 void dispatch(FuncRequest const & action, DispatchResult & dr)
1510 theApp()->dispatch(action, dr);
1514 vector<string> & theFilesToLoad()
1516 LAPPERR(singleton_);
1517 return singleton_->pimpl_->files_to_load_;
1521 BufferList & theBufferList()
1523 LAPPERR(singleton_);
1524 return singleton_->pimpl_->buffer_list_;
1528 Server & theServer()
1530 // FIXME: this should not be use_gui dependent
1532 LAPPERR(singleton_);
1533 return *singleton_->pimpl_->lyx_server_;
1537 ServerSocket & theServerSocket()
1539 // FIXME: this should not be use_gui dependent
1541 LAPPERR(singleton_);
1542 return *singleton_->pimpl_->lyx_socket_;
1546 KeyMap & theTopLevelKeymap()
1548 LAPPERR(singleton_);
1549 return singleton_->pimpl_->toplevel_keymap_;
1553 Formats & theFormats()
1555 LAPPERR(singleton_);
1556 return singleton_->pimpl_->formats_;
1560 Formats & theSystemFormats()
1562 LAPPERR(singleton_);
1563 return singleton_->pimpl_->system_formats_;
1567 Converters & theConverters()
1569 LAPPERR(singleton_);
1570 return singleton_->pimpl_->converters_;
1574 Converters & theSystemConverters()
1576 LAPPERR(singleton_);
1577 return singleton_->pimpl_->system_converters_;
1581 Movers & theMovers()
1583 LAPPERR(singleton_);
1584 return singleton_->pimpl_->movers_;
1588 Mover const & getMover(string const & fmt)
1590 LAPPERR(singleton_);
1591 return singleton_->pimpl_->movers_(fmt);
1595 void setMover(string const & fmt, string const & command)
1597 LAPPERR(singleton_);
1598 singleton_->pimpl_->movers_.set(fmt, command);
1602 Movers & theSystemMovers()
1604 LAPPERR(singleton_);
1605 return singleton_->pimpl_->system_movers_;
1609 Messages const & getMessages(string const & language)
1611 LAPPERR(singleton_);
1612 return singleton_->messages(language);
1616 Messages const & getGuiMessages()
1618 LAPPERR(singleton_);
1619 return singleton_->messages(Messages::guiLanguage());
1623 Session & theSession()
1625 LAPPERR(singleton_);
1626 return *singleton_->pimpl_->session_.get();
1630 LaTeXFonts & theLaTeXFonts()
1632 LAPPERR(singleton_);
1633 if (!singleton_->pimpl_->latexfonts_)
1634 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1635 return *singleton_->pimpl_->latexfonts_;
1639 CmdDef & theTopLevelCmdDef()
1641 LAPPERR(singleton_);
1642 return singleton_->pimpl_->toplevel_cmddef_;
1646 SpellChecker * theSpellChecker()
1648 if (!singleton_->pimpl_->spell_checker_)
1650 return singleton_->pimpl_->spell_checker_;
1654 void setSpellChecker()
1656 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1657 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1659 if (lyxrc.spellchecker == "native") {
1660 #if defined(USE_MACOSX_PACKAGING)
1661 if (!singleton_->pimpl_->apple_spell_checker_)
1662 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1663 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1665 singleton_->pimpl_->spell_checker_ = nullptr;
1667 } else if (lyxrc.spellchecker == "aspell") {
1668 #if defined(USE_ASPELL)
1669 if (!singleton_->pimpl_->aspell_checker_)
1670 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1671 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1673 singleton_->pimpl_->spell_checker_ = nullptr;
1675 } else if (lyxrc.spellchecker == "enchant") {
1676 #if defined(USE_ENCHANT)
1677 if (!singleton_->pimpl_->enchant_checker_)
1678 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1679 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1681 singleton_->pimpl_->spell_checker_ = nullptr;
1683 } else if (lyxrc.spellchecker == "hunspell") {
1684 #if defined(USE_HUNSPELL)
1685 if (!singleton_->pimpl_->hunspell_checker_)
1686 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1687 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1689 singleton_->pimpl_->spell_checker_ = nullptr;
1692 singleton_->pimpl_->spell_checker_ = nullptr;
1694 if (singleton_->pimpl_->spell_checker_) {
1695 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1696 singleton_->pimpl_->spell_checker_->advanceChangeNumber();