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/lassert.h"
56 #include "support/debug.h"
57 #include "support/environment.h"
58 #include "support/ExceptionMessage.h"
59 #include "support/filetools.h"
60 #include "support/gettext.h"
61 #include "support/lstrings.h"
62 #include "support/Messages.h"
63 #include "support/os.h"
64 #include "support/Package.h"
65 #include "support/unique_ptr.h"
77 using namespace lyx::support;
79 #if defined (USE_MACOSX_PACKAGING)
80 #include <crt_externs.h>
85 namespace Alert = frontend::Alert;
86 namespace os = support::os;
90 // Are we using the GUI at all? We default to true and this is changed
91 // to false when the export feature is used.
96 // Report on the terminal about spawned commands. The default is false
97 // and can be changed with the option -v (--verbose).
102 // We default to open documents in an already running instance, provided that
103 // the lyxpipe has been setup. This can be overridden either on the command
104 // line or through preference settings.
106 RunMode run_mode = PREFERRED;
109 // Tell what files can be silently overwritten during batch export.
110 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
111 // Unless specified on command line (through the -f switch) or through the
112 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
114 OverwriteFiles force_overwrite = UNSPECIFIED;
119 // Filled with the command line arguments "foo" of "-sysdir foo" or
121 string cl_system_support;
122 string cl_user_support;
126 LyX * singleton_ = 0;
128 void showFileError(string const & error)
130 Alert::warning(_("Could not read configuration file"),
131 bformat(_("Error while reading the configuration file\n%1$s.\n"
132 "Please check your installation."), from_utf8(error)));
137 /// The main application class private implementation.
140 : latexfonts_(0), spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
146 delete apple_spell_checker_;
147 delete aspell_checker_;
148 delete enchant_checker_;
149 delete hunspell_checker_;
153 BufferList buffer_list_;
155 KeyMap toplevel_keymap_;
157 CmdDef toplevel_cmddef_;
159 unique_ptr<Server> lyx_server_;
161 unique_ptr<ServerSocket> lyx_socket_;
163 unique_ptr<frontend::Application> application_;
164 /// lyx session, containing lastfiles, lastfilepos, and lastopened
165 unique_ptr<Session> session_;
167 /// Files to load at start.
168 vector<string> files_to_load_;
170 /// The messages translators.
171 map<string, Messages> messages_;
173 /// The file converters.
174 Converters converters_;
176 // The system converters copy after reading lyxrc.defaults.
177 Converters system_converters_;
182 Movers system_movers_;
184 /// the parsed command line batch command if any
185 vector<string> batch_commands;
188 LaTeXFonts * latexfonts_;
191 SpellChecker * spell_checker_;
193 SpellChecker * apple_spell_checker_;
195 SpellChecker * aspell_checker_;
197 SpellChecker * enchant_checker_;
199 SpellChecker * hunspell_checker_;
203 /// The main application class for console mode
204 class LyXConsoleApp : public ConsoleApplication
207 LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
208 : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
209 argc_(argc), argv_(argv)
214 int const exit_status = lyx_->execWithoutGui(argc_, argv_);
225 frontend::Application * theApp()
228 return singleton_->pimpl_->application_.get();
238 WordList::cleanupWordLists();
242 void lyx_exit(int exit_code)
245 // Something wrong happened so better save everything, just in
250 // Properly crash in debug mode in order to get a useful backtrace.
254 // In release mode, try to exit gracefully.
256 theApp()->exit(exit_code);
270 Messages & LyX::messages(string const & language)
272 map<string, Messages>::iterator it = pimpl_->messages_.find(language);
274 if (it != pimpl_->messages_.end())
277 pair<map<string, Messages>::iterator, bool> result =
278 pimpl_->messages_.insert(make_pair(language, Messages(language)));
280 LATTEST(result.second);
281 return result.first->second;
285 int LyX::exec(int & argc, char * argv[])
287 // Minimal setting of locale before parsing command line
289 init_package(os::utf8_argv(0), string(), string());
290 // we do not get to this point when init_package throws an exception
292 } catch (ExceptionMessage const & message) {
293 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
296 // Here we need to parse the command line. At least
297 // we need to parse for "-dbg" and "-help"
298 easyParse(argc, argv);
301 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
302 } catch (ExceptionMessage const & message) {
303 if (message.type_ == ErrorException) {
304 Alert::error(message.title_, message.details_);
306 } else if (message.type_ == WarningException) {
307 Alert::warning(message.title_, message.details_);
311 // Reinit the messages machinery in case package() knows
312 // something interesting about the locale directory.
316 LyXConsoleApp app(this, argc, argv);
318 // Reestablish our defaults, as Qt overwrites them
319 // after creating app
325 // Let the frontend parse and remove all arguments that it knows
326 pimpl_->application_.reset(createApplication(argc, argv));
328 // Reestablish our defaults, as Qt overwrites them
329 // after createApplication()
332 // Parse and remove all known arguments in the LyX singleton
333 // Give an error for all remaining ones.
334 int exit_status = init(argc, argv);
336 // Kill the application object before exiting.
337 pimpl_->application_.reset();
343 // If not otherwise specified by a command line option or
344 // by preferences, we default to reuse a running instance.
345 if (run_mode == PREFERRED)
346 run_mode = USE_REMOTE;
349 /* Create a CoreApplication class that will provide the main event loop
350 * and the socket callback registering. With Qt, only QtCore
351 * library would be needed.
352 * When this is done, a server_mode could be created and the following two
353 * line would be moved out from here.
354 * However, note that the first of the two lines below triggers the
355 * "single instance" behavior, which should occur right at this point.
357 // Note: socket callback must be registered after init(argc, argv)
358 // such that package().temp_dir() is properly initialized.
359 pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
360 pimpl_->lyx_socket_.reset(new ServerSocket(
361 FileName(package().temp_dir().absFileName() + "/lyxsocket")));
363 // Start the real execution loop.
364 if (!theServer().deferredLoadingToOtherInstance())
365 exit_status = pimpl_->application_->exec();
366 else if (!pimpl_->files_to_load_.empty()) {
367 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
368 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
369 lyxerr << _("The following files could not be loaded:") << endl;
370 for (; it != end; ++it)
371 lyxerr << *it << endl;
380 void LyX::prepareExit()
382 // Clear the clipboard and selection stack:
383 cap::clearCutStack();
384 cap::clearSelection();
386 // Write the index file of the converter cache
387 ConverterCache::get().writeIndex();
389 // close buffers first
390 pimpl_->buffer_list_.closeAll();
392 // register session changes and shutdown server and socket
394 if (pimpl_->session_)
395 pimpl_->session_->writeFile();
396 pimpl_->session_.reset();
397 pimpl_->lyx_server_.reset();
398 pimpl_->lyx_socket_.reset();
401 // do any other cleanup procedures now
402 if (package().temp_dir() != package().system_temp_dir()) {
403 string const abs_tmpdir = package().temp_dir().absFileName();
404 if (!contains(package().temp_dir().absFileName(), "lyx_tmpdir")) {
405 docstring const msg =
406 bformat(_("%1$s does not appear like a LyX created temporary directory."),
407 from_utf8(abs_tmpdir));
408 Alert::warning(_("Cannot remove temporary directory"), msg);
410 LYXERR(Debug::INFO, "Deleting tmp dir "
411 << package().temp_dir().absFileName());
412 if (!package().temp_dir().destroyDirectory()) {
413 LYXERR0(bformat(_("Unable to remove the temporary directory %1$s"),
414 from_utf8(package().temp_dir().absFileName())));
419 // Kill the application object before exiting. This avoids crashes
420 // when exiting on Linux.
421 pimpl_->application_.reset();
425 void LyX::earlyExit(int status)
427 LATTEST(pimpl_->application_.get());
428 // LyX::pimpl_::application_ is not initialised at this
429 // point so it's safe to just exit after some cleanup.
435 int LyX::init(int & argc, char * argv[])
437 // check for any spurious extra arguments
438 // other than documents
439 for (int argi = 1; argi < argc ; ++argi) {
440 if (argv[argi][0] == '-') {
442 bformat(_("Wrong command line option `%1$s'. Exiting."),
443 from_utf8(os::utf8_argv(argi)))) << endl;
448 // Initialization of LyX (reads lyxrc and more)
449 LYXERR(Debug::INIT, "Initializing LyX::init...");
450 bool success = init();
451 LYXERR(Debug::INIT, "Initializing LyX::init...done");
455 // Remaining arguments are assumed to be files to load.
456 for (int argi = 1; argi < argc; ++argi)
457 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
459 if (!use_gui && pimpl_->files_to_load_.empty()) {
460 lyxerr << to_utf8(_("Missing filename for this operation.")) << endl;
465 pimpl_->files_to_load_.push_back(
466 i18nLibFileSearch("examples", "splash.lyx").absFileName());
473 int LyX::execWithoutGui(int & argc, char * argv[])
475 int exit_status = init(argc, argv);
481 // Used to keep track of which buffers were explicitly loaded by user request.
482 // This is necessary because master and child document buffers are loaded, even
483 // if they were not named on the command line. We do not want to dispatch to
485 vector<Buffer *> command_line_buffers;
487 // Load the files specified on the command line
488 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
489 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
490 for (; it != end; ++it) {
491 // get absolute path of file and add ".lyx" to the filename if necessary
492 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
498 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName());
499 LYXERR(Debug::FILES, "Loading " << fname);
500 if (buf && buf->loadLyXFile() == Buffer::ReadSuccess) {
501 ErrorList const & el = buf->errorList("Parse");
502 for(ErrorItem const & e : el)
504 command_line_buffers.push_back(buf);
507 pimpl_->buffer_list_.release(buf);
508 docstring const error_message =
509 bformat(_("LyX failed to load the following file: %1$s"),
510 from_utf8(fname.absFileName()));
511 lyxerr << to_utf8(error_message) << endl;
512 exit_status = 1; // failed
516 if (exit_status || pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
521 // Iterate through the buffers that were specified on the command line
522 bool final_success = false;
523 vector<Buffer *>::iterator buf_it = command_line_buffers.begin();
524 for (; buf_it != command_line_buffers.end(); ++buf_it) {
525 Buffer * buf = *buf_it;
526 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
527 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
529 for (; bcit != bcend; ++bcit) {
530 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
531 buf->dispatch(*bcit, dr);
532 final_success |= !dr.error();
536 return !final_success;
540 void execBatchCommands()
543 singleton_->execCommands();
547 void LyX::execCommands()
549 // The advantage of doing this here is that the event loop
550 // is already started. So any need for interaction will be
553 // if reconfiguration is needed.
554 if (LayoutFileList::get().empty()) {
555 switch (Alert::prompt(
556 _("No textclass is found"),
557 _("LyX will only have minimal functionality because no textclasses "
558 "have been found. You can either try to reconfigure LyX normally, "
559 "try to reconfigure without checking your LaTeX installation, or continue."),
566 // regular reconfigure
567 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
570 // reconfigure --without-latex-config
571 lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
572 " --without-latex-config"));
579 // create the first main window
580 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
582 if (!pimpl_->files_to_load_.empty()) {
583 // if some files were specified at command-line we assume that the
584 // user wants to edit *these* files and not to restore the session.
585 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
587 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
589 // clear this list to save a few bytes of RAM
590 pimpl_->files_to_load_.clear();
592 pimpl_->application_->restoreGuiSession();
594 // Execute batch commands if available
595 if (pimpl_->batch_commands.empty())
598 vector<string>::const_iterator bcit = pimpl_->batch_commands.begin();
599 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
600 for (; bcit != bcend; ++bcit) {
601 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
602 lyx::dispatch(lyxaction.lookupFunc(*bcit));
610 The SIGHUP signal does not exist on Windows and does not need to be handled.
612 Windows handles SIGFPE and SIGSEGV signals as expected.
614 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
615 cause a new thread to be spawned. This may well result in unexpected
616 behaviour by the single-threaded LyX.
618 SIGTERM signals will come only from another process actually sending
619 that signal using 'raise' in Windows' POSIX compatability layer. It will
620 not come from the general "terminate process" methods that everyone
621 actually uses (and which can't be trapped). Killing an app 'politely' on
622 Windows involves first sending a WM_CLOSE message, something that is
623 caught already by the Qt frontend.
625 For more information see:
627 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
628 ...signals are mostly useless on Windows for a variety of reasons that are
631 'UNIX Application Migration Guide, Chapter 9'
632 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
634 'How To Terminate an Application "Cleanly" in Win32'
635 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
639 static void error_handler(int err_sig)
641 // Throw away any signals other than the first one received.
642 static sig_atomic_t handling_error = false;
645 handling_error = true;
647 // We have received a signal indicating a fatal error, so
648 // try and save the data ASAP.
651 // These lyxerr calls may or may not work:
653 // Signals are asynchronous, so the main program may be in a very
654 // fragile state when a signal is processed and thus while a signal
655 // handler function executes.
656 // In general, therefore, we should avoid performing any
657 // I/O operations or calling most library and system functions from
660 // This shouldn't matter here, however, as we've already invoked
666 msg = _("SIGHUP signal caught!\nBye.");
670 msg = _("SIGFPE signal caught!\nBye.");
673 msg = _("SIGSEGV signal caught!\n"
674 "Sorry, you have found a bug in LyX, "
675 "hope you have not lost any data.\n"
676 "Please read the bug-reporting instructions "
677 "in 'Help->Introduction' and send us a bug report, "
678 "if necessary. Thanks!\nBye.");
687 lyxerr << "\nlyx: " << msg << endl;
688 // try to make a GUI message
689 Alert::error(_("LyX crashed!"), msg, true);
692 // Deinstall the signal handlers
694 signal(SIGHUP, SIG_DFL);
696 signal(SIGINT, SIG_DFL);
697 signal(SIGFPE, SIG_DFL);
698 signal(SIGSEGV, SIG_DFL);
699 signal(SIGTERM, SIG_DFL);
702 if (err_sig == SIGSEGV ||
703 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
705 if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
708 // with abort() it crashes again.
721 void LyX::printError(ErrorItem const & ei)
723 docstring tmp = _("LyX: ") + ei.error + char_type(':')
725 cerr << to_utf8(tmp) << endl;
728 #if defined (USE_MACOSX_PACKAGING)
730 // Unexposed--extract an environment variable name from its NAME=VALUE
732 std::string varname(const char* line)
734 size_t nameLen = strcspn(line, "=");
735 if (nameLen == strlen(line)) {
736 return std::string();
738 return std::string(line, nameLen);
743 void cleanDuplicateEnvVars()
745 std::set<std::string> seen;
746 std::set<std::string> dupes;
748 // Create a list of the environment variables that appear more than once
749 for (char **read = *_NSGetEnviron(); *read; read++) {
750 std::string name = varname(*read);
751 if (name.size() == 0) {
754 if (seen.find(name) != seen.end()) {
761 // Loop over the list of duplicated variables
762 std::set<std::string>::iterator dupe = dupes.begin();
763 std::set<std::string>::iterator const dend = dupes.end();
764 for (; dupe != dend; ++dupe) {
765 const char *name = (*dupe).c_str();
766 char *val = getenv(name);
768 LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
769 // unsetenv removes *all* instances of the variable from the environment
772 // replace with the value from getenv (in practice appears to be the
773 // first value in the list)
774 setenv(name, val, 0);
781 static void initTemplatePath()
783 FileName const package_template_path =
784 FileName(addName(package().system_support().absFileName(), "templates"));
786 if (lyxrc.template_path.empty()) {
787 lyxrc.template_path = package_template_path.absFileName();
789 #if defined (USE_MACOSX_PACKAGING)
790 FileName const user_template_path =
791 FileName(addName(package().user_support().absFileName(), "templates"));
793 if (package_template_path != FileName(lyxrc.template_path) &&
794 user_template_path != FileName(lyxrc.template_path))
798 FileName const user_template_link =
799 FileName(addName(user_template_path.absFileName(),"SystemTemplates"));
800 if (user_template_link.isSymLink() && !equivalent(user_template_link, package_template_path)) {
801 user_template_link.removeFile();
803 if (!user_template_link.exists()) {
804 if (!package_template_path.link(user_template_link)) {
805 FileName const user_support = package().user_support();
806 if (user_support.exists() && user_support.isDirectory()) {
807 LYXERR(Debug::INIT, "Cannot create symlink " + user_template_link.absFileName());
808 lyxrc.template_path = package_template_path.absFileName();
812 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
814 lyxrc.template_path = user_template_path.absFileName();
822 signal(SIGHUP, error_handler);
824 signal(SIGFPE, error_handler);
825 signal(SIGSEGV, error_handler);
826 signal(SIGINT, error_handler);
827 signal(SIGTERM, error_handler);
828 // SIGPIPE can be safely ignored.
830 #if defined (USE_MACOSX_PACKAGING)
831 cleanDuplicateEnvVars();
834 lyxrc.tempdir_path = package().temp_dir().absFileName();
835 lyxrc.document_path = ".";
837 if (lyxrc.example_path.empty()) {
838 lyxrc.example_path = addPath(package().system_support().absFileName(),
843 // init LyXDir environment variable
844 string const lyx_dir = package().lyx_dir().absFileName();
845 LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
846 if (!setEnv("LyXDir", lyx_dir))
847 LYXERR(Debug::INIT, "\t... failed!");
849 if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
850 // -userdir was given on the command line.
851 // Make it available to child processes, otherwise tex2lyx
852 // would not find all layout files, and other converters might
854 string const user_dir = package().user_support().absFileName();
855 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
857 if (!setEnv(LYX_USERDIR_VER, user_dir))
858 LYXERR(Debug::INIT, "\t... failed!");
862 // Read configuration files
865 // This one may have been distributed along with LyX.
866 if (!readRcFile("lyxrc.dist"))
869 // Set the PATH correctly.
870 #if !defined (USE_POSIX_PACKAGING) && !defined (USE_HAIKU_PACKAGING)
871 // Add the directory containing the LyX executable to the path
872 // so that LyX can find things like tex2lyx.
873 if (package().build_support().empty())
874 prependEnvPath("PATH", package().binary_dir().absFileName());
876 if (!lyxrc.path_prefix.empty())
877 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
879 // Check that user LyX directory is ok.
881 string const lock_file = package().getConfigureLockName();
882 int fd = fileLock(lock_file.c_str());
884 if (queryUserLyXDir(package().explicit_user_support())) {
885 package().reconfigureUserLyXDir("");
886 // Now the user directory is present on first start.
889 fileUnlock(fd, lock_file.c_str());
893 // No need for a splash when there is no GUI
895 // Default is to overwrite the main file during export, unless
896 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
897 if (force_overwrite == UNSPECIFIED) {
898 string const what = getEnv("LYX_FORCE_OVERWRITE");
900 force_overwrite = ALL_FILES;
901 else if (what == "none")
902 force_overwrite = NO_FILES;
904 force_overwrite = MAIN_FILE;
908 // This one is generated in user_support directory by lib/configure.py.
909 if (!readRcFile("lyxrc.defaults"))
912 // Query the OS to know what formats are viewed natively
913 formats.setAutoOpen();
915 // Read lyxrc.dist again to be able to override viewer auto-detection.
916 readRcFile("lyxrc.dist");
918 system_lyxrc = lyxrc;
919 system_formats = formats;
920 pimpl_->system_converters_ = pimpl_->converters_;
921 pimpl_->system_movers_ = pimpl_->movers_;
922 system_lcolor = lcolor;
924 // This one is edited through the preferences dialog.
925 if (!readRcFile("preferences", true))
928 // The language may have been set to someting useful through prefs
931 if (!readEncodingsFile("encodings", "unicodesymbols"))
933 if (!readLanguagesFile("languages"))
936 LYXERR(Debug::INIT, "Reading layouts...");
938 LayoutFileList::get().read();
940 theModuleList.read();
941 //... and the cite engines
942 theCiteEnginesList.read();
944 // read keymap and ui files in batch mode as well
945 // because InsetInfo needs to know these to produce
946 // the correct output
948 // Set up command definitions
949 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
953 pimpl_->toplevel_keymap_.read("site");
954 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
955 // load user bind file user.bind
956 pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
958 if (lyxerr.debugging(Debug::LYXRC))
961 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
962 // Prepend path prefix a second time to take the user preferences into a account
963 if (!lyxrc.path_prefix.empty())
964 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
966 FileName const document_path(lyxrc.document_path);
967 if (document_path.exists() && document_path.isDirectory())
968 package().document_dir() = document_path;
970 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
971 if (package().temp_dir().empty()) {
972 Alert::error(_("Could not create temporary directory"),
973 bformat(_("Could not create a temporary directory in\n"
975 "Make sure that this path exists and is writable and try again."),
976 from_utf8(lyxrc.tempdir_path)));
977 // createLyXTmpDir() tries sufficiently hard to create a
978 // usable temp dir, so the probability to come here is
979 // close to zero. We therefore don't try to overcome this
980 // problem with e.g. asking the user for a new path and
981 // trying again but simply exit.
985 LYXERR(Debug::INIT, "LyX tmp dir: `"
986 << package().temp_dir().absFileName() << '\'');
988 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
989 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
991 // This must happen after package initialization and after lyxrc is
992 // read, therefore it can't be done by a static object.
993 ConverterCache::init();
999 void emergencyCleanup()
1001 // what to do about tmpfiles is non-obvious. we would
1002 // like to delete any we find, but our lyxdir might
1003 // contain documents etc. which might be helpful on
1006 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1008 if (singleton_->pimpl_->lyx_server_)
1009 singleton_->pimpl_->lyx_server_->emergencyCleanup();
1010 singleton_->pimpl_->lyx_server_.reset();
1011 singleton_->pimpl_->lyx_socket_.reset();
1016 bool LyX::queryUserLyXDir(bool explicit_userdir)
1018 // Does user directory exist?
1019 FileName const sup = package().user_support();
1020 if (sup.exists() && sup.isDirectory()) {
1021 first_start = false;
1023 return configFileNeedsUpdate("lyxrc.defaults")
1024 || configFileNeedsUpdate("lyxmodules.lst")
1025 || configFileNeedsUpdate("textclass.lst")
1026 || configFileNeedsUpdate("packages.lst")
1027 || configFileNeedsUpdate("lyxciteengines.lst");
1030 first_start = !explicit_userdir;
1032 // If the user specified explicitly a directory, ask whether
1033 // to create it. If the user says "no", then exit.
1034 if (explicit_userdir &&
1036 _("Missing user LyX directory"),
1037 bformat(_("You have specified a non-existent user "
1038 "LyX directory, %1$s.\n"
1039 "It is needed to keep your own configuration."),
1040 from_utf8(package().user_support().absFileName())),
1042 _("&Create directory"),
1044 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1045 earlyExit(EXIT_FAILURE);
1048 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1049 from_utf8(sup.absFileName()))) << endl;
1051 if (!sup.createDirectory(0755)) {
1052 // Failed, so let's exit.
1053 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1055 earlyExit(EXIT_FAILURE);
1062 bool LyX::readRcFile(string const & name, bool check_format)
1064 LYXERR(Debug::INIT, "About to read " << name << "... ");
1066 FileName const lyxrc_path = libFileSearch(string(), name);
1067 if (lyxrc_path.empty()) {
1068 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1070 // This was the previous logic, but can it be right??
1073 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1074 bool const success = lyxrc.read(lyxrc_path, check_format);
1076 showFileError(name);
1080 // Read the languages file `name'
1081 bool LyX::readLanguagesFile(string const & name)
1083 LYXERR(Debug::INIT, "About to read " << name << "...");
1085 FileName const lang_path = libFileSearch(string(), name);
1086 if (lang_path.empty()) {
1087 showFileError(name);
1090 languages.read(lang_path);
1095 // Read the encodings file `name'
1096 bool LyX::readEncodingsFile(string const & enc_name,
1097 string const & symbols_name)
1099 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1100 << symbols_name << "...");
1102 FileName const symbols_path = libFileSearch(string(), symbols_name);
1103 if (symbols_path.empty()) {
1104 showFileError(symbols_name);
1108 FileName const enc_path = libFileSearch(string(), enc_name);
1109 if (enc_path.empty()) {
1110 showFileError(enc_name);
1113 encodings.read(enc_path, symbols_path);
1120 /// return the the number of arguments consumed
1121 typedef function<int(string const &, string const &, string &)> cmd_helper;
1123 int parse_dbg(string const & arg, string const &, string &)
1126 cout << to_utf8(_("List of supported debug flags:")) << endl;
1127 Debug::showTags(cout);
1130 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1132 lyxerr.setLevel(Debug::value(arg));
1133 Debug::showLevel(lyxerr, lyxerr.level());
1138 int parse_help(string const &, string const &, string &)
1141 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1142 "Command line switches (case sensitive):\n"
1143 "\t-help summarize LyX usage\n"
1144 "\t-userdir dir set user directory to dir\n"
1145 "\t-sysdir dir set system directory to dir\n"
1146 "\t-geometry WxH+X+Y set geometry of the main window\n"
1147 "\t-dbg feature[,feature]...\n"
1148 " select the features to debug.\n"
1149 " Type `lyx -dbg' to see the list of features\n"
1150 "\t-x [--execute] command\n"
1151 " where command is a lyx command.\n"
1152 "\t-e [--export] fmt\n"
1153 " where fmt is the export format of choice. Look in\n"
1154 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1155 " to see which parameter (which differs from the format name\n"
1156 " in the File->Export menu) should be passed.\n"
1157 " Note that the order of -e and -x switches matters.\n"
1158 "\t-E [--export-to] fmt filename\n"
1159 " where fmt is the export format of choice (see --export),\n"
1160 " and filename is the destination filename.\n"
1161 "\t-i [--import] fmt file.xxx\n"
1162 " where fmt is the import format of choice\n"
1163 " and file.xxx is the file to be imported.\n"
1164 "\t-f [--force-overwrite] what\n"
1165 " where what is either `all', `main' or `none',\n"
1166 " specifying whether all files, main file only, or no files,\n"
1167 " respectively, are to be overwritten during a batch export.\n"
1168 " Anything else is equivalent to `all', but is not consumed.\n"
1169 "\t-n [--no-remote]\n"
1170 " open documents in a new instance\n"
1172 " open documents in an already running instance\n"
1173 " (a working lyxpipe is needed)\n"
1174 "\t-v [--verbose]\n"
1175 " report on terminal about spawned commands.\n"
1176 "\t-batch execute commands without launching GUI and exit.\n"
1177 "\t-version summarize version and build info\n"
1178 "Check the LyX man page for more details.")) << endl;
1184 int parse_version(string const &, string const &, string &)
1186 cout << "LyX " << lyx_version
1187 << " (" << lyx_release_date << ")" << endl;
1188 if (string(lyx_git_commit_hash) != "none")
1189 cout << to_utf8(_(" Git commit hash "))
1190 << string(lyx_git_commit_hash).substr(0,8) << endl;
1191 cout << lyx_version_info << endl;
1197 int parse_sysdir(string const & arg, string const &, string &)
1200 Alert::error(_("No system directory"),
1201 _("Missing directory for -sysdir switch"));
1204 cl_system_support = arg;
1209 int parse_userdir(string const & arg, string const &, string &)
1212 Alert::error(_("No user directory"),
1213 _("Missing directory for -userdir switch"));
1216 cl_user_support = arg;
1221 int parse_execute(string const & arg, string const &, string & batch)
1224 Alert::error(_("Incomplete command"),
1225 _("Missing command string after --execute switch"));
1233 int parse_export_to(string const & type, string const & output_file, string & batch)
1236 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1237 "--export-to switch")) << endl;
1240 if (output_file.empty()) {
1241 lyxerr << to_utf8(_("Missing destination filename after "
1242 "--export-to switch")) << endl;
1245 batch = "buffer-export " + type + " " + output_file;
1251 int parse_export(string const & type, string const &, string & batch)
1254 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1255 "--export switch")) << endl;
1258 batch = "buffer-export " + type;
1264 int parse_import(string const & type, string const & file, string & batch)
1267 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1268 "--import switch")) << endl;
1272 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1275 batch = "buffer-import " + type + ' ' + file;
1280 int parse_geometry(string const & arg1, string const &, string &)
1283 // don't remove "-geometry", it will be pruned out later in the
1284 // frontend if need be.
1289 int parse_batch(string const &, string const &, string &)
1296 int parse_noremote(string const &, string const &, string &)
1298 run_mode = NEW_INSTANCE;
1303 int parse_remote(string const &, string const &, string &)
1305 run_mode = USE_REMOTE;
1310 int parse_verbose(string const &, string const &, string &)
1317 int parse_force(string const & arg, string const &, string &)
1320 force_overwrite = ALL_FILES;
1322 } else if (arg == "main") {
1323 force_overwrite = MAIN_FILE;
1325 } else if (arg == "none") {
1326 force_overwrite = NO_FILES;
1329 force_overwrite = ALL_FILES;
1337 void LyX::easyParse(int & argc, char * argv[])
1339 map<string, cmd_helper> cmdmap;
1341 cmdmap["-dbg"] = parse_dbg;
1342 cmdmap["-help"] = parse_help;
1343 cmdmap["--help"] = parse_help;
1344 cmdmap["-version"] = parse_version;
1345 cmdmap["--version"] = parse_version;
1346 cmdmap["-sysdir"] = parse_sysdir;
1347 cmdmap["-userdir"] = parse_userdir;
1348 cmdmap["-x"] = parse_execute;
1349 cmdmap["--execute"] = parse_execute;
1350 cmdmap["-e"] = parse_export;
1351 cmdmap["--export"] = parse_export;
1352 cmdmap["-E"] = parse_export_to;
1353 cmdmap["--export-to"] = parse_export_to;
1354 cmdmap["-i"] = parse_import;
1355 cmdmap["--import"] = parse_import;
1356 cmdmap["-geometry"] = parse_geometry;
1357 cmdmap["-batch"] = parse_batch;
1358 cmdmap["-f"] = parse_force;
1359 cmdmap["--force-overwrite"] = parse_force;
1360 cmdmap["-n"] = parse_noremote;
1361 cmdmap["--no-remote"] = parse_noremote;
1362 cmdmap["-r"] = parse_remote;
1363 cmdmap["--remote"] = parse_remote;
1364 cmdmap["-v"] = parse_verbose;
1365 cmdmap["--verbose"] = parse_verbose;
1367 for (int i = 1; i < argc; ++i) {
1368 map<string, cmd_helper>::const_iterator it
1369 = cmdmap.find(argv[i]);
1371 // don't complain if not found - may be parsed later
1372 if (it == cmdmap.end())
1376 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1378 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1381 int const remove = 1 + it->second(arg, arg2, batch);
1383 pimpl_->batch_commands.push_back(batch);
1385 // Now, remove used arguments by shifting
1386 // the following ones remove places down.
1388 os::remove_internal_args(i, remove);
1390 for (int j = i; j < argc; ++j)
1391 argv[j] = argv[j + remove];
1398 FuncStatus getStatus(FuncRequest const & action)
1401 return theApp()->getStatus(action);
1405 DispatchResult const & dispatch(FuncRequest const & action)
1408 return theApp()->dispatch(action);
1412 void dispatch(FuncRequest const & action, DispatchResult & dr)
1415 theApp()->dispatch(action, dr);
1419 vector<string> & theFilesToLoad()
1421 LAPPERR(singleton_);
1422 return singleton_->pimpl_->files_to_load_;
1426 BufferList & theBufferList()
1428 LAPPERR(singleton_);
1429 return singleton_->pimpl_->buffer_list_;
1433 Server & theServer()
1435 // FIXME: this should not be use_gui dependent
1437 LAPPERR(singleton_);
1438 return *singleton_->pimpl_->lyx_server_;
1442 ServerSocket & theServerSocket()
1444 // FIXME: this should not be use_gui dependent
1446 LAPPERR(singleton_);
1447 return *singleton_->pimpl_->lyx_socket_;
1451 KeyMap & theTopLevelKeymap()
1453 LAPPERR(singleton_);
1454 return singleton_->pimpl_->toplevel_keymap_;
1458 Converters & theConverters()
1460 LAPPERR(singleton_);
1461 return singleton_->pimpl_->converters_;
1465 Converters & theSystemConverters()
1467 LAPPERR(singleton_);
1468 return singleton_->pimpl_->system_converters_;
1472 Movers & theMovers()
1474 LAPPERR(singleton_);
1475 return singleton_->pimpl_->movers_;
1479 Mover const & getMover(string const & fmt)
1481 LAPPERR(singleton_);
1482 return singleton_->pimpl_->movers_(fmt);
1486 void setMover(string const & fmt, string const & command)
1488 LAPPERR(singleton_);
1489 singleton_->pimpl_->movers_.set(fmt, command);
1493 Movers & theSystemMovers()
1495 LAPPERR(singleton_);
1496 return singleton_->pimpl_->system_movers_;
1500 Messages const & getMessages(string const & language)
1502 LAPPERR(singleton_);
1503 return singleton_->messages(language);
1507 Messages const & getGuiMessages()
1509 LAPPERR(singleton_);
1510 return singleton_->messages(Messages::guiLanguage());
1514 Session & theSession()
1516 LAPPERR(singleton_);
1517 return *singleton_->pimpl_->session_.get();
1521 LaTeXFonts & theLaTeXFonts()
1523 LAPPERR(singleton_);
1524 if (!singleton_->pimpl_->latexfonts_)
1525 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1526 return *singleton_->pimpl_->latexfonts_;
1530 CmdDef & theTopLevelCmdDef()
1532 LAPPERR(singleton_);
1533 return singleton_->pimpl_->toplevel_cmddef_;
1537 SpellChecker * theSpellChecker()
1539 if (!singleton_->pimpl_->spell_checker_)
1541 return singleton_->pimpl_->spell_checker_;
1545 void setSpellChecker()
1547 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1548 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1550 if (lyxrc.spellchecker == "native") {
1551 #if defined(USE_MACOSX_PACKAGING)
1552 if (!singleton_->pimpl_->apple_spell_checker_)
1553 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1554 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1556 singleton_->pimpl_->spell_checker_ = 0;
1558 } else if (lyxrc.spellchecker == "aspell") {
1559 #if defined(USE_ASPELL)
1560 if (!singleton_->pimpl_->aspell_checker_)
1561 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1562 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1564 singleton_->pimpl_->spell_checker_ = 0;
1566 } else if (lyxrc.spellchecker == "enchant") {
1567 #if defined(USE_ENCHANT)
1568 if (!singleton_->pimpl_->enchant_checker_)
1569 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1570 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1572 singleton_->pimpl_->spell_checker_ = 0;
1574 } else if (lyxrc.spellchecker == "hunspell") {
1575 #if defined(USE_HUNSPELL)
1576 if (!singleton_->pimpl_->hunspell_checker_)
1577 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1578 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1580 singleton_->pimpl_->spell_checker_ = 0;
1583 singleton_->pimpl_->spell_checker_ = 0;
1585 if (singleton_->pimpl_->spell_checker_) {
1586 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1587 singleton_->pimpl_->spell_checker_->advanceChangeNumber();