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 download and install of an python interpreter."),
625 lyx::dispatch(FuncRequest(LFUN_LYX_QUIT, ""));
632 // create the first main window
633 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
635 if (!pimpl_->files_to_load_.empty()) {
636 // if some files were specified at command-line we assume that the
637 // user wants to edit *these* files and not to restore the session.
638 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
640 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
642 // clear this list to save a few bytes of RAM
643 pimpl_->files_to_load_.clear();
645 pimpl_->application_->restoreGuiSession();
647 // Execute batch commands if available
648 if (pimpl_->batch_commands.empty())
651 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
652 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
653 for (; bcit != bcend; ++bcit) {
654 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
655 lyx::dispatch(lyxaction.lookupFunc(*bcit));
663 The SIGHUP signal does not exist on Windows and does not need to be handled.
665 Windows handles SIGFPE and SIGSEGV signals as expected.
667 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
668 cause a new thread to be spawned. This may well result in unexpected
669 behaviour by the single-threaded LyX.
671 SIGTERM signals will come only from another process actually sending
672 that signal using 'raise' in Windows' POSIX compatability layer. It will
673 not come from the general "terminate process" methods that everyone
674 actually uses (and which can't be trapped). Killing an app 'politely' on
675 Windows involves first sending a WM_CLOSE message, something that is
676 caught already by the Qt frontend.
678 For more information see:
680 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
681 ...signals are mostly useless on Windows for a variety of reasons that are
684 'UNIX Application Migration Guide, Chapter 9'
685 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
687 'How To Terminate an Application "Cleanly" in Win32'
688 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
692 static void error_handler(int err_sig)
694 // Throw away any signals other than the first one received.
695 static sig_atomic_t handling_error = false;
698 handling_error = true;
700 // We have received a signal indicating a fatal error, so
701 // try and save the data ASAP.
704 // These lyxerr calls may or may not work:
706 // Signals are asynchronous, so the main program may be in a very
707 // fragile state when a signal is processed and thus while a signal
708 // handler function executes.
709 // In general, therefore, we should avoid performing any
710 // I/O operations or calling most library and system functions from
713 // This shouldn't matter here, however, as we've already invoked
719 msg = _("SIGHUP signal caught!\nBye.");
723 msg = _("SIGFPE signal caught!\nBye.");
726 msg = _("SIGSEGV signal caught!\n"
727 "Sorry, you have found a bug in LyX, "
728 "hope you have not lost any data.\n"
729 "Please read the bug-reporting instructions "
730 "in 'Help->Introduction' and send us a bug report, "
731 "if necessary. Thanks!\nBye.");
740 lyxerr << "\nlyx: " << msg << endl;
741 // try to make a GUI message
742 Alert::error(_("LyX crashed!"), msg, true);
745 // Deinstall the signal handlers
747 signal(SIGHUP, SIG_DFL);
749 signal(SIGINT, SIG_DFL);
750 signal(SIGFPE, SIG_DFL);
751 signal(SIGSEGV, SIG_DFL);
752 signal(SIGTERM, SIG_DFL);
755 if (err_sig == SIGSEGV ||
756 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
758 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
761 // with abort() it crashes again.
774 void LyX::printError(ErrorItem const & ei)
776 docstring tmp = _("LyX: ") + ei.error + char_type(':')
778 cerr << to_utf8(tmp) << endl;
781 #if defined (USE_MACOSX_PACKAGING)
783 // Unexposed--extract an environment variable name from its NAME=VALUE
785 std::string varname(const char* line)
787 size_t nameLen = strcspn(line, "=");
788 if (nameLen == strlen(line)) {
789 return std::string();
791 return std::string(line, nameLen);
796 void cleanDuplicateEnvVars()
798 std::set<std::string> seen;
799 std::set<std::string> dupes;
801 // Create a list of the environment variables that appear more than once
802 for (char **read = *_NSGetEnviron(); *read; read++) {
803 std::string name = varname(*read);
804 if (name.size() == 0) {
807 if (seen.find(name) != seen.end()) {
814 // Loop over the list of duplicated variables
815 std::set<std::string>::iterator dupe = dupes.begin();
816 std::set<std::string>::iterator const dend = dupes.end();
817 for (; dupe != dend; ++dupe) {
818 const char *name = (*dupe).c_str();
819 char *val = getenv(name);
821 LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
822 // unsetenv removes *all* instances of the variable from the environment
825 // replace with the value from getenv (in practice appears to be the
826 // first value in the list)
827 setenv(name, val, 0);
834 static void initTemplatePath()
836 FileName const package_template_path =
837 FileName(addName(package().system_support().absFileName(), "templates"));
839 if (lyxrc.template_path.empty()) {
840 lyxrc.template_path = package_template_path.absFileName();
842 #if defined (USE_MACOSX_PACKAGING)
843 FileName const user_template_path =
844 FileName(addName(package().user_support().absFileName(), "templates"));
846 if (package_template_path != FileName(lyxrc.template_path) &&
847 user_template_path != FileName(lyxrc.template_path))
851 FileName const user_template_link =
852 FileName(addName(user_template_path.absFileName(),"SystemTemplates"));
853 if (user_template_link.isSymLink() && !equivalent(user_template_link, package_template_path)) {
854 user_template_link.removeFile();
856 if (!user_template_link.exists()) {
857 if (!package_template_path.link(user_template_link)) {
858 FileName const user_support = package().user_support();
859 if (user_support.exists() && user_support.isDirectory()) {
860 LYXERR(Debug::INIT, "Cannot create symlink " + user_template_link.absFileName());
861 lyxrc.template_path = package_template_path.absFileName();
865 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
867 lyxrc.template_path = user_template_path.absFileName();
875 signal(SIGHUP, error_handler);
877 signal(SIGFPE, error_handler);
878 signal(SIGSEGV, error_handler);
879 signal(SIGINT, error_handler);
880 signal(SIGTERM, error_handler);
881 // SIGPIPE can be safely ignored.
883 #if defined (USE_MACOSX_PACKAGING)
884 cleanDuplicateEnvVars();
887 lyxrc.tempdir_path = package().temp_dir().absFileName();
888 lyxrc.document_path = package().document_dir().absFileName();
890 if (lyxrc.example_path.empty()) {
891 lyxrc.example_path = addPath(package().system_support().absFileName(),
896 // init LyXDir environment variable
897 string const lyx_dir = package().lyx_dir().absFileName();
898 LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
899 if (!setEnv("LyXDir", lyx_dir))
900 LYXERR(Debug::INIT, "\t... failed!");
902 if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
903 // -userdir was given on the command line.
904 // Make it available to child processes, otherwise tex2lyx
905 // would not find all layout files, and other converters might
907 string const user_dir = package().user_support().absFileName();
908 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
910 if (!setEnv(LYX_USERDIR_VER, user_dir))
911 LYXERR(Debug::INIT, "\t... failed!");
915 // Read configuration files
918 // This one may have been distributed along with LyX.
919 if (!readRcFile("lyxrc.dist"))
922 // Set the PATH correctly.
923 #if !defined (USE_POSIX_PACKAGING) && !defined (USE_HAIKU_PACKAGING)
924 // Add the directory containing the LyX executable to the path
925 // so that LyX can find things like tex2lyx.
926 if (package().build_support().empty())
927 prependEnvPath("PATH", package().binary_dir().absFileName());
930 // Add the directory containing the dt2dv and dv2dt executables to the path
932 if (!package().build_support().empty()) {
933 // dtl executables should be in the same dir ar tex2lyx
934 dtldir = package().binary_dir();
937 dtldir = FileName(addName(package().system_support().absFileName(), "extratools"));
940 string dtlexe = "dt2dv.exe";
942 string dtlexe = "dt2dv";
944 FileName const dt2dv = FileName(addName(dtldir.absFileName(), dtlexe));
946 prependEnvPath("PATH", dtldir.absFileName());
948 if (!lyxrc.path_prefix.empty())
949 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
951 // Check that user LyX directory is ok.
953 string const lock_file = package().getConfigureLockName();
954 int fd = fileLock(lock_file.c_str());
956 if (queryUserLyXDir(package().explicit_user_support())) {
957 package().reconfigureUserLyXDir("");
958 // Now the user directory is present on first start.
961 fileUnlock(fd, lock_file.c_str());
965 // No need for a splash when there is no GUI
967 // Default is to overwrite the main file during export, unless
968 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
969 if (force_overwrite == UNSPECIFIED) {
970 string const what = getEnv("LYX_FORCE_OVERWRITE");
972 force_overwrite = ALL_FILES;
973 else if (what == "none")
974 force_overwrite = NO_FILES;
976 force_overwrite = MAIN_FILE;
980 // This one is generated in user_support directory by lib/configure.py.
981 if (!readRcFile("lyxrc.defaults"))
984 // Query the OS to know what formats are viewed natively
985 theFormats().setAutoOpen();
987 // Read lyxrc.dist again to be able to override viewer auto-detection.
988 readRcFile("lyxrc.dist");
990 system_lyxrc = lyxrc;
991 theSystemFormats() = theFormats();
992 pimpl_->system_converters_ = pimpl_->converters_;
993 pimpl_->system_movers_ = pimpl_->movers_;
994 system_lcolor = lcolor;
996 // This one is edited through the preferences dialog.
997 if (!readRcFile("preferences", true))
1000 // The language may have been set to someting useful through prefs
1003 if (!readEncodingsFile("encodings", "unicodesymbols"))
1005 if (!readLanguagesFile("languages"))
1008 LYXERR(Debug::INIT, "Reading layouts...");
1010 LayoutFileList::get().read();
1012 theModuleList.read();
1013 //... and the cite engines
1014 theCiteEnginesList.read();
1016 // read keymap and ui files in batch mode as well
1017 // because InsetInfo needs to know these to produce
1018 // the correct output
1020 // Set up command definitions
1021 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
1025 pimpl_->toplevel_keymap_.read("site");
1026 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
1027 // load user bind file user.bind
1028 pimpl_->toplevel_keymap_.read("user", nullptr, KeyMap::MissingOK);
1030 if (lyxerr.debugging(Debug::LYXRC))
1033 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
1034 // Prepend path prefix a second time to take the user preferences into a account
1035 if (!lyxrc.path_prefix.empty())
1036 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
1038 FileName const document_path(lyxrc.document_path);
1039 if (document_path.exists() && document_path.isDirectory())
1040 package().document_dir() = document_path;
1042 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
1043 if (package().temp_dir().empty()) {
1044 Alert::error(_("Could not create temporary directory"),
1045 bformat(_("Could not create a temporary directory in\n"
1047 "Make sure that this path exists and is writable and try again."),
1048 from_utf8(lyxrc.tempdir_path)));
1049 // createLyXTmpDir() tries sufficiently hard to create a
1050 // usable temp dir, so the probability to come here is
1051 // close to zero. We therefore don't try to overcome this
1052 // problem with e.g. asking the user for a new path and
1053 // trying again but simply exit.
1057 LYXERR(Debug::INIT, "LyX tmp dir: `"
1058 << package().temp_dir().absFileName() << '\'');
1060 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
1061 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1063 // This must happen after package initialization and after lyxrc is
1064 // read, therefore it can't be done by a static object.
1065 ConverterCache::init();
1071 void emergencyCleanup()
1073 // what to do about tmpfiles is non-obvious. we would
1074 // like to delete any we find, but our lyxdir might
1075 // contain documents etc. which might be helpful on
1078 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1080 if (singleton_->pimpl_->lyx_server_)
1081 singleton_->pimpl_->lyx_server_->emergencyCleanup();
1082 singleton_->pimpl_->lyx_server_.reset();
1083 singleton_->pimpl_->lyx_socket_.reset();
1088 bool LyX::queryUserLyXDir(bool explicit_userdir)
1090 // Does user directory exist?
1091 FileName const sup = package().user_support();
1092 if (sup.exists() && sup.isDirectory()) {
1093 first_start = false;
1095 return configFileNeedsUpdate("lyxrc.defaults")
1096 || configFileNeedsUpdate("lyxmodules.lst")
1097 || configFileNeedsUpdate("textclass.lst")
1098 || configFileNeedsUpdate("packages.lst")
1099 || configFileNeedsUpdate("lyxciteengines.lst")
1100 || configFileNeedsUpdate("xtemplates.lst");
1103 first_start = !explicit_userdir;
1105 // If the user specified explicitly a directory, ask whether
1106 // to create it. If the user says "no", then exit.
1107 if (explicit_userdir &&
1109 _("Missing user LyX directory"),
1110 bformat(_("You have specified a non-existent user "
1111 "LyX directory, %1$s.\n"
1112 "It is needed to keep your own configuration."),
1113 from_utf8(package().user_support().absFileName())),
1115 _("&Create directory"),
1117 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1118 earlyExit(EXIT_FAILURE);
1121 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1122 from_utf8(sup.absFileName()))) << endl;
1124 if (!sup.createDirectory(0755)) {
1125 // Failed, so let's exit.
1126 lyxerr << to_utf8(_("Failed to create directory. Perhaps wrong -userdir option?\nExiting."))
1128 earlyExit(EXIT_FAILURE);
1135 bool LyX::readRcFile(string const & name, bool check_format)
1137 LYXERR(Debug::INIT, "About to read " << name << "... ");
1139 FileName const lyxrc_path = libFileSearch(string(), name);
1140 if (lyxrc_path.empty()) {
1141 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1143 // This was the previous logic, but can it be right??
1146 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1147 bool const success = lyxrc.read(lyxrc_path, check_format);
1149 showFileError(name);
1153 // Read the languages file `name'
1154 bool LyX::readLanguagesFile(string const & name)
1156 LYXERR(Debug::INIT, "About to read " << name << "...");
1158 FileName const lang_path = libFileSearch(string(), name);
1159 if (lang_path.empty()) {
1160 showFileError(name);
1163 languages.read(lang_path);
1168 // Read the encodings file `name'
1169 bool LyX::readEncodingsFile(string const & enc_name,
1170 string const & symbols_name)
1172 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1173 << symbols_name << "...");
1175 FileName const symbols_path = libFileSearch(string(), symbols_name);
1176 if (symbols_path.empty()) {
1177 showFileError(symbols_name);
1181 FileName const enc_path = libFileSearch(string(), enc_name);
1182 if (enc_path.empty()) {
1183 showFileError(enc_name);
1186 encodings.read(enc_path, symbols_path);
1193 /// return the the number of arguments consumed
1194 typedef function<int(string const &, string const &, string &)> cmd_helper;
1196 int parse_dbg(string const & arg, string const &, string &)
1199 cout << to_utf8(_("List of supported debug flags:")) << endl;
1200 Debug::showTags(cout);
1203 string bad = Debug::badValue(arg);
1205 lyxerr.setLevel(Debug::value(arg));
1206 Debug::showLevel(lyxerr, lyxerr.level());
1208 cout << to_utf8(bformat(_("Bad debug value `%1$s'. Exiting."),
1209 from_utf8(bad))) << endl;
1216 int parse_help(string const &, string const &, string &)
1219 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1220 "Command line switches (case sensitive):\n"
1221 "\t-help summarize LyX usage\n"
1222 "\t-userdir dir set user directory to dir\n"
1223 "\t-sysdir dir set system directory to dir\n"
1224 "\t-geometry WxH+X+Y set geometry of the main window\n"
1225 "\t-dbg feature[,feature]...\n"
1226 " select the features to debug.\n"
1227 " Type `lyx -dbg' to see the list of features\n"
1228 "\t-x [--execute] command\n"
1229 " where command is a lyx command.\n"
1230 "\t-e [--export] fmt\n"
1231 " where fmt is the export format of choice. Look in\n"
1232 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1233 " to see which parameter (which differs from the format name\n"
1234 " in the File->Export menu) should be passed. To export to\n"
1235 " the document's default output format, use 'default'.\n"
1236 " Note that the order of -e and -x switches matters.\n"
1237 "\t-E [--export-to] fmt filename\n"
1238 " where fmt is the export format of choice (see --export),\n"
1239 " and filename is the destination filename.\n"
1240 "\t-i [--import] fmt file.xxx\n"
1241 " where fmt is the import format of choice\n"
1242 " and file.xxx is the file to be imported.\n"
1243 "\t-f [--force-overwrite] what\n"
1244 " where what is either `all', `main' or `none',\n"
1245 " specifying whether all files, main file only, or no files,\n"
1246 " respectively, are to be overwritten during a batch export.\n"
1247 " Anything else is equivalent to `all', but is not consumed.\n"
1248 "\t--ignore-error-message which\n"
1249 " allows you to ignore specific LaTeX error messages.\n"
1250 " Do not use for final documents! Currently supported values:\n"
1251 " * missing_glyphs: Fontspec `missing glyphs' error.\n"
1252 "\t-n [--no-remote]\n"
1253 " open documents in a new instance\n"
1255 " open documents in an already running instance\n"
1256 " (a working lyxpipe is needed)\n"
1257 "\t-v [--verbose]\n"
1258 " report on terminal about spawned commands.\n"
1259 "\t-batch execute commands without launching GUI and exit.\n"
1260 "\t-version summarize version and build info\n"
1261 "Check the LyX man page for more details.")) << endl;
1267 int parse_version(string const &, string const &, string &)
1269 cout << "LyX " << lyx_version
1270 << " (" << lyx_release_date << ")" << endl;
1271 if (string(lyx_git_commit_hash) != "none")
1272 cout << to_utf8(_(" Git commit hash "))
1273 << string(lyx_git_commit_hash).substr(0,8) << endl;
1274 cout << lyx_version_info << endl;
1280 int parse_sysdir(string const & arg, string const &, string &)
1283 Alert::error(_("No system directory"),
1284 _("Missing directory for -sysdir switch"));
1287 cl_system_support = arg;
1292 int parse_userdir(string const & arg, string const &, string &)
1295 Alert::error(_("No user directory"),
1296 _("Missing directory for -userdir switch"));
1299 cl_user_support = arg;
1304 int parse_execute(string const & arg, string const &, string & batch)
1307 Alert::error(_("Incomplete command"),
1308 _("Missing command string after --execute switch"));
1316 int parse_export_to(string const & type, string const & output_file, string & batch)
1319 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1320 "--export-to switch")) << endl;
1323 if (output_file.empty()) {
1324 lyxerr << to_utf8(_("Missing destination filename after "
1325 "--export-to switch")) << endl;
1328 batch = "buffer-export " + type + " " + output_file;
1334 int parse_export(string const & type, string const &, string & batch)
1337 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1338 "--export switch")) << endl;
1341 batch = "buffer-export " + type;
1347 int parse_import(string const & type, string const & file, string & batch)
1350 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1351 "--import switch")) << endl;
1355 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1358 batch = "buffer-import " + type + ' ' + file;
1363 int parse_geometry(string const & arg1, string const &, string &)
1366 // don't remove "-geometry", it will be pruned out later in the
1367 // frontend if need be.
1372 int parse_batch(string const &, string const &, string &)
1379 int parse_noremote(string const &, string const &, string &)
1381 run_mode = NEW_INSTANCE;
1386 int parse_remote(string const &, string const &, string &)
1388 run_mode = USE_REMOTE;
1393 int parse_verbose(string const &, string const &, string &)
1400 int parse_ignore_error_message(string const & arg1, string const &, string &)
1402 if (arg1 == "missing_glyphs") {
1403 ignore_missing_glyphs = true;
1410 int parse_force(string const & arg, string const &, string &)
1413 force_overwrite = ALL_FILES;
1415 } else if (arg == "main") {
1416 force_overwrite = MAIN_FILE;
1418 } else if (arg == "none") {
1419 force_overwrite = NO_FILES;
1422 force_overwrite = ALL_FILES;
1430 void LyX::easyParse(int & argc, char * argv[])
1432 map<string, cmd_helper> cmdmap;
1434 cmdmap["-dbg"] = parse_dbg;
1435 cmdmap["-help"] = parse_help;
1436 cmdmap["--help"] = parse_help;
1437 cmdmap["-version"] = parse_version;
1438 cmdmap["--version"] = parse_version;
1439 cmdmap["-sysdir"] = parse_sysdir;
1440 cmdmap["-userdir"] = parse_userdir;
1441 cmdmap["-x"] = parse_execute;
1442 cmdmap["--execute"] = parse_execute;
1443 cmdmap["-e"] = parse_export;
1444 cmdmap["--export"] = parse_export;
1445 cmdmap["-E"] = parse_export_to;
1446 cmdmap["--export-to"] = parse_export_to;
1447 cmdmap["-i"] = parse_import;
1448 cmdmap["--import"] = parse_import;
1449 cmdmap["-geometry"] = parse_geometry;
1450 cmdmap["-batch"] = parse_batch;
1451 cmdmap["-f"] = parse_force;
1452 cmdmap["--force-overwrite"] = parse_force;
1453 cmdmap["-n"] = parse_noremote;
1454 cmdmap["--no-remote"] = parse_noremote;
1455 cmdmap["-r"] = parse_remote;
1456 cmdmap["--remote"] = parse_remote;
1457 cmdmap["-v"] = parse_verbose;
1458 cmdmap["--verbose"] = parse_verbose;
1459 cmdmap["--ignore-error-message"] = parse_ignore_error_message;
1461 for (int i = 1; i < argc; ++i) {
1462 map<string, cmd_helper>::const_iterator it
1463 = cmdmap.find(argv[i]);
1465 // don't complain if not found - may be parsed later
1466 if (it == cmdmap.end())
1470 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1472 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1475 int const remove = 1 + it->second(arg, arg2, batch);
1477 pimpl_->batch_commands.push_back(batch);
1479 // Now, remove used arguments by shifting
1480 // the following ones remove places down.
1482 os::remove_internal_args(i, remove);
1484 for (int j = i; j < argc; ++j)
1485 argv[j] = argv[j + remove];
1492 FuncStatus getStatus(FuncRequest const & action)
1495 return theApp()->getStatus(action);
1499 DispatchResult const & dispatch(FuncRequest const & action)
1502 return theApp()->dispatch(action);
1506 void dispatch(FuncRequest const & action, DispatchResult & dr)
1509 theApp()->dispatch(action, dr);
1513 vector<string> & theFilesToLoad()
1515 LAPPERR(singleton_);
1516 return singleton_->pimpl_->files_to_load_;
1520 BufferList & theBufferList()
1522 LAPPERR(singleton_);
1523 return singleton_->pimpl_->buffer_list_;
1527 Server & theServer()
1529 // FIXME: this should not be use_gui dependent
1531 LAPPERR(singleton_);
1532 return *singleton_->pimpl_->lyx_server_;
1536 ServerSocket & theServerSocket()
1538 // FIXME: this should not be use_gui dependent
1540 LAPPERR(singleton_);
1541 return *singleton_->pimpl_->lyx_socket_;
1545 KeyMap & theTopLevelKeymap()
1547 LAPPERR(singleton_);
1548 return singleton_->pimpl_->toplevel_keymap_;
1552 Formats & theFormats()
1554 LAPPERR(singleton_);
1555 return singleton_->pimpl_->formats_;
1559 Formats & theSystemFormats()
1561 LAPPERR(singleton_);
1562 return singleton_->pimpl_->system_formats_;
1566 Converters & theConverters()
1568 LAPPERR(singleton_);
1569 return singleton_->pimpl_->converters_;
1573 Converters & theSystemConverters()
1575 LAPPERR(singleton_);
1576 return singleton_->pimpl_->system_converters_;
1580 Movers & theMovers()
1582 LAPPERR(singleton_);
1583 return singleton_->pimpl_->movers_;
1587 Mover const & getMover(string const & fmt)
1589 LAPPERR(singleton_);
1590 return singleton_->pimpl_->movers_(fmt);
1594 void setMover(string const & fmt, string const & command)
1596 LAPPERR(singleton_);
1597 singleton_->pimpl_->movers_.set(fmt, command);
1601 Movers & theSystemMovers()
1603 LAPPERR(singleton_);
1604 return singleton_->pimpl_->system_movers_;
1608 Messages const & getMessages(string const & language)
1610 LAPPERR(singleton_);
1611 return singleton_->messages(language);
1615 Messages const & getGuiMessages()
1617 LAPPERR(singleton_);
1618 return singleton_->messages(Messages::guiLanguage());
1622 Session & theSession()
1624 LAPPERR(singleton_);
1625 return *singleton_->pimpl_->session_.get();
1629 LaTeXFonts & theLaTeXFonts()
1631 LAPPERR(singleton_);
1632 if (!singleton_->pimpl_->latexfonts_)
1633 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1634 return *singleton_->pimpl_->latexfonts_;
1638 CmdDef & theTopLevelCmdDef()
1640 LAPPERR(singleton_);
1641 return singleton_->pimpl_->toplevel_cmddef_;
1645 SpellChecker * theSpellChecker()
1647 if (!singleton_->pimpl_->spell_checker_)
1649 return singleton_->pimpl_->spell_checker_;
1653 void setSpellChecker()
1655 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1656 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1658 if (lyxrc.spellchecker == "native") {
1659 #if defined(USE_MACOSX_PACKAGING)
1660 if (!singleton_->pimpl_->apple_spell_checker_)
1661 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1662 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1664 singleton_->pimpl_->spell_checker_ = nullptr;
1666 } else if (lyxrc.spellchecker == "aspell") {
1667 #if defined(USE_ASPELL)
1668 if (!singleton_->pimpl_->aspell_checker_)
1669 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1670 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1672 singleton_->pimpl_->spell_checker_ = nullptr;
1674 } else if (lyxrc.spellchecker == "enchant") {
1675 #if defined(USE_ENCHANT)
1676 if (!singleton_->pimpl_->enchant_checker_)
1677 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1678 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1680 singleton_->pimpl_->spell_checker_ = nullptr;
1682 } else if (lyxrc.spellchecker == "hunspell") {
1683 #if defined(USE_HUNSPELL)
1684 if (!singleton_->pimpl_->hunspell_checker_)
1685 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1686 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1688 singleton_->pimpl_->spell_checker_ = nullptr;
1691 singleton_->pimpl_->spell_checker_ = nullptr;
1693 if (singleton_->pimpl_->spell_checker_) {
1694 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1695 singleton_->pimpl_->spell_checker_->advanceChangeNumber();