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();
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();
940 //... and the cite engines
941 theCiteEnginesList.read();
943 // read keymap and ui files in batch mode as well
944 // because InsetInfo needs to know these to produce
945 // the correct output
947 // Set up command definitions
948 pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
952 pimpl_->toplevel_keymap_.read("site");
953 pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
954 // load user bind file user.bind
955 pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
957 if (lyxerr.debugging(Debug::LYXRC))
960 os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
961 // Prepend path prefix a second time to take the user preferences into a account
962 if (!lyxrc.path_prefix.empty())
963 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
965 FileName const document_path(lyxrc.document_path);
966 if (document_path.exists() && document_path.isDirectory())
967 package().document_dir() = document_path;
969 package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
970 if (package().temp_dir().empty()) {
971 Alert::error(_("Could not create temporary directory"),
972 bformat(_("Could not create a temporary directory in\n"
974 "Make sure that this path exists and is writable and try again."),
975 from_utf8(lyxrc.tempdir_path)));
976 // createLyXTmpDir() tries sufficiently hard to create a
977 // usable temp dir, so the probability to come here is
978 // close to zero. We therefore don't try to overcome this
979 // problem with e.g. asking the user for a new path and
980 // trying again but simply exit.
984 LYXERR(Debug::INIT, "LyX tmp dir: `"
985 << package().temp_dir().absFileName() << '\'');
987 LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
988 pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
990 // This must happen after package initialization and after lyxrc is
991 // read, therefore it can't be done by a static object.
992 ConverterCache::init();
998 void emergencyCleanup()
1000 // what to do about tmpfiles is non-obvious. we would
1001 // like to delete any we find, but our lyxdir might
1002 // contain documents etc. which might be helpful on
1005 singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1007 if (singleton_->pimpl_->lyx_server_)
1008 singleton_->pimpl_->lyx_server_->emergencyCleanup();
1009 singleton_->pimpl_->lyx_server_.reset();
1010 singleton_->pimpl_->lyx_socket_.reset();
1015 bool LyX::queryUserLyXDir(bool explicit_userdir)
1017 // Does user directory exist?
1018 FileName const sup = package().user_support();
1019 if (sup.exists() && sup.isDirectory()) {
1020 first_start = false;
1022 return configFileNeedsUpdate("lyxrc.defaults")
1023 || configFileNeedsUpdate("lyxmodules.lst")
1024 || configFileNeedsUpdate("textclass.lst")
1025 || configFileNeedsUpdate("packages.lst")
1026 || configFileNeedsUpdate("lyxciteengines.lst");
1029 first_start = !explicit_userdir;
1031 // If the user specified explicitly a directory, ask whether
1032 // to create it. If the user says "no", then exit.
1033 if (explicit_userdir &&
1035 _("Missing user LyX directory"),
1036 bformat(_("You have specified a non-existent user "
1037 "LyX directory, %1$s.\n"
1038 "It is needed to keep your own configuration."),
1039 from_utf8(package().user_support().absFileName())),
1041 _("&Create directory"),
1043 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1044 earlyExit(EXIT_FAILURE);
1047 lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1048 from_utf8(sup.absFileName()))) << endl;
1050 if (!sup.createDirectory(0755)) {
1051 // Failed, so let's exit.
1052 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1054 earlyExit(EXIT_FAILURE);
1061 bool LyX::readRcFile(string const & name, bool check_format)
1063 LYXERR(Debug::INIT, "About to read " << name << "... ");
1065 FileName const lyxrc_path = libFileSearch(string(), name);
1066 if (lyxrc_path.empty()) {
1067 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1069 // This was the previous logic, but can it be right??
1072 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1073 bool const success = lyxrc.read(lyxrc_path, check_format);
1075 showFileError(name);
1079 // Read the languages file `name'
1080 bool LyX::readLanguagesFile(string const & name)
1082 LYXERR(Debug::INIT, "About to read " << name << "...");
1084 FileName const lang_path = libFileSearch(string(), name);
1085 if (lang_path.empty()) {
1086 showFileError(name);
1089 languages.read(lang_path);
1094 // Read the encodings file `name'
1095 bool LyX::readEncodingsFile(string const & enc_name,
1096 string const & symbols_name)
1098 LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1099 << symbols_name << "...");
1101 FileName const symbols_path = libFileSearch(string(), symbols_name);
1102 if (symbols_path.empty()) {
1103 showFileError(symbols_name);
1107 FileName const enc_path = libFileSearch(string(), enc_name);
1108 if (enc_path.empty()) {
1109 showFileError(enc_name);
1112 encodings.read(enc_path, symbols_path);
1119 /// return the the number of arguments consumed
1120 typedef function<int(string const &, string const &, string &)> cmd_helper;
1122 int parse_dbg(string const & arg, string const &, string &)
1125 cout << to_utf8(_("List of supported debug flags:")) << endl;
1126 Debug::showTags(cout);
1129 lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1131 lyxerr.setLevel(Debug::value(arg));
1132 Debug::showLevel(lyxerr, lyxerr.level());
1137 int parse_help(string const &, string const &, string &)
1140 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1141 "Command line switches (case sensitive):\n"
1142 "\t-help summarize LyX usage\n"
1143 "\t-userdir dir set user directory to dir\n"
1144 "\t-sysdir dir set system directory to dir\n"
1145 "\t-geometry WxH+X+Y set geometry of the main window\n"
1146 "\t-dbg feature[,feature]...\n"
1147 " select the features to debug.\n"
1148 " Type `lyx -dbg' to see the list of features\n"
1149 "\t-x [--execute] command\n"
1150 " where command is a lyx command.\n"
1151 "\t-e [--export] fmt\n"
1152 " where fmt is the export format of choice. Look in\n"
1153 " Tools->Preferences->File Handling->File Formats->Short Name\n"
1154 " to see which parameter (which differs from the format name\n"
1155 " in the File->Export menu) should be passed.\n"
1156 " Note that the order of -e and -x switches matters.\n"
1157 "\t-E [--export-to] fmt filename\n"
1158 " where fmt is the export format of choice (see --export),\n"
1159 " and filename is the destination filename.\n"
1160 "\t-i [--import] fmt file.xxx\n"
1161 " where fmt is the import format of choice\n"
1162 " and file.xxx is the file to be imported.\n"
1163 "\t-f [--force-overwrite] what\n"
1164 " where what is either `all', `main' or `none',\n"
1165 " specifying whether all files, main file only, or no files,\n"
1166 " respectively, are to be overwritten during a batch export.\n"
1167 " Anything else is equivalent to `all', but is not consumed.\n"
1168 "\t-n [--no-remote]\n"
1169 " open documents in a new instance\n"
1171 " open documents in an already running instance\n"
1172 " (a working lyxpipe is needed)\n"
1173 "\t-v [--verbose]\n"
1174 " report on terminal about spawned commands.\n"
1175 "\t-batch execute commands without launching GUI and exit.\n"
1176 "\t-version summarize version and build info\n"
1177 "Check the LyX man page for more details.")) << endl;
1183 int parse_version(string const &, string const &, string &)
1185 cout << "LyX " << lyx_version
1186 << " (" << lyx_release_date << ")" << endl;
1187 if (string(lyx_git_commit_hash) != "none")
1188 cout << to_utf8(_(" Git commit hash "))
1189 << string(lyx_git_commit_hash).substr(0,8) << endl;
1190 cout << lyx_version_info << endl;
1196 int parse_sysdir(string const & arg, string const &, string &)
1199 Alert::error(_("No system directory"),
1200 _("Missing directory for -sysdir switch"));
1203 cl_system_support = arg;
1208 int parse_userdir(string const & arg, string const &, string &)
1211 Alert::error(_("No user directory"),
1212 _("Missing directory for -userdir switch"));
1215 cl_user_support = arg;
1220 int parse_execute(string const & arg, string const &, string & batch)
1223 Alert::error(_("Incomplete command"),
1224 _("Missing command string after --execute switch"));
1232 int parse_export_to(string const & type, string const & output_file, string & batch)
1235 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1236 "--export-to switch")) << endl;
1239 if (output_file.empty()) {
1240 lyxerr << to_utf8(_("Missing destination filename after "
1241 "--export-to switch")) << endl;
1244 batch = "buffer-export " + type + " " + output_file;
1250 int parse_export(string const & type, string const &, string & batch)
1253 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1254 "--export switch")) << endl;
1257 batch = "buffer-export " + type;
1263 int parse_import(string const & type, string const & file, string & batch)
1266 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1267 "--import switch")) << endl;
1271 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1274 batch = "buffer-import " + type + ' ' + file;
1279 int parse_geometry(string const & arg1, string const &, string &)
1282 // don't remove "-geometry", it will be pruned out later in the
1283 // frontend if need be.
1288 int parse_batch(string const &, string const &, string &)
1295 int parse_noremote(string const &, string const &, string &)
1297 run_mode = NEW_INSTANCE;
1302 int parse_remote(string const &, string const &, string &)
1304 run_mode = USE_REMOTE;
1309 int parse_verbose(string const &, string const &, string &)
1316 int parse_force(string const & arg, string const &, string &)
1319 force_overwrite = ALL_FILES;
1321 } else if (arg == "main") {
1322 force_overwrite = MAIN_FILE;
1324 } else if (arg == "none") {
1325 force_overwrite = NO_FILES;
1328 force_overwrite = ALL_FILES;
1336 void LyX::easyParse(int & argc, char * argv[])
1338 map<string, cmd_helper> cmdmap;
1340 cmdmap["-dbg"] = parse_dbg;
1341 cmdmap["-help"] = parse_help;
1342 cmdmap["--help"] = parse_help;
1343 cmdmap["-version"] = parse_version;
1344 cmdmap["--version"] = parse_version;
1345 cmdmap["-sysdir"] = parse_sysdir;
1346 cmdmap["-userdir"] = parse_userdir;
1347 cmdmap["-x"] = parse_execute;
1348 cmdmap["--execute"] = parse_execute;
1349 cmdmap["-e"] = parse_export;
1350 cmdmap["--export"] = parse_export;
1351 cmdmap["-E"] = parse_export_to;
1352 cmdmap["--export-to"] = parse_export_to;
1353 cmdmap["-i"] = parse_import;
1354 cmdmap["--import"] = parse_import;
1355 cmdmap["-geometry"] = parse_geometry;
1356 cmdmap["-batch"] = parse_batch;
1357 cmdmap["-f"] = parse_force;
1358 cmdmap["--force-overwrite"] = parse_force;
1359 cmdmap["-n"] = parse_noremote;
1360 cmdmap["--no-remote"] = parse_noremote;
1361 cmdmap["-r"] = parse_remote;
1362 cmdmap["--remote"] = parse_remote;
1363 cmdmap["-v"] = parse_verbose;
1364 cmdmap["--verbose"] = parse_verbose;
1366 for (int i = 1; i < argc; ++i) {
1367 map<string, cmd_helper>::const_iterator it
1368 = cmdmap.find(argv[i]);
1370 // don't complain if not found - may be parsed later
1371 if (it == cmdmap.end())
1375 (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1377 (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1380 int const remove = 1 + it->second(arg, arg2, batch);
1382 pimpl_->batch_commands.push_back(batch);
1384 // Now, remove used arguments by shifting
1385 // the following ones remove places down.
1387 os::remove_internal_args(i, remove);
1389 for (int j = i; j < argc; ++j)
1390 argv[j] = argv[j + remove];
1397 FuncStatus getStatus(FuncRequest const & action)
1400 return theApp()->getStatus(action);
1404 DispatchResult const & dispatch(FuncRequest const & action)
1407 return theApp()->dispatch(action);
1411 void dispatch(FuncRequest const & action, DispatchResult & dr)
1414 theApp()->dispatch(action, dr);
1418 vector<string> & theFilesToLoad()
1420 LAPPERR(singleton_);
1421 return singleton_->pimpl_->files_to_load_;
1425 BufferList & theBufferList()
1427 LAPPERR(singleton_);
1428 return singleton_->pimpl_->buffer_list_;
1432 Server & theServer()
1434 // FIXME: this should not be use_gui dependent
1436 LAPPERR(singleton_);
1437 return *singleton_->pimpl_->lyx_server_;
1441 ServerSocket & theServerSocket()
1443 // FIXME: this should not be use_gui dependent
1445 LAPPERR(singleton_);
1446 return *singleton_->pimpl_->lyx_socket_;
1450 KeyMap & theTopLevelKeymap()
1452 LAPPERR(singleton_);
1453 return singleton_->pimpl_->toplevel_keymap_;
1457 Converters & theConverters()
1459 LAPPERR(singleton_);
1460 return singleton_->pimpl_->converters_;
1464 Converters & theSystemConverters()
1466 LAPPERR(singleton_);
1467 return singleton_->pimpl_->system_converters_;
1471 Movers & theMovers()
1473 LAPPERR(singleton_);
1474 return singleton_->pimpl_->movers_;
1478 Mover const & getMover(string const & fmt)
1480 LAPPERR(singleton_);
1481 return singleton_->pimpl_->movers_(fmt);
1485 void setMover(string const & fmt, string const & command)
1487 LAPPERR(singleton_);
1488 singleton_->pimpl_->movers_.set(fmt, command);
1492 Movers & theSystemMovers()
1494 LAPPERR(singleton_);
1495 return singleton_->pimpl_->system_movers_;
1499 Messages const & getMessages(string const & language)
1501 LAPPERR(singleton_);
1502 return singleton_->messages(language);
1506 Messages const & getGuiMessages()
1508 LAPPERR(singleton_);
1509 return singleton_->messages(Messages::guiLanguage());
1513 Session & theSession()
1515 LAPPERR(singleton_);
1516 return *singleton_->pimpl_->session_.get();
1520 LaTeXFonts & theLaTeXFonts()
1522 LAPPERR(singleton_);
1523 if (!singleton_->pimpl_->latexfonts_)
1524 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1525 return *singleton_->pimpl_->latexfonts_;
1529 CmdDef & theTopLevelCmdDef()
1531 LAPPERR(singleton_);
1532 return singleton_->pimpl_->toplevel_cmddef_;
1536 SpellChecker * theSpellChecker()
1538 if (!singleton_->pimpl_->spell_checker_)
1540 return singleton_->pimpl_->spell_checker_;
1544 void setSpellChecker()
1546 SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1547 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1549 if (lyxrc.spellchecker == "native") {
1550 #if defined(USE_MACOSX_PACKAGING)
1551 if (!singleton_->pimpl_->apple_spell_checker_)
1552 singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1553 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1555 singleton_->pimpl_->spell_checker_ = 0;
1557 } else if (lyxrc.spellchecker == "aspell") {
1558 #if defined(USE_ASPELL)
1559 if (!singleton_->pimpl_->aspell_checker_)
1560 singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1561 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1563 singleton_->pimpl_->spell_checker_ = 0;
1565 } else if (lyxrc.spellchecker == "enchant") {
1566 #if defined(USE_ENCHANT)
1567 if (!singleton_->pimpl_->enchant_checker_)
1568 singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1569 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1571 singleton_->pimpl_->spell_checker_ = 0;
1573 } else if (lyxrc.spellchecker == "hunspell") {
1574 #if defined(USE_HUNSPELL)
1575 if (!singleton_->pimpl_->hunspell_checker_)
1576 singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1577 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1579 singleton_->pimpl_->spell_checker_ = 0;
1582 singleton_->pimpl_->spell_checker_ = 0;
1584 if (singleton_->pimpl_->spell_checker_) {
1585 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1586 singleton_->pimpl_->spell_checker_->advanceChangeNumber();