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"
78 using namespace lyx::support;
80 #if defined (USE_MACOSX_PACKAGING)
81 #include <crt_externs.h>
86 namespace Alert = frontend::Alert;
87 namespace os = support::os;
91 // Are we using the GUI at all? We default to true and this is changed
92 // to false when the export feature is used.
97 // Report on the terminal about spawned commands. The default is false
98 // and can be changed with the option -v (--verbose).
100 bool verbose = false;
103 // Do not treat the "missing glyphs" warning of fontspec as an error message.
104 // The default is false and can be changed with the option
105 // --ignore-error-message missing_glyphs
106 // This is used in automated testing.
107 bool ignore_missing_glyphs = false;
110 // We default to open documents in an already running instance, provided that
111 // the lyxpipe has been setup. This can be overridden either on the command
112 // line or through preference settings.
114 RunMode run_mode = PREFERRED;
117 // Tell what files can be silently overwritten during batch export.
118 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
119 // Unless specified on command line (through the -f switch) or through the
120 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
122 OverwriteFiles force_overwrite = UNSPECIFIED;
125 // Scale the GUI by this factor. This works whether we have a HiDpi screen
126 // or not and scales everything, also fonts. Can only be changed by setting
127 // the QT_SCALE_FACTOR environment variable before launching LyX and only
128 // works properly with Qt 5.6 or higher.
130 double qt_scale_factor = 1.0;
135 // Filled with the command line arguments "foo" of "-sysdir foo" or
137 string cl_system_support;
138 string cl_user_support;
142 LyX * singleton_ = 0;
144 void showFileError(string const & error)
146 Alert::warning(_("Could not read configuration file"),
147 bformat(_("Error while reading the configuration file\n%1$s.\n"
148 "Please check your installation."), from_utf8(error)));
153 /// The main application class private implementation.
156 : latexfonts_(0), spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
162 delete apple_spell_checker_;
163 delete aspell_checker_;
164 delete enchant_checker_;
165 delete hunspell_checker_;
169 BufferList buffer_list_;
171 KeyMap toplevel_keymap_;
173 CmdDef toplevel_cmddef_;
175 unique_ptr<Server> lyx_server_;
177 unique_ptr<ServerSocket> lyx_socket_;
179 unique_ptr<frontend::Application> application_;
180 /// lyx session, containing lastfiles, lastfilepos, and lastopened
181 unique_ptr<Session> session_;
183 /// Files to load at start.
184 vector<string> files_to_load_;
186 /// The messages translators.
187 map<string, Messages> messages_;
189 /// The file converters.
190 Converters converters_;
192 // The system converters copy after reading lyxrc.defaults.
193 Converters system_converters_;
198 Movers system_movers_;
200 /// the parsed command line batch command if any
201 vector<string> batch_commands;
204 LaTeXFonts * latexfonts_;
207 SpellChecker * spell_checker_;
209 SpellChecker * apple_spell_checker_;
211 SpellChecker * aspell_checker_;
213 SpellChecker * enchant_checker_;
215 SpellChecker * hunspell_checker_;
219 /// The main application class for console mode
220 class LyXConsoleApp : public ConsoleApplication
223 LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
224 : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
225 argc_(argc), argv_(argv)
230 int const exit_status = lyx_->execWithoutGui(argc_, argv_);
241 frontend::Application * theApp()
244 return singleton_->pimpl_->application_.get();
257 void lyx_exit(int exit_code)
260 // Something wrong happened so better save everything, just in
265 // Properly crash in debug mode in order to get a useful backtrace.
269 // In release mode, try to exit gracefully.
271 theApp()->exit(exit_code);
285 Messages & LyX::messages(string const & language)
287 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
289 if (it != pimpl_->messages_.end())
292 pair<map<string, Messages>::iterator, bool> result =
293 pimpl_->messages_.insert(make_pair(language, Messages(language)));
295 LATTEST(result.second);
296 return result.first->second;
300 int LyX::exec(int & argc, char * argv[])
302 // Minimal setting of locale before parsing command line
304 init_package(os::utf8_argv(0), string(), string());
305 // we do not get to this point when init_package throws an exception
307 } catch (ExceptionMessage const & message) {
308 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
311 // Here we need to parse the command line. At least
312 // we need to parse for "-dbg" and "-help"
313 easyParse(argc, argv);
315 #if QT_VERSION >= 0x050600
316 // Check whether Qt will scale all GUI elements and accordingly
317 // set the scale factor so that to avoid blurred images and text
318 char const * const scale_factor = getenv("QT_SCALE_FACTOR");
320 qt_scale_factor = convert<double>(scale_factor);
321 if (qt_scale_factor < 1.0)
322 qt_scale_factor = 1.0;
327 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
328 } catch (ExceptionMessage const & message) {
329 if (message.type_ == ErrorException) {
330 Alert::error(message.title_, message.details_);
332 } else if (message.type_ == WarningException) {
333 Alert::warning(message.title_, message.details_);
337 // Reinit the messages machinery in case package() knows
338 // something interesting about the locale directory.
342 LyXConsoleApp app(this, argc, argv);
344 // Reestablish our defaults, as Qt overwrites them
345 // after creating app
351 // Let the frontend parse and remove all arguments that it knows
352 pimpl_->application_.reset(createApplication(argc, argv));
354 // Reestablish our defaults, as Qt overwrites them
355 // after createApplication()
358 // Parse and remove all known arguments in the LyX singleton
359 // Give an error for all remaining ones.
360 int exit_status = init(argc, argv);
362 // Kill the application object before exiting.
363 pimpl_->application_.reset();
369 // If not otherwise specified by a command line option or
370 // by preferences, we default to reuse a running instance.
371 if (run_mode == PREFERRED)
372 run_mode = USE_REMOTE;
375 /* Create a CoreApplication class that will provide the main event loop
376 * and the socket callback registering. With Qt, only QtCore
377 * library would be needed.
378 * When this is done, a server_mode could be created and the following two
379 * line would be moved out from here.
380 * However, note that the first of the two lines below triggers the
381 * "single instance" behavior, which should occur right at this point.
383 // Note: socket callback must be registered after init(argc, argv)
384 // such that package().temp_dir() is properly initialized.
385 pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
386 pimpl_->lyx_socket_.reset(new ServerSocket(
387 FileName(package().temp_dir().absFileName() + "/lyxsocket")));
389 // Start the real execution loop.
390 if (!theServer().deferredLoadingToOtherInstance())
391 exit_status = pimpl_->application_->exec();
392 else if (!pimpl_->files_to_load_.empty()) {
393 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
394 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
395 lyxerr << _("The following files could not be loaded:") << endl;
396 for (; it != end; ++it)
397 lyxerr << *it << endl;
406 void LyX::prepareExit()
408 // Clear the clipboard and selection stack:
409 cap::clearCutStack();
410 cap::clearSelection();
412 // Write the index file of the converter cache
413 ConverterCache::get().writeIndex();
415 // close buffers first
416 pimpl_->buffer_list_.closeAll();
418 // register session changes and shutdown server and socket
420 if (pimpl_->session_)
421 pimpl_->session_->writeFile();
422 pimpl_->session_.reset();
423 pimpl_->lyx_server_.reset();
424 pimpl_->lyx_socket_.reset();
427 // do any other cleanup procedures now
428 if (package().temp_dir() != package().system_temp_dir()) {
429 string const abs_tmpdir = package().temp_dir().absFileName();
430 if (!contains(package().temp_dir().absFileName(), "lyx_tmpdir")) {
431 docstring const msg =
432 bformat(_("%1$s does not appear like a LyX created temporary directory."),
433 from_utf8(abs_tmpdir));
434 Alert::warning(_("Cannot remove temporary directory"), msg);
436 LYXERR(Debug::INFO, "Deleting tmp dir "
437 << package().temp_dir().absFileName());
438 if (!package().temp_dir().destroyDirectory()) {
439 LYXERR0(bformat(_("Unable to remove the temporary directory %1$s"),
440 from_utf8(package().temp_dir().absFileName())));
445 // Kill the application object before exiting. This avoids crashes
446 // when exiting on Linux.
447 pimpl_->application_.reset();
451 void LyX::earlyExit(int status)
453 LATTEST(pimpl_->application_.get());
454 // LyX::pimpl_::application_ is not initialised at this
455 // point so it's safe to just exit after some cleanup.
461 int LyX::init(int & argc, char * argv[])
463 // check for any spurious extra arguments
464 // other than documents
465 for (int argi = 1; argi < argc ; ++argi) {
466 if (argv[argi][0] == '-') {
468 bformat(_("Wrong command line option `%1$s'. Exiting."),
469 from_utf8(os::utf8_argv(argi)))) << endl;
474 // Initialization of LyX (reads lyxrc and more)
475 LYXERR(Debug::INIT, "Initializing LyX::init...");
476 bool success = init();
477 LYXERR(Debug::INIT, "Initializing LyX::init...done");
481 // Remaining arguments are assumed to be files to load.
482 for (int argi = 1; argi < argc; ++argi)
483 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
485 if (!use_gui && pimpl_->files_to_load_.empty()) {
486 lyxerr << to_utf8(_("Missing filename for this operation.")) << endl;
491 pimpl_->files_to_load_.push_back(
492 i18nLibFileSearch("examples", "splash.lyx").absFileName());
499 int LyX::execWithoutGui(int & argc, char * argv[])
501 int exit_status = init(argc, argv);
507 // Used to keep track of which buffers were explicitly loaded by user request.
508 // This is necessary because master and child document buffers are loaded, even
509 // if they were not named on the command line. We do not want to dispatch to
511 vector<Buffer *> command_line_buffers;
513 // Load the files specified on the command line
514 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
515 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
516 for (; it != end; ++it) {
517 // get absolute path of file and add ".lyx" to the filename if necessary
518 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
524 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName());
525 LYXERR(Debug::FILES, "Loading " << fname);
526 if (buf && buf->loadLyXFile() == Buffer::ReadSuccess) {
527 ErrorList const & el = buf->errorList("Parse");
528 for(ErrorItem const & e : el)
530 command_line_buffers.push_back(buf);
533 pimpl_->buffer_list_.release(buf);
534 docstring const error_message =
535 bformat(_("LyX failed to load the following file: %1$s"),
536 from_utf8(fname.absFileName()));
537 lyxerr << to_utf8(error_message) << endl;
538 exit_status = 1; // failed
542 if (exit_status || pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
547 // Iterate through the buffers that were specified on the command line
548 bool final_success = false;
549 vector<Buffer *>::iterator buf_it = command_line_buffers.begin();
550 for (; buf_it != command_line_buffers.end(); ++buf_it) {
551 Buffer * buf = *buf_it;
552 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
553 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
555 for (; bcit != bcend; ++bcit) {
556 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
557 buf->dispatch(*bcit, dr);
558 final_success |= !dr.error();
562 return !final_success;
566 void execBatchCommands()
569 singleton_->execCommands();
573 void LyX::execCommands()
575 // The advantage of doing this here is that the event loop
576 // is already started. So any need for interaction will be
579 // if reconfiguration is needed.
580 if (LayoutFileList::get().empty()) {
581 switch (Alert::prompt(
582 _("No textclass is found"),
583 _("LyX will only have minimal functionality because no textclasses "
584 "have been found. You can either try to reconfigure LyX normally, "
585 "try to reconfigure without checking your LaTeX installation, or continue."),
592 // regular reconfigure
593 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
596 // reconfigure --without-latex-config
597 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
598 " --without-latex-config"));
605 // create the first main window
606 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
608 if (!pimpl_->files_to_load_.empty()) {
609 // if some files were specified at command-line we assume that the
610 // user wants to edit *these* files and not to restore the session.
611 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
613 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
615 // clear this list to save a few bytes of RAM
616 pimpl_->files_to_load_.clear();
618 pimpl_->application_->restoreGuiSession();
620 // Execute batch commands if available
621 if (pimpl_->batch_commands.empty())
624 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
625 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
626 for (; bcit != bcend; ++bcit) {
627 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
628 lyx::dispatch(lyxaction.lookupFunc(*bcit));
636 The SIGHUP signal does not exist on Windows and does not need to be handled.
638 Windows handles SIGFPE and SIGSEGV signals as expected.
640 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
641 cause a new thread to be spawned. This may well result in unexpected
642 behaviour by the single-threaded LyX.
644 SIGTERM signals will come only from another process actually sending
645 that signal using 'raise' in Windows' POSIX compatability layer. It will
646 not come from the general "terminate process" methods that everyone
647 actually uses (and which can't be trapped). Killing an app 'politely' on
648 Windows involves first sending a WM_CLOSE message, something that is
649 caught already by the Qt frontend.
651 For more information see:
653 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
654 ...signals are mostly useless on Windows for a variety of reasons that are
657 'UNIX Application Migration Guide, Chapter 9'
658 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
660 'How To Terminate an Application "Cleanly" in Win32'
661 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
665 static void error_handler(int err_sig)
667 // Throw away any signals other than the first one received.
668 static sig_atomic_t handling_error = false;
671 handling_error = true;
673 // We have received a signal indicating a fatal error, so
674 // try and save the data ASAP.
677 // These lyxerr calls may or may not work:
679 // Signals are asynchronous, so the main program may be in a very
680 // fragile state when a signal is processed and thus while a signal
681 // handler function executes.
682 // In general, therefore, we should avoid performing any
683 // I/O operations or calling most library and system functions from
686 // This shouldn't matter here, however, as we've already invoked
692 msg = _("SIGHUP signal caught!\nBye.");
696 msg = _("SIGFPE signal caught!\nBye.");
699 msg = _("SIGSEGV signal caught!\n"
700 "Sorry, you have found a bug in LyX, "
701 "hope you have not lost any data.\n"
702 "Please read the bug-reporting instructions "
703 "in 'Help->Introduction' and send us a bug report, "
704 "if necessary. Thanks!\nBye.");
713 lyxerr << "\nlyx: " << msg << endl;
714 // try to make a GUI message
715 Alert::error(_("LyX crashed!"), msg, true);
718 // Deinstall the signal handlers
720 signal(SIGHUP, SIG_DFL);
722 signal(SIGINT, SIG_DFL);
723 signal(SIGFPE, SIG_DFL);
724 signal(SIGSEGV, SIG_DFL);
725 signal(SIGTERM, SIG_DFL);
728 if (err_sig == SIGSEGV ||
729 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
731 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
734 // with abort() it crashes again.
747 void LyX::printError(ErrorItem const & ei)
749 docstring tmp = _("LyX: ") + ei.error + char_type(':')
751 cerr << to_utf8(tmp) << endl;
754 #if defined (USE_MACOSX_PACKAGING)
756 // Unexposed--extract an environment variable name from its NAME=VALUE
758 std::string varname(const char* line)
760 size_t nameLen = strcspn(line, "=");
761 if (nameLen == strlen(line)) {
762 return std::string();
764 return std::string(line, nameLen);
769 void cleanDuplicateEnvVars()
771 std::set<std::string> seen;
772 std::set<std::string> dupes;
774 // Create a list of the environment variables that appear more than once
775 for (char **read = *_NSGetEnviron(); *read; read++) {
776 std::string name = varname(*read);
777 if (name.size() == 0) {
780 if (seen.find(name) != seen.end()) {
787 // Loop over the list of duplicated variables
788 std::set<std::string>::iterator dupe = dupes.begin();
789 std::set<std::string>::iterator const dend = dupes.end();
790 for (; dupe != dend; ++dupe) {
791 const char *name = (*dupe).c_str();
792 char *val = getenv(name);
794 LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
795 // unsetenv removes *all* instances of the variable from the environment
798 // replace with the value from getenv (in practice appears to be the
799 // first value in the list)
800 setenv(name, val, 0);
807 static void initTemplatePath()
809 FileName const package_template_path =
810 FileName(addName(package().system_support().absFileName(), "templates"));
812 if (lyxrc.template_path.empty()) {
813 lyxrc.template_path = package_template_path.absFileName();
815 #if defined (USE_MACOSX_PACKAGING)
816 FileName const user_template_path =
817 FileName(addName(package().user_support().absFileName(), "templates"));
819 if (package_template_path != FileName(lyxrc.template_path) &&
820 user_template_path != FileName(lyxrc.template_path))
824 FileName const user_template_link =
825 FileName(addName(user_template_path.absFileName(),"SystemTemplates"));
826 if (user_template_link.isSymLink() && !equivalent(user_template_link, package_template_path)) {
827 user_template_link.removeFile();
829 if (!user_template_link.exists()) {
830 if (!package_template_path.link(user_template_link)) {
831 FileName const user_support = package().user_support();
832 if (user_support.exists() && user_support.isDirectory()) {
833 LYXERR(Debug::INIT, "Cannot create symlink " + user_template_link.absFileName());
834 lyxrc.template_path = package_template_path.absFileName();
838 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
840 lyxrc.template_path = user_template_path.absFileName();
848 signal(SIGHUP, error_handler);
850 signal(SIGFPE, error_handler);
851 signal(SIGSEGV, error_handler);
852 signal(SIGINT, error_handler);
853 signal(SIGTERM, error_handler);
854 // SIGPIPE can be safely ignored.
856 #if defined (USE_MACOSX_PACKAGING)
857 cleanDuplicateEnvVars();
860 lyxrc.tempdir_path = package().temp_dir().absFileName();
861 lyxrc.document_path = ".";
863 if (lyxrc.example_path.empty()) {
864 lyxrc.example_path = addPath(package().system_support().absFileName(),
869 // init LyXDir environment variable
870 string const lyx_dir = package().lyx_dir().absFileName();
871 LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
872 if (!setEnv("LyXDir", lyx_dir))
873 LYXERR(Debug::INIT, "\t... failed!");
875 if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
876 // -userdir was given on the command line.
877 // Make it available to child processes, otherwise tex2lyx
878 // would not find all layout files, and other converters might
880 string const user_dir = package().user_support().absFileName();
881 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
883 if (!setEnv(LYX_USERDIR_VER, user_dir))
884 LYXERR(Debug::INIT, "\t... failed!");
888 // Read configuration files
891 // This one may have been distributed along with LyX.
892 if (!readRcFile("lyxrc.dist"))
895 // Set the PATH correctly.
896 #if !defined (USE_POSIX_PACKAGING) && !defined (USE_HAIKU_PACKAGING)
897 // Add the directory containing the LyX executable to the path
898 // so that LyX can find things like tex2lyx.
899 if (package().build_support().empty())
900 prependEnvPath("PATH", package().binary_dir().absFileName());
902 if (!lyxrc.path_prefix.empty())
903 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
905 // Check that user LyX directory is ok.
907 string const lock_file = package().getConfigureLockName();
908 int fd = fileLock(lock_file.c_str());
910 if (queryUserLyXDir(package().explicit_user_support())) {
911 package().reconfigureUserLyXDir("");
912 // Now the user directory is present on first start.
915 fileUnlock(fd, lock_file.c_str());
919 // No need for a splash when there is no GUI
921 // Default is to overwrite the main file during export, unless
922 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
923 if (force_overwrite == UNSPECIFIED) {
924 string const what = getEnv("LYX_FORCE_OVERWRITE");
926 force_overwrite = ALL_FILES;
927 else if (what == "none")
928 force_overwrite = NO_FILES;
930 force_overwrite = MAIN_FILE;
934 // This one is generated in user_support directory by lib/configure.py.
935 if (!readRcFile("lyxrc.defaults"))
938 // Query the OS to know what formats are viewed natively
939 formats.setAutoOpen();
941 // Read lyxrc.dist again to be able to override viewer auto-detection.
942 readRcFile("lyxrc.dist");
944 system_lyxrc = lyxrc;
945 system_formats = formats;
946 pimpl_->system_converters_ = pimpl_->converters_;
947 pimpl_->system_movers_ = pimpl_->movers_;
948 system_lcolor = lcolor;
950 // This one is edited through the preferences dialog.
951 if (!readRcFile("preferences", true))
954 // The language may have been set to someting useful through prefs
957 if (!readEncodingsFile("encodings", "unicodesymbols"))
959 if (!readLanguagesFile("languages"))
962 LYXERR(Debug::INIT, "Reading layouts...");
964 LayoutFileList::get().read();
966 theModuleList.read();
967 //... and the cite engines
968 theCiteEnginesList.read();
970 // read keymap and ui files in batch mode as well
971 // because InsetInfo needs to know these to produce
972 // the correct output
974 // Set up command definitions
975 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
979 pimpl_->toplevel_keymap_.read("site");
980 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
981 // load user bind file user.bind
982 pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
984 if (lyxerr.debugging(Debug::LYXRC))
987 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
988 // Prepend path prefix a second time to take the user preferences into a account
989 if (!lyxrc.path_prefix.empty())
990 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
992 FileName const document_path(lyxrc.document_path);
993 if (document_path.exists() && document_path.isDirectory())
994 package().document_dir() = document_path;
996 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
997 if (package().temp_dir().empty()) {
998 Alert::error(_("Could not create temporary directory"),
999 bformat(_("Could not create a temporary directory in\n"
1001 "Make sure that this path exists and is writable and try again."),
1002 from_utf8(lyxrc.tempdir_path)));
1003 // createLyXTmpDir() tries sufficiently hard to create a
1004 // usable temp dir, so the probability to come here is
1005 // close to zero. We therefore don't try to overcome this
1006 // problem with e.g. asking the user for a new path and
1007 // trying again but simply exit.
1011 LYXERR(Debug::INIT, "LyX tmp dir: `"
1012 << package().temp_dir().absFileName() << '\'');
1014 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
1015 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1017 // This must happen after package initialization and after lyxrc is
1018 // read, therefore it can't be done by a static object.
1019 ConverterCache::init();
1025 void emergencyCleanup()
1027 // what to do about tmpfiles is non-obvious. we would
1028 // like to delete any we find, but our lyxdir might
1029 // contain documents etc. which might be helpful on
1032 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1034 if (singleton_->pimpl_->lyx_server_)
1035 singleton_->pimpl_->lyx_server_->emergencyCleanup();
1036 singleton_->pimpl_->lyx_server_.reset();
1037 singleton_->pimpl_->lyx_socket_.reset();
1042 bool LyX::queryUserLyXDir(bool explicit_userdir)
1044 // Does user directory exist?
1045 FileName const sup = package().user_support();
1046 if (sup.exists() && sup.isDirectory()) {
1047 first_start = false;
1049 return configFileNeedsUpdate("lyxrc.defaults")
1050 || configFileNeedsUpdate("lyxmodules.lst")
1051 || configFileNeedsUpdate("textclass.lst")
1052 || configFileNeedsUpdate("packages.lst")
1053 || configFileNeedsUpdate("lyxciteengines.lst");
1056 first_start = !explicit_userdir;
1058 // If the user specified explicitly a directory, ask whether
1059 // to create it. If the user says "no", then exit.
1060 if (explicit_userdir &&
1062 _("Missing user LyX directory"),
1063 bformat(_("You have specified a non-existent user "
1064 "LyX directory, %1$s.\n"
1065 "It is needed to keep your own configuration."),
1066 from_utf8(package().user_support().absFileName())),
1068 _("&Create directory"),
1070 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1071 earlyExit(EXIT_FAILURE);
1074 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1075 from_utf8(sup.absFileName()))) << endl;
1077 if (!sup.createDirectory(0755)) {
1078 // Failed, so let's exit.
1079 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1081 earlyExit(EXIT_FAILURE);
1088 bool LyX::readRcFile(string const & name, bool check_format)
1090 LYXERR(Debug::INIT, "About to read " << name << "... ");
1092 FileName const lyxrc_path = libFileSearch(string(), name);
1093 if (lyxrc_path.empty()) {
1094 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1096 // This was the previous logic, but can it be right??
1099 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1100 bool const success = lyxrc.read(lyxrc_path, check_format);
1102 showFileError(name);
1106 // Read the languages file `name'
1107 bool LyX::readLanguagesFile(string const & name)
1109 LYXERR(Debug::INIT, "About to read " << name << "...");
1111 FileName const lang_path = libFileSearch(string(), name);
1112 if (lang_path.empty()) {
1113 showFileError(name);
1116 languages.read(lang_path);
1121 // Read the encodings file `name'
1122 bool LyX::readEncodingsFile(string const & enc_name,
1123 string const & symbols_name)
1125 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1126 << symbols_name << "...");
1128 FileName const symbols_path = libFileSearch(string(), symbols_name);
1129 if (symbols_path.empty()) {
1130 showFileError(symbols_name);
1134 FileName const enc_path = libFileSearch(string(), enc_name);
1135 if (enc_path.empty()) {
1136 showFileError(enc_name);
1139 encodings.read(enc_path, symbols_path);
1146 /// return the the number of arguments consumed
1147 typedef function<int(string const &, string const &, string &)> cmd_helper;
1149 int parse_dbg(string const & arg, string const &, string &)
1152 cout << to_utf8(_("List of supported debug flags:")) << endl;
1153 Debug::showTags(cout);
1156 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1158 lyxerr.setLevel(Debug::value(arg));
1159 Debug::showLevel(lyxerr, lyxerr.level());
1164 int parse_help(string const &, string const &, string &)
1167 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1168 "Command line switches (case sensitive):\n"
1169 "\t-help summarize LyX usage\n"
1170 "\t-userdir dir set user directory to dir\n"
1171 "\t-sysdir dir set system directory to dir\n"
1172 "\t-geometry WxH+X+Y set geometry of the main window\n"
1173 "\t-dbg feature[,feature]...\n"
1174 " select the features to debug.\n"
1175 " Type `lyx -dbg' to see the list of features\n"
1176 "\t-x [--execute] command\n"
1177 " where command is a lyx command.\n"
1178 "\t-e [--export] fmt\n"
1179 " where fmt is the export format of choice. Look in\n"
1180 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1181 " to see which parameter (which differs from the format name\n"
1182 " in the File->Export menu) should be passed.\n"
1183 " Note that the order of -e and -x switches matters.\n"
1184 "\t-E [--export-to] fmt filename\n"
1185 " where fmt is the export format of choice (see --export),\n"
1186 " and filename is the destination filename.\n"
1187 "\t-i [--import] fmt file.xxx\n"
1188 " where fmt is the import format of choice\n"
1189 " and file.xxx is the file to be imported.\n"
1190 "\t-f [--force-overwrite] what\n"
1191 " where what is either `all', `main' or `none',\n"
1192 " specifying whether all files, main file only, or no files,\n"
1193 " respectively, are to be overwritten during a batch export.\n"
1194 " Anything else is equivalent to `all', but is not consumed.\n"
1195 "\t--ignore-error-message which\n"
1196 " allows you to ignore specific LaTeX error messages.\n"
1197 " Do not use for final documents! Currently supported values:\n"
1198 " * missing_glyphs: Fontspec `missing glyphs' error.\n"
1199 "\t-n [--no-remote]\n"
1200 " open documents in a new instance\n"
1202 " open documents in an already running instance\n"
1203 " (a working lyxpipe is needed)\n"
1204 "\t-v [--verbose]\n"
1205 " report on terminal about spawned commands.\n"
1206 "\t-batch execute commands without launching GUI and exit.\n"
1207 "\t-version summarize version and build info\n"
1208 "Check the LyX man page for more details.")) << endl;
1214 int parse_version(string const &, string const &, string &)
1216 cout << "LyX " << lyx_version
1217 << " (" << lyx_release_date << ")" << endl;
1218 if (string(lyx_git_commit_hash) != "none")
1219 cout << to_utf8(_(" Git commit hash "))
1220 << string(lyx_git_commit_hash).substr(0,8) << endl;
1221 cout << lyx_version_info << endl;
1227 int parse_sysdir(string const & arg, string const &, string &)
1230 Alert::error(_("No system directory"),
1231 _("Missing directory for -sysdir switch"));
1234 cl_system_support = arg;
1239 int parse_userdir(string const & arg, string const &, string &)
1242 Alert::error(_("No user directory"),
1243 _("Missing directory for -userdir switch"));
1246 cl_user_support = arg;
1251 int parse_execute(string const & arg, string const &, string & batch)
1254 Alert::error(_("Incomplete command"),
1255 _("Missing command string after --execute switch"));
1263 int parse_export_to(string const & type, string const & output_file, string & batch)
1266 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1267 "--export-to switch")) << endl;
1270 if (output_file.empty()) {
1271 lyxerr << to_utf8(_("Missing destination filename after "
1272 "--export-to switch")) << endl;
1275 batch = "buffer-export " + type + " " + output_file;
1281 int parse_export(string const & type, string const &, string & batch)
1284 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1285 "--export switch")) << endl;
1288 batch = "buffer-export " + type;
1294 int parse_import(string const & type, string const & file, string & batch)
1297 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1298 "--import switch")) << endl;
1302 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1305 batch = "buffer-import " + type + ' ' + file;
1310 int parse_geometry(string const & arg1, string const &, string &)
1313 // don't remove "-geometry", it will be pruned out later in the
1314 // frontend if need be.
1319 int parse_batch(string const &, string const &, string &)
1326 int parse_noremote(string const &, string const &, string &)
1328 run_mode = NEW_INSTANCE;
1333 int parse_remote(string const &, string const &, string &)
1335 run_mode = USE_REMOTE;
1340 int parse_verbose(string const &, string const &, string &)
1347 int parse_ignore_error_message(string const & arg1, string const &, string &)
1349 if (arg1 == "missing_glyphs") {
1350 ignore_missing_glyphs = true;
1357 int parse_force(string const & arg, string const &, string &)
1360 force_overwrite = ALL_FILES;
1362 } else if (arg == "main") {
1363 force_overwrite = MAIN_FILE;
1365 } else if (arg == "none") {
1366 force_overwrite = NO_FILES;
1369 force_overwrite = ALL_FILES;
1377 void LyX::easyParse(int & argc, char * argv[])
1379 map<string, cmd_helper> cmdmap;
1381 cmdmap["-dbg"] = parse_dbg;
1382 cmdmap["-help"] = parse_help;
1383 cmdmap["--help"] = parse_help;
1384 cmdmap["-version"] = parse_version;
1385 cmdmap["--version"] = parse_version;
1386 cmdmap["-sysdir"] = parse_sysdir;
1387 cmdmap["-userdir"] = parse_userdir;
1388 cmdmap["-x"] = parse_execute;
1389 cmdmap["--execute"] = parse_execute;
1390 cmdmap["-e"] = parse_export;
1391 cmdmap["--export"] = parse_export;
1392 cmdmap["-E"] = parse_export_to;
1393 cmdmap["--export-to"] = parse_export_to;
1394 cmdmap["-i"] = parse_import;
1395 cmdmap["--import"] = parse_import;
1396 cmdmap["-geometry"] = parse_geometry;
1397 cmdmap["-batch"] = parse_batch;
1398 cmdmap["-f"] = parse_force;
1399 cmdmap["--force-overwrite"] = parse_force;
1400 cmdmap["-n"] = parse_noremote;
1401 cmdmap["--no-remote"] = parse_noremote;
1402 cmdmap["-r"] = parse_remote;
1403 cmdmap["--remote"] = parse_remote;
1404 cmdmap["-v"] = parse_verbose;
1405 cmdmap["--verbose"] = parse_verbose;
1406 cmdmap["--ignore-error-message"] = parse_ignore_error_message;
1408 for (int i = 1; i < argc; ++i) {
1409 map<string, cmd_helper>::const_iterator it
1410 = cmdmap.find(argv[i]);
1412 // don't complain if not found - may be parsed later
1413 if (it == cmdmap.end())
1417 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1419 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1422 int const remove = 1 + it->second(arg, arg2, batch);
1424 pimpl_->batch_commands.push_back(batch);
1426 // Now, remove used arguments by shifting
1427 // the following ones remove places down.
1429 os::remove_internal_args(i, remove);
1431 for (int j = i; j < argc; ++j)
1432 argv[j] = argv[j + remove];
1439 FuncStatus getStatus(FuncRequest const & action)
1442 return theApp()->getStatus(action);
1446 DispatchResult const & dispatch(FuncRequest const & action)
1449 return theApp()->dispatch(action);
1453 void dispatch(FuncRequest const & action, DispatchResult & dr)
1456 theApp()->dispatch(action, dr);
1460 vector<string> & theFilesToLoad()
1462 LAPPERR(singleton_);
1463 return singleton_->pimpl_->files_to_load_;
1467 BufferList & theBufferList()
1469 LAPPERR(singleton_);
1470 return singleton_->pimpl_->buffer_list_;
1474 Server & theServer()
1476 // FIXME: this should not be use_gui dependent
1478 LAPPERR(singleton_);
1479 return *singleton_->pimpl_->lyx_server_;
1483 ServerSocket & theServerSocket()
1485 // FIXME: this should not be use_gui dependent
1487 LAPPERR(singleton_);
1488 return *singleton_->pimpl_->lyx_socket_;
1492 KeyMap & theTopLevelKeymap()
1494 LAPPERR(singleton_);
1495 return singleton_->pimpl_->toplevel_keymap_;
1499 Converters & theConverters()
1501 LAPPERR(singleton_);
1502 return singleton_->pimpl_->converters_;
1506 Converters & theSystemConverters()
1508 LAPPERR(singleton_);
1509 return singleton_->pimpl_->system_converters_;
1513 Movers & theMovers()
1515 LAPPERR(singleton_);
1516 return singleton_->pimpl_->movers_;
1520 Mover const & getMover(string const & fmt)
1522 LAPPERR(singleton_);
1523 return singleton_->pimpl_->movers_(fmt);
1527 void setMover(string const & fmt, string const & command)
1529 LAPPERR(singleton_);
1530 singleton_->pimpl_->movers_.set(fmt, command);
1534 Movers & theSystemMovers()
1536 LAPPERR(singleton_);
1537 return singleton_->pimpl_->system_movers_;
1541 Messages const & getMessages(string const & language)
1543 LAPPERR(singleton_);
1544 return singleton_->messages(language);
1548 Messages const & getGuiMessages()
1550 LAPPERR(singleton_);
1551 return singleton_->messages(Messages::guiLanguage());
1555 Session & theSession()
1557 LAPPERR(singleton_);
1558 return *singleton_->pimpl_->session_.get();
1562 LaTeXFonts & theLaTeXFonts()
1564 LAPPERR(singleton_);
1565 if (!singleton_->pimpl_->latexfonts_)
1566 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1567 return *singleton_->pimpl_->latexfonts_;
1571 CmdDef & theTopLevelCmdDef()
1573 LAPPERR(singleton_);
1574 return singleton_->pimpl_->toplevel_cmddef_;
1578 SpellChecker * theSpellChecker()
1580 if (!singleton_->pimpl_->spell_checker_)
1582 return singleton_->pimpl_->spell_checker_;
1586 void setSpellChecker()
1588 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1589 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1591 if (lyxrc.spellchecker == "native") {
1592 #if defined(USE_MACOSX_PACKAGING)
1593 if (!singleton_->pimpl_->apple_spell_checker_)
1594 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1595 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1597 singleton_->pimpl_->spell_checker_ = 0;
1599 } else if (lyxrc.spellchecker == "aspell") {
1600 #if defined(USE_ASPELL)
1601 if (!singleton_->pimpl_->aspell_checker_)
1602 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1603 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1605 singleton_->pimpl_->spell_checker_ = 0;
1607 } else if (lyxrc.spellchecker == "enchant") {
1608 #if defined(USE_ENCHANT)
1609 if (!singleton_->pimpl_->enchant_checker_)
1610 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1611 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1613 singleton_->pimpl_->spell_checker_ = 0;
1615 } else if (lyxrc.spellchecker == "hunspell") {
1616 #if defined(USE_HUNSPELL)
1617 if (!singleton_->pimpl_->hunspell_checker_)
1618 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1619 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1621 singleton_->pimpl_->spell_checker_ = 0;
1624 singleton_->pimpl_->spell_checker_ = 0;
1626 if (singleton_->pimpl_->spell_checker_) {
1627 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1628 singleton_->pimpl_->spell_checker_->advanceChangeNumber();