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"
26 #include "ConverterCache.h"
27 #include "Converter.h"
28 #include "CutAndPaste.h"
29 #include "EnchantChecker.h"
31 #include "ErrorList.h"
33 #include "FuncStatus.h"
34 #include "HunspellChecker.h"
37 #include "LaTeXFonts.h"
38 #include "LayoutFile.h"
41 #include "LyXAction.h"
43 #include "ModuleList.h"
46 #include "ServerSocket.h"
50 #include "frontends/alert.h"
51 #include "frontends/Application.h"
53 #include "support/ConsoleApplication.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"
76 using namespace lyx::support;
78 #if defined (USE_MACOSX_PACKAGING)
79 #include <crt_externs.h>
84 namespace Alert = frontend::Alert;
85 namespace os = support::os;
89 // Are we using the GUI at all? We default to true and this is changed
90 // to false when the export feature is used.
95 // Report on the terminal about spawned commands. The default is false
96 // and can be changed with the option -v (--verbose).
101 // We default to open documents in an already running instance, provided that
102 // the lyxpipe has been setup. This can be overridden either on the command
103 // line or through preference settings.
105 RunMode run_mode = PREFERRED;
108 // Tell what files can be silently overwritten during batch export.
109 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
110 // Unless specified on command line (through the -f switch) or through the
111 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
113 OverwriteFiles force_overwrite = UNSPECIFIED;
118 // Filled with the command line arguments "foo" of "-sysdir foo" or
120 string cl_system_support;
121 string cl_user_support;
125 LyX * singleton_ = 0;
127 void showFileError(string const & error)
129 Alert::warning(_("Could not read configuration file"),
130 bformat(_("Error while reading the configuration file\n%1$s.\n"
131 "Please check your installation."), from_utf8(error)));
136 /// The main application class private implementation.
139 : latexfonts_(0), spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
145 delete apple_spell_checker_;
146 delete aspell_checker_;
147 delete enchant_checker_;
148 delete hunspell_checker_;
152 BufferList buffer_list_;
154 KeyMap toplevel_keymap_;
156 CmdDef toplevel_cmddef_;
158 unique_ptr<Server> lyx_server_;
160 unique_ptr<ServerSocket> lyx_socket_;
162 unique_ptr<frontend::Application> application_;
163 /// lyx session, containing lastfiles, lastfilepos, and lastopened
164 unique_ptr<Session> session_;
166 /// Files to load at start.
167 vector<string> files_to_load_;
169 /// The messages translators.
170 map<string, Messages> messages_;
172 /// The file converters.
173 Converters converters_;
175 // The system converters copy after reading lyxrc.defaults.
176 Converters system_converters_;
181 Movers system_movers_;
183 /// the parsed command line batch command if any
184 vector<string> batch_commands;
187 LaTeXFonts * latexfonts_;
190 SpellChecker * spell_checker_;
192 SpellChecker * apple_spell_checker_;
194 SpellChecker * aspell_checker_;
196 SpellChecker * enchant_checker_;
198 SpellChecker * hunspell_checker_;
202 /// The main application class for console mode
203 class LyXConsoleApp : public ConsoleApplication
206 LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
207 : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
208 argc_(argc), argv_(argv)
213 int const exit_status = lyx_->execWithoutGui(argc_, argv_);
224 frontend::Application * theApp()
227 return singleton_->pimpl_->application_.get();
237 WordList::cleanupWordLists();
241 void lyx_exit(int exit_code)
244 // Something wrong happened so better save everything, just in
249 // Properly crash in debug mode in order to get a useful backtrace.
253 // In release mode, try to exit gracefully.
255 theApp()->exit(exit_code);
269 Messages & LyX::messages(string const & language)
271 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
273 if (it != pimpl_->messages_.end())
276 pair<map<string, Messages>::iterator, bool> result =
277 pimpl_->messages_.insert(make_pair(language, Messages(language)));
279 LATTEST(result.second);
280 return result.first->second;
284 int LyX::exec(int & argc, char * argv[])
286 // Minimal setting of locale before parsing command line
288 init_package(os::utf8_argv(0), string(), string());
289 // we do not get to this point when init_package throws an exception
291 } catch (ExceptionMessage const & message) {
292 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
295 // Here we need to parse the command line. At least
296 // we need to parse for "-dbg" and "-help"
297 easyParse(argc, argv);
300 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
301 } catch (ExceptionMessage const & message) {
302 if (message.type_ == ErrorException) {
303 Alert::error(message.title_, message.details_);
305 } else if (message.type_ == WarningException) {
306 Alert::warning(message.title_, message.details_);
310 // Reinit the messages machinery in case package() knows
311 // something interesting about the locale directory.
315 LyXConsoleApp app(this, argc, argv);
317 // Reestablish our defaults, as Qt overwrites them
318 // after creating app
324 // Let the frontend parse and remove all arguments that it knows
325 pimpl_->application_.reset(createApplication(argc, argv));
327 // Reestablish our defaults, as Qt overwrites them
328 // after createApplication()
331 // Parse and remove all known arguments in the LyX singleton
332 // Give an error for all remaining ones.
333 int exit_status = init(argc, argv);
335 // Kill the application object before exiting.
336 pimpl_->application_.reset();
342 // If not otherwise specified by a command line option or
343 // by preferences, we default to reuse a running instance.
344 if (run_mode == PREFERRED)
345 run_mode = USE_REMOTE;
348 /* Create a CoreApplication class that will provide the main event loop
349 * and the socket callback registering. With Qt, only QtCore
350 * library would be needed.
351 * When this is done, a server_mode could be created and the following two
352 * line would be moved out from here.
353 * However, note that the first of the two lines below triggers the
354 * "single instance" behavior, which should occur right at this point.
356 // Note: socket callback must be registered after init(argc, argv)
357 // such that package().temp_dir() is properly initialized.
358 pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
359 pimpl_->lyx_socket_.reset(new ServerSocket(
360 FileName(package().temp_dir().absFileName() + "/lyxsocket")));
362 // Start the real execution loop.
363 if (!theServer().deferredLoadingToOtherInstance())
364 exit_status = pimpl_->application_->exec();
365 else if (!pimpl_->files_to_load_.empty()) {
366 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
367 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
368 lyxerr << _("The following files could not be loaded:") << endl;
369 for (; it != end; ++it)
370 lyxerr << *it << endl;
379 void LyX::prepareExit()
381 // Clear the clipboard and selection stack:
382 cap::clearCutStack();
383 cap::clearSelection();
385 // Write the index file of the converter cache
386 ConverterCache::get().writeIndex();
388 // close buffers first
389 pimpl_->buffer_list_.closeAll();
391 // register session changes and shutdown server and socket
393 if (pimpl_->session_)
394 pimpl_->session_->writeFile();
395 pimpl_->session_.reset();
396 pimpl_->lyx_server_.reset();
397 pimpl_->lyx_socket_.reset();
400 // do any other cleanup procedures now
401 if (package().temp_dir() != package().system_temp_dir()) {
402 string const abs_tmpdir = package().temp_dir().absFileName();
403 if (!contains(package().temp_dir().absFileName(), "lyx_tmpdir")) {
404 docstring const msg =
405 bformat(_("%1$s does not appear like a LyX created temporary directory."),
406 from_utf8(abs_tmpdir));
407 Alert::warning(_("Cannot remove temporary directory"), msg);
409 LYXERR(Debug::INFO, "Deleting tmp dir "
410 << package().temp_dir().absFileName());
411 if (!package().temp_dir().destroyDirectory()) {
412 LYXERR0(bformat(_("Unable to remove the temporary directory %1$s"),
413 from_utf8(package().temp_dir().absFileName())));
418 // Kill the application object before exiting. This avoids crashes
419 // when exiting on Linux.
420 pimpl_->application_.reset();
424 void LyX::earlyExit(int status)
426 LATTEST(pimpl_->application_.get());
427 // LyX::pimpl_::application_ is not initialised at this
428 // point so it's safe to just exit after some cleanup.
434 int LyX::init(int & argc, char * argv[])
436 // check for any spurious extra arguments
437 // other than documents
438 for (int argi = 1; argi < argc ; ++argi) {
439 if (argv[argi][0] == '-') {
441 bformat(_("Wrong command line option `%1$s'. Exiting."),
442 from_utf8(os::utf8_argv(argi)))) << endl;
447 // Initialization of LyX (reads lyxrc and more)
448 LYXERR(Debug::INIT, "Initializing LyX::init...");
449 bool success = init();
450 LYXERR(Debug::INIT, "Initializing LyX::init...done");
454 // Remaining arguments are assumed to be files to load.
455 for (int argi = 1; argi < argc; ++argi)
456 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
458 if (!use_gui && pimpl_->files_to_load_.empty()) {
459 lyxerr << to_utf8(_("Missing filename for this operation.")) << endl;
464 pimpl_->files_to_load_.push_back(
465 i18nLibFileSearch("examples", "splash.lyx").absFileName());
472 int LyX::execWithoutGui(int & argc, char * argv[])
474 int exit_status = init(argc, argv);
480 // Used to keep track of which buffers were explicitly loaded by user request.
481 // This is necessary because master and child document buffers are loaded, even
482 // if they were not named on the command line. We do not want to dispatch to
484 vector<Buffer *> command_line_buffers;
486 // Load the files specified on the command line
487 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
488 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
489 for (; it != end; ++it) {
490 // get absolute path of file and add ".lyx" to the filename if necessary
491 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
497 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName());
498 LYXERR(Debug::FILES, "Loading " << fname);
499 if (buf && buf->loadLyXFile() == Buffer::ReadSuccess) {
500 ErrorList const & el = buf->errorList("Parse");
501 for(ErrorItem const & e : el)
503 command_line_buffers.push_back(buf);
506 pimpl_->buffer_list_.release(buf);
507 docstring const error_message =
508 bformat(_("LyX failed to load the following file: %1$s"),
509 from_utf8(fname.absFileName()));
510 lyxerr << to_utf8(error_message) << endl;
511 exit_status = 1; // failed
515 if (exit_status || pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
520 // Iterate through the buffers that were specified on the command line
521 bool final_success = false;
522 vector<Buffer *>::iterator buf_it = command_line_buffers.begin();
523 for (; buf_it != command_line_buffers.end(); ++buf_it) {
524 Buffer * buf = *buf_it;
525 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
526 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
528 for (; bcit != bcend; ++bcit) {
529 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
530 buf->dispatch(*bcit, dr);
531 final_success |= !dr.error();
535 return !final_success;
539 void execBatchCommands()
542 singleton_->execCommands();
546 void LyX::execCommands()
548 // The advantage of doing this here is that the event loop
549 // is already started. So any need for interaction will be
552 // if reconfiguration is needed.
553 if (LayoutFileList::get().empty()) {
554 switch (Alert::prompt(
555 _("No textclass is found"),
556 _("LyX will only have minimal functionality because no textclasses "
557 "have been found. You can either try to reconfigure LyX normally, "
558 "try to reconfigure without checking your LaTeX installation, or continue."),
565 // regular reconfigure
566 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
569 // reconfigure --without-latex-config
570 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
571 " --without-latex-config"));
578 // create the first main window
579 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
581 if (!pimpl_->files_to_load_.empty()) {
582 // if some files were specified at command-line we assume that the
583 // user wants to edit *these* files and not to restore the session.
584 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
586 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
588 // clear this list to save a few bytes of RAM
589 pimpl_->files_to_load_.clear();
591 pimpl_->application_->restoreGuiSession();
593 // Execute batch commands if available
594 if (pimpl_->batch_commands.empty())
597 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
598 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
599 for (; bcit != bcend; ++bcit) {
600 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
601 lyx::dispatch(lyxaction.lookupFunc(*bcit));
609 The SIGHUP signal does not exist on Windows and does not need to be handled.
611 Windows handles SIGFPE and SIGSEGV signals as expected.
613 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
614 cause a new thread to be spawned. This may well result in unexpected
615 behaviour by the single-threaded LyX.
617 SIGTERM signals will come only from another process actually sending
618 that signal using 'raise' in Windows' POSIX compatability layer. It will
619 not come from the general "terminate process" methods that everyone
620 actually uses (and which can't be trapped). Killing an app 'politely' on
621 Windows involves first sending a WM_CLOSE message, something that is
622 caught already by the Qt frontend.
624 For more information see:
626 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
627 ...signals are mostly useless on Windows for a variety of reasons that are
630 'UNIX Application Migration Guide, Chapter 9'
631 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
633 'How To Terminate an Application "Cleanly" in Win32'
634 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
638 static void error_handler(int err_sig)
640 // Throw away any signals other than the first one received.
641 static sig_atomic_t handling_error = false;
644 handling_error = true;
646 // We have received a signal indicating a fatal error, so
647 // try and save the data ASAP.
650 // These lyxerr calls may or may not work:
652 // Signals are asynchronous, so the main program may be in a very
653 // fragile state when a signal is processed and thus while a signal
654 // handler function executes.
655 // In general, therefore, we should avoid performing any
656 // I/O operations or calling most library and system functions from
659 // This shouldn't matter here, however, as we've already invoked
665 msg = _("SIGHUP signal caught!\nBye.");
669 msg = _("SIGFPE signal caught!\nBye.");
672 msg = _("SIGSEGV signal caught!\n"
673 "Sorry, you have found a bug in LyX, "
674 "hope you have not lost any data.\n"
675 "Please read the bug-reporting instructions "
676 "in 'Help->Introduction' and send us a bug report, "
677 "if necessary. Thanks!\nBye.");
686 lyxerr << "\nlyx: " << msg << endl;
687 // try to make a GUI message
688 Alert::error(_("LyX crashed!"), msg, true);
691 // Deinstall the signal handlers
693 signal(SIGHUP, SIG_DFL);
695 signal(SIGINT, SIG_DFL);
696 signal(SIGFPE, SIG_DFL);
697 signal(SIGSEGV, SIG_DFL);
698 signal(SIGTERM, SIG_DFL);
701 if (err_sig == SIGSEGV ||
702 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
704 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
707 // with abort() it crashes again.
720 void LyX::printError(ErrorItem const & ei)
722 docstring tmp = _("LyX: ") + ei.error + char_type(':')
724 cerr << to_utf8(tmp) << endl;
727 #if defined (USE_MACOSX_PACKAGING)
729 // Unexposed--extract an environment variable name from its NAME=VALUE
731 std::string varname(const char* line)
733 size_t nameLen = strcspn(line, "=");
734 if (nameLen == strlen(line)) {
735 return std::string();
737 return std::string(line, nameLen);
742 void cleanDuplicateEnvVars()
744 std::set<std::string> seen;
745 std::set<std::string> dupes;
747 // Create a list of the environment variables that appear more than once
748 for (char **read = *_NSGetEnviron(); *read; read++) {
749 std::string name = varname(*read);
750 if (name.size() == 0) {
753 if (seen.find(name) != seen.end()) {
760 // Loop over the list of duplicated variables
761 std::set<std::string>::iterator dupe = dupes.begin();
762 std::set<std::string>::iterator const dend = dupes.end();
763 for (; dupe != dend; ++dupe) {
764 const char *name = (*dupe).c_str();
765 char *val = getenv(name);
767 LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
768 // unsetenv removes *all* instances of the variable from the environment
771 // replace with the value from getenv (in practice appears to be the
772 // first value in the list)
773 setenv(name, val, 0);
780 static void initTemplatePath()
782 FileName const package_template_path =
783 FileName(addName(package().system_support().absFileName(), "templates"));
785 if (lyxrc.template_path.empty()) {
786 lyxrc.template_path = package_template_path.absFileName();
788 #if defined (USE_MACOSX_PACKAGING)
789 FileName const user_template_path =
790 FileName(addName(package().user_support().absFileName(), "templates"));
792 if (package_template_path != FileName(lyxrc.template_path) &&
793 user_template_path != FileName(lyxrc.template_path))
797 FileName const user_template_link =
798 FileName(addName(user_template_path.absFileName(),"SystemTemplates"));
799 if (user_template_link.isSymLink() && !equivalent(user_template_link, package_template_path)) {
800 user_template_link.removeFile();
802 if (!user_template_link.exists()) {
803 if (!package_template_path.link(user_template_link)) {
804 FileName const user_support = package().user_support();
805 if (user_support.exists() && user_support.isDirectory()) {
806 LYXERR(Debug::INIT, "Cannot create symlink " + user_template_link.absFileName());
807 lyxrc.template_path = package_template_path.absFileName();
811 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
813 lyxrc.template_path = user_template_path.absFileName();
821 signal(SIGHUP, error_handler);
823 signal(SIGFPE, error_handler);
824 signal(SIGSEGV, error_handler);
825 signal(SIGINT, error_handler);
826 signal(SIGTERM, error_handler);
827 // SIGPIPE can be safely ignored.
829 #if defined (USE_MACOSX_PACKAGING)
830 cleanDuplicateEnvVars();
833 lyxrc.tempdir_path = package().temp_dir().absFileName();
834 lyxrc.document_path = ".";
836 if (lyxrc.example_path.empty()) {
837 lyxrc.example_path = addPath(package().system_support().absFileName(),
842 // init LyXDir environment variable
843 string const lyx_dir = package().lyx_dir().absFileName();
844 LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
845 if (!setEnv("LyXDir", lyx_dir))
846 LYXERR(Debug::INIT, "\t... failed!");
848 if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
849 // -userdir was given on the command line.
850 // Make it available to child processes, otherwise tex2lyx
851 // would not find all layout files, and other converters might
853 string const user_dir = package().user_support().absFileName();
854 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
856 if (!setEnv(LYX_USERDIR_VER, user_dir))
857 LYXERR(Debug::INIT, "\t... failed!");
861 // Read configuration files
864 // This one may have been distributed along with LyX.
865 if (!readRcFile("lyxrc.dist"))
868 // Set the PATH correctly.
869 #if !defined (USE_POSIX_PACKAGING) && !defined (USE_HAIKU_PACKAGING)
870 // Add the directory containing the LyX executable to the path
871 // so that LyX can find things like tex2lyx.
872 if (package().build_support().empty())
873 prependEnvPath("PATH", package().binary_dir().absFileName());
875 if (!lyxrc.path_prefix.empty())
876 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
878 // Check that user LyX directory is ok.
880 string const lock_file = package().getConfigureLockName();
881 int fd = fileLock(lock_file.c_str());
883 if (queryUserLyXDir(package().explicit_user_support())) {
884 package().reconfigureUserLyXDir("");
885 // Now the user directory is present on first start.
888 fileUnlock(fd, lock_file.c_str());
892 // No need for a splash when there is no GUI
894 // Default is to overwrite the main file during export, unless
895 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
896 if (force_overwrite == UNSPECIFIED) {
897 string const what = getEnv("LYX_FORCE_OVERWRITE");
899 force_overwrite = ALL_FILES;
900 else if (what == "none")
901 force_overwrite = NO_FILES;
903 force_overwrite = MAIN_FILE;
907 // This one is generated in user_support directory by lib/configure.py.
908 if (!readRcFile("lyxrc.defaults"))
911 // Query the OS to know what formats are viewed natively
912 formats.setAutoOpen();
914 // Read lyxrc.dist again to be able to override viewer auto-detection.
915 readRcFile("lyxrc.dist");
917 system_lyxrc = lyxrc;
918 system_formats = formats;
919 pimpl_->system_converters_ = pimpl_->converters_;
920 pimpl_->system_movers_ = pimpl_->movers_;
921 system_lcolor = lcolor;
923 // This one is edited through the preferences dialog.
924 if (!readRcFile("preferences", true))
927 // The language may have been set to someting useful through prefs
930 if (!readEncodingsFile("encodings", "unicodesymbols"))
932 if (!readLanguagesFile("languages"))
935 LYXERR(Debug::INIT, "Reading layouts...");
937 LayoutFileList::get().read();
939 theModuleList.read();
941 // read keymap and ui files in batch mode as well
942 // because InsetInfo needs to know these to produce
943 // the correct output
945 // Set up command definitions
946 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
950 pimpl_->toplevel_keymap_.read("site");
951 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
952 // load user bind file user.bind
953 pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
955 if (lyxerr.debugging(Debug::LYXRC))
958 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
959 // Prepend path prefix a second time to take the user preferences into a account
960 if (!lyxrc.path_prefix.empty())
961 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
963 FileName const document_path(lyxrc.document_path);
964 if (document_path.exists() && document_path.isDirectory())
965 package().document_dir() = document_path;
967 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
968 if (package().temp_dir().empty()) {
969 Alert::error(_("Could not create temporary directory"),
970 bformat(_("Could not create a temporary directory in\n"
972 "Make sure that this path exists and is writable and try again."),
973 from_utf8(lyxrc.tempdir_path)));
974 // createLyXTmpDir() tries sufficiently hard to create a
975 // usable temp dir, so the probability to come here is
976 // close to zero. We therefore don't try to overcome this
977 // problem with e.g. asking the user for a new path and
978 // trying again but simply exit.
982 LYXERR(Debug::INIT, "LyX tmp dir: `"
983 << package().temp_dir().absFileName() << '\'');
985 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
986 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
988 // This must happen after package initialization and after lyxrc is
989 // read, therefore it can't be done by a static object.
990 ConverterCache::init();
996 void emergencyCleanup()
998 // what to do about tmpfiles is non-obvious. we would
999 // like to delete any we find, but our lyxdir might
1000 // contain documents etc. which might be helpful on
1003 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1005 if (singleton_->pimpl_->lyx_server_)
1006 singleton_->pimpl_->lyx_server_->emergencyCleanup();
1007 singleton_->pimpl_->lyx_server_.reset();
1008 singleton_->pimpl_->lyx_socket_.reset();
1013 bool LyX::queryUserLyXDir(bool explicit_userdir)
1015 // Does user directory exist?
1016 FileName const sup = package().user_support();
1017 if (sup.exists() && sup.isDirectory()) {
1018 first_start = false;
1020 return configFileNeedsUpdate("lyxrc.defaults")
1021 || configFileNeedsUpdate("lyxmodules.lst")
1022 || configFileNeedsUpdate("textclass.lst")
1023 || configFileNeedsUpdate("packages.lst");
1026 first_start = !explicit_userdir;
1028 // If the user specified explicitly a directory, ask whether
1029 // to create it. If the user says "no", then exit.
1030 if (explicit_userdir &&
1032 _("Missing user LyX directory"),
1033 bformat(_("You have specified a non-existent user "
1034 "LyX directory, %1$s.\n"
1035 "It is needed to keep your own configuration."),
1036 from_utf8(package().user_support().absFileName())),
1038 _("&Create directory"),
1040 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1041 earlyExit(EXIT_FAILURE);
1044 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1045 from_utf8(sup.absFileName()))) << endl;
1047 if (!sup.createDirectory(0755)) {
1048 // Failed, so let's exit.
1049 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1051 earlyExit(EXIT_FAILURE);
1058 bool LyX::readRcFile(string const & name, bool check_format)
1060 LYXERR(Debug::INIT, "About to read " << name << "... ");
1062 FileName const lyxrc_path = libFileSearch(string(), name);
1063 if (lyxrc_path.empty()) {
1064 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1066 // This was the previous logic, but can it be right??
1069 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1070 bool const success = lyxrc.read(lyxrc_path, check_format);
1072 showFileError(name);
1076 // Read the languages file `name'
1077 bool LyX::readLanguagesFile(string const & name)
1079 LYXERR(Debug::INIT, "About to read " << name << "...");
1081 FileName const lang_path = libFileSearch(string(), name);
1082 if (lang_path.empty()) {
1083 showFileError(name);
1086 languages.read(lang_path);
1091 // Read the encodings file `name'
1092 bool LyX::readEncodingsFile(string const & enc_name,
1093 string const & symbols_name)
1095 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1096 << symbols_name << "...");
1098 FileName const symbols_path = libFileSearch(string(), symbols_name);
1099 if (symbols_path.empty()) {
1100 showFileError(symbols_name);
1104 FileName const enc_path = libFileSearch(string(), enc_name);
1105 if (enc_path.empty()) {
1106 showFileError(enc_name);
1109 encodings.read(enc_path, symbols_path);
1116 /// return the the number of arguments consumed
1117 typedef function<int(string const &, string const &, string &)> cmd_helper;
1119 int parse_dbg(string const & arg, string const &, string &)
1122 cout << to_utf8(_("List of supported debug flags:")) << endl;
1123 Debug::showTags(cout);
1126 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1128 lyxerr.setLevel(Debug::value(arg));
1129 Debug::showLevel(lyxerr, lyxerr.level());
1134 int parse_help(string const &, string const &, string &)
1137 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1138 "Command line switches (case sensitive):\n"
1139 "\t-help summarize LyX usage\n"
1140 "\t-userdir dir set user directory to dir\n"
1141 "\t-sysdir dir set system directory to dir\n"
1142 "\t-geometry WxH+X+Y set geometry of the main window\n"
1143 "\t-dbg feature[,feature]...\n"
1144 " select the features to debug.\n"
1145 " Type `lyx -dbg' to see the list of features\n"
1146 "\t-x [--execute] command\n"
1147 " where command is a lyx command.\n"
1148 "\t-e [--export] fmt\n"
1149 " where fmt is the export format of choice. Look in\n"
1150 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1151 " to see which parameter (which differs from the format name\n"
1152 " in the File->Export menu) should be passed.\n"
1153 " Note that the order of -e and -x switches matters.\n"
1154 "\t-E [--export-to] fmt filename\n"
1155 " where fmt is the export format of choice (see --export),\n"
1156 " and filename is the destination filename.\n"
1157 "\t-i [--import] fmt file.xxx\n"
1158 " where fmt is the import format of choice\n"
1159 " and file.xxx is the file to be imported.\n"
1160 "\t-f [--force-overwrite] what\n"
1161 " where what is either `all', `main' or `none',\n"
1162 " specifying whether all files, main file only, or no files,\n"
1163 " respectively, are to be overwritten during a batch export.\n"
1164 " Anything else is equivalent to `all', but is not consumed.\n"
1165 "\t-n [--no-remote]\n"
1166 " open documents in a new instance\n"
1168 " open documents in an already running instance\n"
1169 " (a working lyxpipe is needed)\n"
1170 "\t-v [--verbose]\n"
1171 " report on terminal about spawned commands.\n"
1172 "\t-batch execute commands without launching GUI and exit.\n"
1173 "\t-version summarize version and build info\n"
1174 "Check the LyX man page for more details.")) << endl;
1180 int parse_version(string const &, string const &, string &)
1182 cout << "LyX " << lyx_version
1183 << " (" << lyx_release_date << ")" << endl;
1184 if (string(lyx_git_commit_hash) != "none")
1185 cout << to_utf8(_(" Git commit hash "))
1186 << string(lyx_git_commit_hash).substr(0,8) << endl;
1187 cout << lyx_version_info << endl;
1193 int parse_sysdir(string const & arg, string const &, string &)
1196 Alert::error(_("No system directory"),
1197 _("Missing directory for -sysdir switch"));
1200 cl_system_support = arg;
1205 int parse_userdir(string const & arg, string const &, string &)
1208 Alert::error(_("No user directory"),
1209 _("Missing directory for -userdir switch"));
1212 cl_user_support = arg;
1217 int parse_execute(string const & arg, string const &, string & batch)
1220 Alert::error(_("Incomplete command"),
1221 _("Missing command string after --execute switch"));
1229 int parse_export_to(string const & type, string const & output_file, string & batch)
1232 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1233 "--export-to switch")) << endl;
1236 if (output_file.empty()) {
1237 lyxerr << to_utf8(_("Missing destination filename after "
1238 "--export-to switch")) << endl;
1241 batch = "buffer-export " + type + " " + output_file;
1247 int parse_export(string const & type, string const &, string & batch)
1250 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1251 "--export switch")) << endl;
1254 batch = "buffer-export " + type;
1260 int parse_import(string const & type, string const & file, string & batch)
1263 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1264 "--import switch")) << endl;
1268 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1271 batch = "buffer-import " + type + ' ' + file;
1276 int parse_geometry(string const & arg1, string const &, string &)
1279 // don't remove "-geometry", it will be pruned out later in the
1280 // frontend if need be.
1285 int parse_batch(string const &, string const &, string &)
1292 int parse_noremote(string const &, string const &, string &)
1294 run_mode = NEW_INSTANCE;
1299 int parse_remote(string const &, string const &, string &)
1301 run_mode = USE_REMOTE;
1306 int parse_verbose(string const &, string const &, string &)
1313 int parse_force(string const & arg, string const &, string &)
1316 force_overwrite = ALL_FILES;
1318 } else if (arg == "main") {
1319 force_overwrite = MAIN_FILE;
1321 } else if (arg == "none") {
1322 force_overwrite = NO_FILES;
1325 force_overwrite = ALL_FILES;
1333 void LyX::easyParse(int & argc, char * argv[])
1335 map<string, cmd_helper> cmdmap;
1337 cmdmap["-dbg"] = parse_dbg;
1338 cmdmap["-help"] = parse_help;
1339 cmdmap["--help"] = parse_help;
1340 cmdmap["-version"] = parse_version;
1341 cmdmap["--version"] = parse_version;
1342 cmdmap["-sysdir"] = parse_sysdir;
1343 cmdmap["-userdir"] = parse_userdir;
1344 cmdmap["-x"] = parse_execute;
1345 cmdmap["--execute"] = parse_execute;
1346 cmdmap["-e"] = parse_export;
1347 cmdmap["--export"] = parse_export;
1348 cmdmap["-E"] = parse_export_to;
1349 cmdmap["--export-to"] = parse_export_to;
1350 cmdmap["-i"] = parse_import;
1351 cmdmap["--import"] = parse_import;
1352 cmdmap["-geometry"] = parse_geometry;
1353 cmdmap["-batch"] = parse_batch;
1354 cmdmap["-f"] = parse_force;
1355 cmdmap["--force-overwrite"] = parse_force;
1356 cmdmap["-n"] = parse_noremote;
1357 cmdmap["--no-remote"] = parse_noremote;
1358 cmdmap["-r"] = parse_remote;
1359 cmdmap["--remote"] = parse_remote;
1360 cmdmap["-v"] = parse_verbose;
1361 cmdmap["--verbose"] = parse_verbose;
1363 for (int i = 1; i < argc; ++i) {
1364 map<string, cmd_helper>::const_iterator it
1365 = cmdmap.find(argv[i]);
1367 // don't complain if not found - may be parsed later
1368 if (it == cmdmap.end())
1372 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1374 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1377 int const remove = 1 + it->second(arg, arg2, batch);
1379 pimpl_->batch_commands.push_back(batch);
1381 // Now, remove used arguments by shifting
1382 // the following ones remove places down.
1384 os::remove_internal_args(i, remove);
1386 for (int j = i; j < argc; ++j)
1387 argv[j] = argv[j + remove];
1394 FuncStatus getStatus(FuncRequest const & action)
1397 return theApp()->getStatus(action);
1401 DispatchResult const & dispatch(FuncRequest const & action)
1404 return theApp()->dispatch(action);
1408 void dispatch(FuncRequest const & action, DispatchResult & dr)
1411 theApp()->dispatch(action, dr);
1415 vector<string> & theFilesToLoad()
1417 LAPPERR(singleton_);
1418 return singleton_->pimpl_->files_to_load_;
1422 BufferList & theBufferList()
1424 LAPPERR(singleton_);
1425 return singleton_->pimpl_->buffer_list_;
1429 Server & theServer()
1431 // FIXME: this should not be use_gui dependent
1433 LAPPERR(singleton_);
1434 return *singleton_->pimpl_->lyx_server_;
1438 ServerSocket & theServerSocket()
1440 // FIXME: this should not be use_gui dependent
1442 LAPPERR(singleton_);
1443 return *singleton_->pimpl_->lyx_socket_;
1447 KeyMap & theTopLevelKeymap()
1449 LAPPERR(singleton_);
1450 return singleton_->pimpl_->toplevel_keymap_;
1454 Converters & theConverters()
1456 LAPPERR(singleton_);
1457 return singleton_->pimpl_->converters_;
1461 Converters & theSystemConverters()
1463 LAPPERR(singleton_);
1464 return singleton_->pimpl_->system_converters_;
1468 Movers & theMovers()
1470 LAPPERR(singleton_);
1471 return singleton_->pimpl_->movers_;
1475 Mover const & getMover(string const & fmt)
1477 LAPPERR(singleton_);
1478 return singleton_->pimpl_->movers_(fmt);
1482 void setMover(string const & fmt, string const & command)
1484 LAPPERR(singleton_);
1485 singleton_->pimpl_->movers_.set(fmt, command);
1489 Movers & theSystemMovers()
1491 LAPPERR(singleton_);
1492 return singleton_->pimpl_->system_movers_;
1496 Messages const & getMessages(string const & language)
1498 LAPPERR(singleton_);
1499 return singleton_->messages(language);
1503 Messages const & getGuiMessages()
1505 LAPPERR(singleton_);
1506 return singleton_->messages(Messages::guiLanguage());
1510 Session & theSession()
1512 LAPPERR(singleton_);
1513 return *singleton_->pimpl_->session_.get();
1517 LaTeXFonts & theLaTeXFonts()
1519 LAPPERR(singleton_);
1520 if (!singleton_->pimpl_->latexfonts_)
1521 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1522 return *singleton_->pimpl_->latexfonts_;
1526 CmdDef & theTopLevelCmdDef()
1528 LAPPERR(singleton_);
1529 return singleton_->pimpl_->toplevel_cmddef_;
1533 SpellChecker * theSpellChecker()
1535 if (!singleton_->pimpl_->spell_checker_)
1537 return singleton_->pimpl_->spell_checker_;
1541 void setSpellChecker()
1543 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1544 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1546 if (lyxrc.spellchecker == "native") {
1547 #if defined(USE_MACOSX_PACKAGING)
1548 if (!singleton_->pimpl_->apple_spell_checker_)
1549 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1550 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1552 singleton_->pimpl_->spell_checker_ = 0;
1554 } else if (lyxrc.spellchecker == "aspell") {
1555 #if defined(USE_ASPELL)
1556 if (!singleton_->pimpl_->aspell_checker_)
1557 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1558 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1560 singleton_->pimpl_->spell_checker_ = 0;
1562 } else if (lyxrc.spellchecker == "enchant") {
1563 #if defined(USE_ENCHANT)
1564 if (!singleton_->pimpl_->enchant_checker_)
1565 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1566 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1568 singleton_->pimpl_->spell_checker_ = 0;
1570 } else if (lyxrc.spellchecker == "hunspell") {
1571 #if defined(USE_HUNSPELL)
1572 if (!singleton_->pimpl_->hunspell_checker_)
1573 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1574 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1576 singleton_->pimpl_->spell_checker_ = 0;
1579 singleton_->pimpl_->spell_checker_ = 0;
1581 if (singleton_->pimpl_->spell_checker_) {
1582 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1583 singleton_->pimpl_->spell_checker_->advanceChangeNumber();