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 "EnchantChecker.h"
32 #include "ErrorList.h"
34 #include "FuncStatus.h"
35 #include "HunspellChecker.h"
38 #include "LaTeXFonts.h"
39 #include "LayoutFile.h"
42 #include "LyXAction.h"
44 #include "ModuleList.h"
47 #include "ServerSocket.h"
51 #include "frontends/alert.h"
52 #include "frontends/Application.h"
54 #include "support/ConsoleApplication.h"
55 #include "support/convert.h"
56 #include "support/lassert.h"
57 #include "support/debug.h"
58 #include "support/environment.h"
59 #include "support/ExceptionMessage.h"
60 #include "support/filetools.h"
61 #include "support/gettext.h"
62 #include "support/lstrings.h"
63 #include "support/Messages.h"
64 #include "support/os.h"
65 #include "support/Package.h"
66 #include "support/unique_ptr.h"
77 #include <qglobal.h> // For QT_VERSION
80 using namespace lyx::support;
82 #if defined (USE_MACOSX_PACKAGING)
83 #include <crt_externs.h>
88 namespace Alert = frontend::Alert;
89 namespace os = support::os;
93 // Are we using the GUI at all? We default to true and this is changed
94 // to false when the export feature is used.
99 // Report on the terminal about spawned commands. The default is false
100 // and can be changed with the option -v (--verbose).
102 bool verbose = false;
105 // Do not treat the "missing glyphs" warning of fontspec as an error message.
106 // The default is false and can be changed with the option
107 // --ignore-error-message missing_glyphs
108 // This is used in automated testing.
109 bool ignore_missing_glyphs = false;
112 // We default to open documents in an already running instance, provided that
113 // the lyxpipe has been setup. This can be overridden either on the command
114 // line or through preference settings.
116 RunMode run_mode = PREFERRED;
119 // Tell what files can be silently overwritten during batch export.
120 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
121 // Unless specified on command line (through the -f switch) or through the
122 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
124 OverwriteFiles force_overwrite = UNSPECIFIED;
127 // Scale the GUI by this factor. This works whether we have a HiDpi screen
128 // or not and scales everything, also fonts. Can only be changed by setting
129 // the QT_SCALE_FACTOR environment variable before launching LyX and only
130 // works properly with Qt 5.6 or higher.
132 double qt_scale_factor = 1.0;
137 // Filled with the command line arguments "foo" of "-sysdir foo" or
139 string cl_system_support;
140 string cl_user_support;
144 LyX * singleton_ = 0;
146 void showFileError(string const & error)
148 Alert::warning(_("Could not read configuration file"),
149 bformat(_("Error while reading the configuration file\n%1$s.\n"
150 "Please check your installation."), from_utf8(error)));
155 /// The main application class private implementation.
158 : latexfonts_(0), spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
164 delete apple_spell_checker_;
165 delete aspell_checker_;
166 delete enchant_checker_;
167 delete hunspell_checker_;
171 BufferList buffer_list_;
173 KeyMap toplevel_keymap_;
175 CmdDef toplevel_cmddef_;
177 unique_ptr<Server> lyx_server_;
179 unique_ptr<ServerSocket> lyx_socket_;
181 unique_ptr<frontend::Application> application_;
182 /// lyx session, containing lastfiles, lastfilepos, and lastopened
183 unique_ptr<Session> session_;
185 /// Files to load at start.
186 vector<string> files_to_load_;
188 /// The messages translators.
189 map<string, Messages> messages_;
191 /// The file converters.
192 Converters converters_;
193 /// The system converters after reading lyxrc.defaults.
194 Converters system_converters_;
196 /// Global format information
198 /// The system formats after reading lyxrc.defaults.
199 Formats system_formats_;
205 Movers system_movers_;
207 /// the parsed command line batch command if any
208 vector<string> batch_commands;
211 LaTeXFonts * latexfonts_;
214 SpellChecker * spell_checker_;
216 SpellChecker * apple_spell_checker_;
218 SpellChecker * aspell_checker_;
220 SpellChecker * enchant_checker_;
222 SpellChecker * hunspell_checker_;
226 /// The main application class for console mode
227 class LyXConsoleApp : public ConsoleApplication
230 LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
231 : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
232 argc_(argc), argv_(argv)
237 int const exit_status = lyx_->execWithoutGui(argc_, argv_);
248 frontend::Application * theApp()
251 return singleton_->pimpl_->application_.get();
264 void lyx_exit(int exit_code)
267 // Something wrong happened so better save everything, just in
272 // Properly crash in debug mode in order to get a useful backtrace.
276 // In release mode, try to exit gracefully.
278 theApp()->exit(exit_code);
292 Messages & LyX::messages(string const & language)
294 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
296 if (it != pimpl_->messages_.end())
299 pair<map<string, Messages>::iterator, bool> result =
300 pimpl_->messages_.insert(make_pair(language, Messages(language)));
302 LATTEST(result.second);
303 return result.first->second;
307 int LyX::exec(int & argc, char * argv[])
309 // Minimal setting of locale before parsing command line
311 init_package(os::utf8_argv(0), string(), string());
312 // we do not get to this point when init_package throws an exception
314 } catch (ExceptionMessage const & message) {
315 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
318 // Here we need to parse the command line. At least
319 // we need to parse for "-dbg" and "-help"
320 easyParse(argc, argv);
322 #if QT_VERSION >= 0x050600
323 // Check whether Qt will scale all GUI elements and accordingly
324 // set the scale factor so that to avoid blurred images and text
325 char const * const scale_factor = getenv("QT_SCALE_FACTOR");
327 qt_scale_factor = convert<double>(scale_factor);
328 if (qt_scale_factor < 1.0)
329 qt_scale_factor = 1.0;
334 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
335 } catch (ExceptionMessage const & message) {
336 if (message.type_ == ErrorException) {
337 Alert::error(message.title_, message.details_);
339 } else if (message.type_ == WarningException) {
340 Alert::warning(message.title_, message.details_);
344 // Reinit the messages machinery in case package() knows
345 // something interesting about the locale directory.
349 LyXConsoleApp app(this, argc, argv);
351 // Reestablish our defaults, as Qt overwrites them
352 // after creating app
358 // Let the frontend parse and remove all arguments that it knows
359 pimpl_->application_.reset(createApplication(argc, argv));
361 // Reestablish our defaults, as Qt overwrites them
362 // after createApplication()
365 // Parse and remove all known arguments in the LyX singleton
366 // Give an error for all remaining ones.
367 int exit_status = init(argc, argv);
369 // Kill the application object before exiting.
370 pimpl_->application_.reset();
376 // If not otherwise specified by a command line option or
377 // by preferences, we default to reuse a running instance.
378 if (run_mode == PREFERRED)
379 run_mode = USE_REMOTE;
382 /* Create a CoreApplication class that will provide the main event loop
383 * and the socket callback registering. With Qt, only QtCore
384 * library would be needed.
385 * When this is done, a server_mode could be created and the following two
386 * line would be moved out from here.
387 * However, note that the first of the two lines below triggers the
388 * "single instance" behavior, which should occur right at this point.
390 // Note: socket callback must be registered after init(argc, argv)
391 // such that package().temp_dir() is properly initialized.
392 pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
393 pimpl_->lyx_socket_.reset(new ServerSocket(
394 FileName(package().temp_dir().absFileName() + "/lyxsocket")));
396 // Start the real execution loop.
397 if (!pimpl_->lyx_server_->deferredLoadingToOtherInstance())
398 exit_status = pimpl_->application_->exec();
399 else if (!pimpl_->files_to_load_.empty()) {
400 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
401 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
402 lyxerr << _("The following files could not be loaded:") << endl;
403 for (; it != end; ++it)
404 lyxerr << *it << endl;
413 void LyX::prepareExit()
415 // Clear the clipboard and selection stack:
416 cap::clearCutStack();
417 cap::clearSelection();
419 // Write the index file of the converter cache
420 ConverterCache::get().writeIndex();
422 // close buffers first
423 pimpl_->buffer_list_.closeAll();
425 // register session changes and shutdown server and socket
427 if (pimpl_->session_)
428 pimpl_->session_->writeFile();
429 pimpl_->session_.reset();
430 pimpl_->lyx_server_.reset();
431 pimpl_->lyx_socket_.reset();
434 // do any other cleanup procedures now
435 if (package().temp_dir() != package().system_temp_dir()) {
436 string const abs_tmpdir = package().temp_dir().absFileName();
437 if (!contains(package().temp_dir().absFileName(), "lyx_tmpdir")) {
438 docstring const msg =
439 bformat(_("%1$s does not appear like a LyX created temporary directory."),
440 from_utf8(abs_tmpdir));
441 Alert::warning(_("Cannot remove temporary directory"), msg);
443 LYXERR(Debug::INFO, "Deleting tmp dir "
444 << package().temp_dir().absFileName());
445 if (!package().temp_dir().destroyDirectory()) {
446 LYXERR0(bformat(_("Unable to remove the temporary directory %1$s"),
447 from_utf8(package().temp_dir().absFileName())));
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", "splash.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());
909 if (!lyxrc.path_prefix.empty())
910 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
912 // Check that user LyX directory is ok.
914 string const lock_file = package().getConfigureLockName();
915 int fd = fileLock(lock_file.c_str());
917 if (queryUserLyXDir(package().explicit_user_support())) {
918 package().reconfigureUserLyXDir("");
919 // Now the user directory is present on first start.
922 fileUnlock(fd, lock_file.c_str());
926 // No need for a splash when there is no GUI
928 // Default is to overwrite the main file during export, unless
929 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
930 if (force_overwrite == UNSPECIFIED) {
931 string const what = getEnv("LYX_FORCE_OVERWRITE");
933 force_overwrite = ALL_FILES;
934 else if (what == "none")
935 force_overwrite = NO_FILES;
937 force_overwrite = MAIN_FILE;
941 // This one is generated in user_support directory by lib/configure.py.
942 if (!readRcFile("lyxrc.defaults"))
945 // Query the OS to know what formats are viewed natively
946 theFormats().setAutoOpen();
948 // Read lyxrc.dist again to be able to override viewer auto-detection.
949 readRcFile("lyxrc.dist");
951 system_lyxrc = lyxrc;
952 theSystemFormats() = theFormats();
953 pimpl_->system_converters_ = pimpl_->converters_;
954 pimpl_->system_movers_ = pimpl_->movers_;
955 system_lcolor = lcolor;
957 // This one is edited through the preferences dialog.
958 if (!readRcFile("preferences", true))
961 // The language may have been set to someting useful through prefs
964 if (!readEncodingsFile("encodings", "unicodesymbols"))
966 if (!readLanguagesFile("languages"))
969 LYXERR(Debug::INIT, "Reading layouts...");
971 LayoutFileList::get().read();
973 theModuleList.read();
974 //... and the cite engines
975 theCiteEnginesList.read();
977 // read keymap and ui files in batch mode as well
978 // because InsetInfo needs to know these to produce
979 // the correct output
981 // Set up command definitions
982 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
986 pimpl_->toplevel_keymap_.read("site");
987 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
988 // load user bind file user.bind
989 pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
991 if (lyxerr.debugging(Debug::LYXRC))
994 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
995 // Prepend path prefix a second time to take the user preferences into a account
996 if (!lyxrc.path_prefix.empty())
997 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
999 FileName const document_path(lyxrc.document_path);
1000 if (document_path.exists() && document_path.isDirectory())
1001 package().document_dir() = document_path;
1003 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
1004 if (package().temp_dir().empty()) {
1005 Alert::error(_("Could not create temporary directory"),
1006 bformat(_("Could not create a temporary directory in\n"
1008 "Make sure that this path exists and is writable and try again."),
1009 from_utf8(lyxrc.tempdir_path)));
1010 // createLyXTmpDir() tries sufficiently hard to create a
1011 // usable temp dir, so the probability to come here is
1012 // close to zero. We therefore don't try to overcome this
1013 // problem with e.g. asking the user for a new path and
1014 // trying again but simply exit.
1018 LYXERR(Debug::INIT, "LyX tmp dir: `"
1019 << package().temp_dir().absFileName() << '\'');
1021 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
1022 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1024 // This must happen after package initialization and after lyxrc is
1025 // read, therefore it can't be done by a static object.
1026 ConverterCache::init();
1032 void emergencyCleanup()
1034 // what to do about tmpfiles is non-obvious. we would
1035 // like to delete any we find, but our lyxdir might
1036 // contain documents etc. which might be helpful on
1039 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1041 if (singleton_->pimpl_->lyx_server_)
1042 singleton_->pimpl_->lyx_server_->emergencyCleanup();
1043 singleton_->pimpl_->lyx_server_.reset();
1044 singleton_->pimpl_->lyx_socket_.reset();
1049 bool LyX::queryUserLyXDir(bool explicit_userdir)
1051 // Does user directory exist?
1052 FileName const sup = package().user_support();
1053 if (sup.exists() && sup.isDirectory()) {
1054 first_start = false;
1056 return configFileNeedsUpdate("lyxrc.defaults")
1057 || configFileNeedsUpdate("lyxmodules.lst")
1058 || configFileNeedsUpdate("textclass.lst")
1059 || configFileNeedsUpdate("packages.lst")
1060 || configFileNeedsUpdate("lyxciteengines.lst")
1061 || configFileNeedsUpdate("xtemplates.lst");
1064 first_start = !explicit_userdir;
1066 // If the user specified explicitly a directory, ask whether
1067 // to create it. If the user says "no", then exit.
1068 if (explicit_userdir &&
1070 _("Missing user LyX directory"),
1071 bformat(_("You have specified a non-existent user "
1072 "LyX directory, %1$s.\n"
1073 "It is needed to keep your own configuration."),
1074 from_utf8(package().user_support().absFileName())),
1076 _("&Create directory"),
1078 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1079 earlyExit(EXIT_FAILURE);
1082 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1083 from_utf8(sup.absFileName()))) << endl;
1085 if (!sup.createDirectory(0755)) {
1086 // Failed, so let's exit.
1087 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1089 earlyExit(EXIT_FAILURE);
1096 bool LyX::readRcFile(string const & name, bool check_format)
1098 LYXERR(Debug::INIT, "About to read " << name << "... ");
1100 FileName const lyxrc_path = libFileSearch(string(), name);
1101 if (lyxrc_path.empty()) {
1102 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1104 // This was the previous logic, but can it be right??
1107 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1108 bool const success = lyxrc.read(lyxrc_path, check_format);
1110 showFileError(name);
1114 // Read the languages file `name'
1115 bool LyX::readLanguagesFile(string const & name)
1117 LYXERR(Debug::INIT, "About to read " << name << "...");
1119 FileName const lang_path = libFileSearch(string(), name);
1120 if (lang_path.empty()) {
1121 showFileError(name);
1124 languages.read(lang_path);
1129 // Read the encodings file `name'
1130 bool LyX::readEncodingsFile(string const & enc_name,
1131 string const & symbols_name)
1133 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1134 << symbols_name << "...");
1136 FileName const symbols_path = libFileSearch(string(), symbols_name);
1137 if (symbols_path.empty()) {
1138 showFileError(symbols_name);
1142 FileName const enc_path = libFileSearch(string(), enc_name);
1143 if (enc_path.empty()) {
1144 showFileError(enc_name);
1147 encodings.read(enc_path, symbols_path);
1154 /// return the the number of arguments consumed
1155 typedef function<int(string const &, string const &, string &)> cmd_helper;
1157 int parse_dbg(string const & arg, string const &, string &)
1160 cout << to_utf8(_("List of supported debug flags:")) << endl;
1161 Debug::showTags(cout);
1164 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1166 lyxerr.setLevel(Debug::value(arg));
1167 Debug::showLevel(lyxerr, lyxerr.level());
1172 int parse_help(string const &, string const &, string &)
1175 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1176 "Command line switches (case sensitive):\n"
1177 "\t-help summarize LyX usage\n"
1178 "\t-userdir dir set user directory to dir\n"
1179 "\t-sysdir dir set system directory to dir\n"
1180 "\t-geometry WxH+X+Y set geometry of the main window\n"
1181 "\t-dbg feature[,feature]...\n"
1182 " select the features to debug.\n"
1183 " Type `lyx -dbg' to see the list of features\n"
1184 "\t-x [--execute] command\n"
1185 " where command is a lyx command.\n"
1186 "\t-e [--export] fmt\n"
1187 " where fmt is the export format of choice. Look in\n"
1188 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1189 " to see which parameter (which differs from the format name\n"
1190 " in the File->Export menu) should be passed. To export to\n"
1191 " the document's default output format, use 'default'.\n"
1192 " Note that the order of -e and -x switches matters.\n"
1193 "\t-E [--export-to] fmt filename\n"
1194 " where fmt is the export format of choice (see --export),\n"
1195 " and filename is the destination filename.\n"
1196 "\t-i [--import] fmt file.xxx\n"
1197 " where fmt is the import format of choice\n"
1198 " and file.xxx is the file to be imported.\n"
1199 "\t-f [--force-overwrite] what\n"
1200 " where what is either `all', `main' or `none',\n"
1201 " specifying whether all files, main file only, or no files,\n"
1202 " respectively, are to be overwritten during a batch export.\n"
1203 " Anything else is equivalent to `all', but is not consumed.\n"
1204 "\t--ignore-error-message which\n"
1205 " allows you to ignore specific LaTeX error messages.\n"
1206 " Do not use for final documents! Currently supported values:\n"
1207 " * missing_glyphs: Fontspec `missing glyphs' error.\n"
1208 "\t-n [--no-remote]\n"
1209 " open documents in a new instance\n"
1211 " open documents in an already running instance\n"
1212 " (a working lyxpipe is needed)\n"
1213 "\t-v [--verbose]\n"
1214 " report on terminal about spawned commands.\n"
1215 "\t-batch execute commands without launching GUI and exit.\n"
1216 "\t-version summarize version and build info\n"
1217 "Check the LyX man page for more details.")) << endl;
1223 int parse_version(string const &, string const &, string &)
1225 cout << "LyX " << lyx_version
1226 << " (" << lyx_release_date << ")" << endl;
1227 if (string(lyx_git_commit_hash) != "none")
1228 cout << to_utf8(_(" Git commit hash "))
1229 << string(lyx_git_commit_hash).substr(0,8) << endl;
1230 cout << lyx_version_info << endl;
1236 int parse_sysdir(string const & arg, string const &, string &)
1239 Alert::error(_("No system directory"),
1240 _("Missing directory for -sysdir switch"));
1243 cl_system_support = arg;
1248 int parse_userdir(string const & arg, string const &, string &)
1251 Alert::error(_("No user directory"),
1252 _("Missing directory for -userdir switch"));
1255 cl_user_support = arg;
1260 int parse_execute(string const & arg, string const &, string & batch)
1263 Alert::error(_("Incomplete command"),
1264 _("Missing command string after --execute switch"));
1272 int parse_export_to(string const & type, string const & output_file, string & batch)
1275 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1276 "--export-to switch")) << endl;
1279 if (output_file.empty()) {
1280 lyxerr << to_utf8(_("Missing destination filename after "
1281 "--export-to switch")) << endl;
1284 batch = "buffer-export " + type + " " + output_file;
1290 int parse_export(string const & type, string const &, string & batch)
1293 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1294 "--export switch")) << endl;
1297 batch = "buffer-export " + type;
1303 int parse_import(string const & type, string const & file, string & batch)
1306 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1307 "--import switch")) << endl;
1311 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1314 batch = "buffer-import " + type + ' ' + file;
1319 int parse_geometry(string const & arg1, string const &, string &)
1322 // don't remove "-geometry", it will be pruned out later in the
1323 // frontend if need be.
1328 int parse_batch(string const &, string const &, string &)
1335 int parse_noremote(string const &, string const &, string &)
1337 run_mode = NEW_INSTANCE;
1342 int parse_remote(string const &, string const &, string &)
1344 run_mode = USE_REMOTE;
1349 int parse_verbose(string const &, string const &, string &)
1356 int parse_ignore_error_message(string const & arg1, string const &, string &)
1358 if (arg1 == "missing_glyphs") {
1359 ignore_missing_glyphs = true;
1366 int parse_force(string const & arg, string const &, string &)
1369 force_overwrite = ALL_FILES;
1371 } else if (arg == "main") {
1372 force_overwrite = MAIN_FILE;
1374 } else if (arg == "none") {
1375 force_overwrite = NO_FILES;
1378 force_overwrite = ALL_FILES;
1386 void LyX::easyParse(int & argc, char * argv[])
1388 map<string, cmd_helper> cmdmap;
1390 cmdmap["-dbg"] = parse_dbg;
1391 cmdmap["-help"] = parse_help;
1392 cmdmap["--help"] = parse_help;
1393 cmdmap["-version"] = parse_version;
1394 cmdmap["--version"] = parse_version;
1395 cmdmap["-sysdir"] = parse_sysdir;
1396 cmdmap["-userdir"] = parse_userdir;
1397 cmdmap["-x"] = parse_execute;
1398 cmdmap["--execute"] = parse_execute;
1399 cmdmap["-e"] = parse_export;
1400 cmdmap["--export"] = parse_export;
1401 cmdmap["-E"] = parse_export_to;
1402 cmdmap["--export-to"] = parse_export_to;
1403 cmdmap["-i"] = parse_import;
1404 cmdmap["--import"] = parse_import;
1405 cmdmap["-geometry"] = parse_geometry;
1406 cmdmap["-batch"] = parse_batch;
1407 cmdmap["-f"] = parse_force;
1408 cmdmap["--force-overwrite"] = parse_force;
1409 cmdmap["-n"] = parse_noremote;
1410 cmdmap["--no-remote"] = parse_noremote;
1411 cmdmap["-r"] = parse_remote;
1412 cmdmap["--remote"] = parse_remote;
1413 cmdmap["-v"] = parse_verbose;
1414 cmdmap["--verbose"] = parse_verbose;
1415 cmdmap["--ignore-error-message"] = parse_ignore_error_message;
1417 for (int i = 1; i < argc; ++i) {
1418 map<string, cmd_helper>::const_iterator it
1419 = cmdmap.find(argv[i]);
1421 // don't complain if not found - may be parsed later
1422 if (it == cmdmap.end())
1426 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1428 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1431 int const remove = 1 + it->second(arg, arg2, batch);
1433 pimpl_->batch_commands.push_back(batch);
1435 // Now, remove used arguments by shifting
1436 // the following ones remove places down.
1438 os::remove_internal_args(i, remove);
1440 for (int j = i; j < argc; ++j)
1441 argv[j] = argv[j + remove];
1448 FuncStatus getStatus(FuncRequest const & action)
1451 return theApp()->getStatus(action);
1455 DispatchResult const & dispatch(FuncRequest const & action)
1458 return theApp()->dispatch(action);
1462 void dispatch(FuncRequest const & action, DispatchResult & dr)
1465 theApp()->dispatch(action, dr);
1469 vector<string> & theFilesToLoad()
1471 LAPPERR(singleton_);
1472 return singleton_->pimpl_->files_to_load_;
1476 BufferList & theBufferList()
1478 LAPPERR(singleton_);
1479 return singleton_->pimpl_->buffer_list_;
1483 Server & theServer()
1485 // FIXME: this should not be use_gui dependent
1487 LAPPERR(singleton_);
1488 return *singleton_->pimpl_->lyx_server_;
1492 ServerSocket & theServerSocket()
1494 // FIXME: this should not be use_gui dependent
1496 LAPPERR(singleton_);
1497 return *singleton_->pimpl_->lyx_socket_;
1501 KeyMap & theTopLevelKeymap()
1503 LAPPERR(singleton_);
1504 return singleton_->pimpl_->toplevel_keymap_;
1508 Formats & theFormats()
1510 LAPPERR(singleton_);
1511 return singleton_->pimpl_->formats_;
1515 Formats & theSystemFormats()
1517 LAPPERR(singleton_);
1518 return singleton_->pimpl_->system_formats_;
1522 Converters & theConverters()
1524 LAPPERR(singleton_);
1525 return singleton_->pimpl_->converters_;
1529 Converters & theSystemConverters()
1531 LAPPERR(singleton_);
1532 return singleton_->pimpl_->system_converters_;
1536 Movers & theMovers()
1538 LAPPERR(singleton_);
1539 return singleton_->pimpl_->movers_;
1543 Mover const & getMover(string const & fmt)
1545 LAPPERR(singleton_);
1546 return singleton_->pimpl_->movers_(fmt);
1550 void setMover(string const & fmt, string const & command)
1552 LAPPERR(singleton_);
1553 singleton_->pimpl_->movers_.set(fmt, command);
1557 Movers & theSystemMovers()
1559 LAPPERR(singleton_);
1560 return singleton_->pimpl_->system_movers_;
1564 Messages const & getMessages(string const & language)
1566 LAPPERR(singleton_);
1567 return singleton_->messages(language);
1571 Messages const & getGuiMessages()
1573 LAPPERR(singleton_);
1574 return singleton_->messages(Messages::guiLanguage());
1578 Session & theSession()
1580 LAPPERR(singleton_);
1581 return *singleton_->pimpl_->session_.get();
1585 LaTeXFonts & theLaTeXFonts()
1587 LAPPERR(singleton_);
1588 if (!singleton_->pimpl_->latexfonts_)
1589 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1590 return *singleton_->pimpl_->latexfonts_;
1594 CmdDef & theTopLevelCmdDef()
1596 LAPPERR(singleton_);
1597 return singleton_->pimpl_->toplevel_cmddef_;
1601 SpellChecker * theSpellChecker()
1603 if (!singleton_->pimpl_->spell_checker_)
1605 return singleton_->pimpl_->spell_checker_;
1609 void setSpellChecker()
1611 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1612 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1614 if (lyxrc.spellchecker == "native") {
1615 #if defined(USE_MACOSX_PACKAGING)
1616 if (!singleton_->pimpl_->apple_spell_checker_)
1617 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1618 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1620 singleton_->pimpl_->spell_checker_ = 0;
1622 } else if (lyxrc.spellchecker == "aspell") {
1623 #if defined(USE_ASPELL)
1624 if (!singleton_->pimpl_->aspell_checker_)
1625 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1626 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1628 singleton_->pimpl_->spell_checker_ = 0;
1630 } else if (lyxrc.spellchecker == "enchant") {
1631 #if defined(USE_ENCHANT)
1632 if (!singleton_->pimpl_->enchant_checker_)
1633 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1634 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1636 singleton_->pimpl_->spell_checker_ = 0;
1638 } else if (lyxrc.spellchecker == "hunspell") {
1639 #if defined(USE_HUNSPELL)
1640 if (!singleton_->pimpl_->hunspell_checker_)
1641 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1642 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1644 singleton_->pimpl_->spell_checker_ = 0;
1647 singleton_->pimpl_->spell_checker_ = 0;
1649 if (singleton_->pimpl_->spell_checker_) {
1650 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1651 singleton_->pimpl_->spell_checker_->advanceChangeNumber();