]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
157d43259149702f9a9cd606f1d1f868f0c49124
[lyx.git] / src / LyX.cpp
1 /**
2  * \file LyX.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16 #include <version.h>
17
18 #include "LyX.h"
19
20 #include "AppleSpellChecker.h"
21 #include "AspellChecker.h"
22 #include "Buffer.h"
23 #include "BufferList.h"
24 #include "CmdDef.h"
25 #include "CiteEnginesList.h"
26 #include "ColorSet.h"
27 #include "ConverterCache.h"
28 #include "Converter.h"
29 #include "CutAndPaste.h"
30 #include "EnchantChecker.h"
31 #include "Encoding.h"
32 #include "ErrorList.h"
33 #include "Format.h"
34 #include "FuncStatus.h"
35 #include "HunspellChecker.h"
36 #include "KeyMap.h"
37 #include "Language.h"
38 #include "LaTeXFonts.h"
39 #include "LayoutFile.h"
40 #include "Lexer.h"
41 #include "LyX.h"
42 #include "LyXAction.h"
43 #include "LyXRC.h"
44 #include "ModuleList.h"
45 #include "Mover.h"
46 #include "Server.h"
47 #include "ServerSocket.h"
48 #include "Session.h"
49 #include "WordList.h"
50
51 #include "frontends/alert.h"
52 #include "frontends/Application.h"
53
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"
66
67 #include <algorithm>
68 #include <csignal>
69 #include <iostream>
70 #include <functional>
71 #include <map>
72 #include <stdlib.h>
73 #include <string>
74 #include <vector>
75
76 using namespace std;
77 using namespace lyx::support;
78
79 #if defined (USE_MACOSX_PACKAGING)
80 #include <crt_externs.h>
81 #endif
82
83 namespace lyx {
84
85 namespace Alert = frontend::Alert;
86 namespace os = support::os;
87
88
89
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.
92
93 bool use_gui = true;
94
95
96 // Report on the terminal about spawned commands. The default is false
97 // and can be changed with the option -v (--verbose).
98
99 bool verbose = false;
100
101
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.
105
106 RunMode run_mode = PREFERRED;
107
108
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.
113
114 OverwriteFiles force_overwrite = UNSPECIFIED;
115
116
117 namespace {
118
119 // Filled with the command line arguments "foo" of "-sysdir foo" or
120 // "-userdir foo".
121 string cl_system_support;
122 string cl_user_support;
123
124 string geometryArg;
125
126 LyX * singleton_ = 0;
127
128 void showFileError(string const & error)
129 {
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)));
133 }
134
135 } // namespace anon
136
137 /// The main application class private implementation.
138 struct LyX::Impl {
139         Impl()
140                 : latexfonts_(0), spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
141         {}
142
143         ~Impl()
144         {
145                 delete latexfonts_;
146                 delete apple_spell_checker_;
147                 delete aspell_checker_;
148                 delete enchant_checker_;
149                 delete hunspell_checker_;
150         }
151
152         ///
153         BufferList buffer_list_;
154         ///
155         KeyMap toplevel_keymap_;
156         ///
157         CmdDef toplevel_cmddef_;
158         ///
159         unique_ptr<Server> lyx_server_;
160         ///
161         unique_ptr<ServerSocket> lyx_socket_;
162         ///
163         unique_ptr<frontend::Application> application_;
164         /// lyx session, containing lastfiles, lastfilepos, and lastopened
165         unique_ptr<Session> session_;
166
167         /// Files to load at start.
168         vector<string> files_to_load_;
169
170         /// The messages translators.
171         map<string, Messages> messages_;
172
173         /// The file converters.
174         Converters converters_;
175
176         // The system converters copy after reading lyxrc.defaults.
177         Converters system_converters_;
178
179         ///
180         Movers movers_;
181         ///
182         Movers system_movers_;
183
184         /// the parsed command line batch command if any
185         vector<string> batch_commands;
186
187         ///
188         LaTeXFonts * latexfonts_;
189
190         ///
191         SpellChecker * spell_checker_;
192         ///
193         SpellChecker * apple_spell_checker_;
194         ///
195         SpellChecker * aspell_checker_;
196         ///
197         SpellChecker * enchant_checker_;
198         ///
199         SpellChecker * hunspell_checker_;
200 };
201
202
203 /// The main application class for console mode
204 class LyXConsoleApp : public ConsoleApplication
205 {
206 public:
207         LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
208                 : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
209                   argc_(argc), argv_(argv)
210         {
211         }
212         void doExec()
213         {
214                 int const exit_status = lyx_->execWithoutGui(argc_, argv_);
215                 exit(exit_status);
216         }
217 private:
218         LyX * lyx_;
219         int & argc_;
220         char ** argv_;
221 };
222
223
224 ///
225 frontend::Application * theApp()
226 {
227         if (singleton_)
228                 return singleton_->pimpl_->application_.get();
229         else
230                 return 0;
231 }
232
233
234 LyX::~LyX()
235 {
236         delete pimpl_;
237         singleton_ = 0;
238         WordList::cleanupWordLists();
239 }
240
241
242 void lyx_exit(int exit_code)
243 {
244         if (exit_code)
245                 // Something wrong happened so better save everything, just in
246                 // case.
247                 emergencyCleanup();
248
249 #ifndef NDEBUG
250         // Properly crash in debug mode in order to get a useful backtrace.
251         abort();
252 #endif
253
254         // In release mode, try to exit gracefully.
255         if (theApp())
256                 theApp()->exit(exit_code);
257         else
258                 exit(exit_code);
259 }
260
261
262 LyX::LyX()
263         : first_start(false)
264 {
265         singleton_ = this;
266         pimpl_ = new Impl;
267 }
268
269
270 Messages & LyX::messages(string const & language)
271 {
272         map<string, Messages>::iterator it = pimpl_->messages_.find(language);
273
274         if (it != pimpl_->messages_.end())
275                 return it->second;
276
277         pair<map<string, Messages>::iterator, bool> result =
278                         pimpl_->messages_.insert(make_pair(language, Messages(language)));
279
280         LATTEST(result.second);
281         return result.first->second;
282 }
283
284
285 int LyX::exec(int & argc, char * argv[])
286 {
287         // Minimal setting of locale before parsing command line
288         try {
289                 init_package(os::utf8_argv(0), string(), string());
290                 // we do not get to this point when init_package throws an exception
291                 setLocale();
292         } catch (ExceptionMessage const & message) {
293                 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
294         }
295
296         // Here we need to parse the command line. At least
297         // we need to parse for "-dbg" and "-help"
298         easyParse(argc, argv);
299
300         try {
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_);
305                         lyx_exit(1);
306                 } else if (message.type_ == WarningException) {
307                         Alert::warning(message.title_, message.details_);
308                 }
309         }
310
311         // Reinit the messages machinery in case package() knows
312         // something interesting about the locale directory.
313         setLocale();
314
315         if (!use_gui) {
316                 LyXConsoleApp app(this, argc, argv);
317
318                 // Reestablish our defaults, as Qt overwrites them
319                 // after creating app
320                 setLocale();//???
321
322                 return app.exec();
323         }
324
325         // Let the frontend parse and remove all arguments that it knows
326         pimpl_->application_.reset(createApplication(argc, argv));
327
328         // Reestablish our defaults, as Qt overwrites them
329         // after createApplication()
330         setLocale();//???
331
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);
335         if (exit_status) {
336                 // Kill the application object before exiting.
337                 pimpl_->application_.reset();
338                 use_gui = false;
339                 prepareExit();
340                 return exit_status;
341         }
342
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;
347
348         // FIXME
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.
356         */
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")));
362
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;
372         }
373
374         prepareExit();
375
376         return exit_status;
377 }
378
379
380 void LyX::prepareExit()
381 {
382         // Clear the clipboard and selection stack:
383         cap::clearCutStack();
384         cap::clearSelection();
385
386         // Write the index file of the converter cache
387         ConverterCache::get().writeIndex();
388
389         // close buffers first
390         pimpl_->buffer_list_.closeAll();
391
392         // register session changes and shutdown server and socket
393         if (use_gui) {
394                 if (pimpl_->session_)
395                         pimpl_->session_->writeFile();
396                 pimpl_->session_.reset();
397                 pimpl_->lyx_server_.reset();
398                 pimpl_->lyx_socket_.reset();
399         }
400
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);
409                 } else {
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())));
415                         }
416                 }
417         }
418
419         // Kill the application object before exiting. This avoids crashes
420         // when exiting on Linux.
421         pimpl_->application_.reset();
422 }
423
424
425 void LyX::earlyExit(int status)
426 {
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.
430         prepareExit();
431         exit(status);
432 }
433
434
435 int LyX::init(int & argc, char * argv[])
436 {
437         // check for any spurious extra arguments
438         // other than documents
439         for (int argi = 1; argi < argc ; ++argi) {
440                 if (argv[argi][0] == '-') {
441                         lyxerr << to_utf8(
442                                 bformat(_("Wrong command line option `%1$s'. Exiting."),
443                                 from_utf8(os::utf8_argv(argi)))) << endl;
444                         return EXIT_FAILURE;
445                 }
446         }
447
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");
452         if (!success)
453                 return EXIT_FAILURE;
454
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));
458
459         if (!use_gui && pimpl_->files_to_load_.empty()) {
460                 lyxerr << to_utf8(_("Missing filename for this operation.")) << endl;
461                 return EXIT_FAILURE;
462         }
463
464         if (first_start) {
465                 pimpl_->files_to_load_.push_back(
466                         i18nLibFileSearch("examples", "splash.lyx").absFileName());
467         }
468
469         return EXIT_SUCCESS;
470 }
471
472
473 int LyX::execWithoutGui(int & argc, char * argv[])
474 {
475         int exit_status = init(argc, argv);
476         if (exit_status) {
477                 prepareExit();
478                 return exit_status;
479         }
480
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
484         // those.
485         vector<Buffer *> command_line_buffers;
486
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",
493                                                                                                                                 may_not_exist);
494
495                 if (fname.empty())
496                         continue;
497
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)
503                                 printError(e);
504                         command_line_buffers.push_back(buf);
505                 } else {
506                         if (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
513                 }
514         }
515
516         if (exit_status || pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
517                 prepareExit();
518                 return exit_status;
519         }
520
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();
528                 DispatchResult dr;
529                 for (; bcit != bcend; ++bcit) {
530                         LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
531                         buf->dispatch(*bcit, dr);
532                         final_success |= !dr.error();
533                 }
534         }
535         prepareExit();
536         return !final_success;
537 }
538
539
540 void execBatchCommands()
541 {
542         LAPPERR(singleton_);
543         singleton_->execCommands();
544 }
545
546
547 void LyX::execCommands()
548 {
549         // The advantage of doing this here is that the event loop
550         // is already started. So any need for interaction will be
551         // aknowledged.
552
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."),
560                         0, 2,
561                         _("&Reconfigure"),
562                         _("&Without LaTeX"),
563                         _("&Continue")))
564                 {
565                 case 0:
566                         // regular reconfigure
567                         lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
568                         break;
569                 case 1:
570                         // reconfigure --without-latex-config
571                         lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
572                                 " --without-latex-config"));
573                         break;
574                 default:
575                         break;
576                 }
577         }
578
579         // create the first main window
580         lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
581
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) {
586                         lyx::dispatch(
587                                 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
588                 }
589                 // clear this list to save a few bytes of RAM
590                 pimpl_->files_to_load_.clear();
591         } else
592                 pimpl_->application_->restoreGuiSession();
593
594         // Execute batch commands if available
595         if (pimpl_->batch_commands.empty())
596                 return;
597
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));
603         }
604 }
605
606
607 /*
608 Signals and Windows
609 ===================
610 The SIGHUP signal does not exist on Windows and does not need to be handled.
611
612 Windows handles SIGFPE and SIGSEGV signals as expected.
613
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.
617
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.
624
625 For more information see:
626
627 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
628 ...signals are mostly useless on Windows for a variety of reasons that are
629 Windows specific...
630
631 'UNIX Application Migration Guide, Chapter 9'
632 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
633
634 'How To Terminate an Application "Cleanly" in Win32'
635 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
636 */
637 extern "C" {
638
639 static void error_handler(int err_sig)
640 {
641         // Throw away any signals other than the first one received.
642         static sig_atomic_t handling_error = false;
643         if (handling_error)
644                 return;
645         handling_error = true;
646
647         // We have received a signal indicating a fatal error, so
648         // try and save the data ASAP.
649         emergencyCleanup();
650
651         // These lyxerr calls may or may not work:
652
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
658         // signal handlers.
659
660         // This shouldn't matter here, however, as we've already invoked
661         // emergencyCleanup.
662         docstring msg;
663         switch (err_sig) {
664 #ifdef SIGHUP
665         case SIGHUP:
666                 msg = _("SIGHUP signal caught!\nBye.");
667                 break;
668 #endif
669         case SIGFPE:
670                 msg = _("SIGFPE signal caught!\nBye.");
671                 break;
672         case SIGSEGV:
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.");
679                 break;
680         case SIGINT:
681         case SIGTERM:
682                 // no comments
683                 break;
684         }
685
686         if (!msg.empty()) {
687                 lyxerr << "\nlyx: " << msg << endl;
688                 // try to make a GUI message
689                 Alert::error(_("LyX crashed!"), msg, true);
690         }
691
692         // Deinstall the signal handlers
693 #ifdef SIGHUP
694         signal(SIGHUP, SIG_DFL);
695 #endif
696         signal(SIGINT, SIG_DFL);
697         signal(SIGFPE, SIG_DFL);
698         signal(SIGSEGV, SIG_DFL);
699         signal(SIGTERM, SIG_DFL);
700
701 #ifdef SIGHUP
702         if (err_sig == SIGSEGV ||
703                 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
704 #else
705         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
706 #endif
707 #ifdef _MSC_VER
708                 // with abort() it crashes again.
709                 exit(err_sig);
710 #else
711                 abort();
712 #endif
713         }
714
715         exit(0);
716 }
717
718 }
719
720
721 void LyX::printError(ErrorItem const & ei)
722 {
723         docstring tmp = _("LyX: ") + ei.error + char_type(':')
724                 + ei.description;
725         cerr << to_utf8(tmp) << endl;
726 }
727
728 #if defined (USE_MACOSX_PACKAGING)
729 namespace {
730         // Unexposed--extract an environment variable name from its NAME=VALUE
731         // representation
732         std::string varname(const char* line)
733         {
734                 size_t nameLen = strcspn(line, "=");
735                 if (nameLen == strlen(line)) {
736                         return std::string();
737                 } else {
738                         return std::string(line, nameLen);
739                 }
740         }
741 }
742
743 void cleanDuplicateEnvVars()
744 {
745         std::set<std::string> seen;
746         std::set<std::string> dupes;
747
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) {
752                         continue;
753                 }
754                 if (seen.find(name) != seen.end()) {
755                         dupes.insert(name);
756                 } else {
757                         seen.insert(name);
758                 }
759         }
760
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);
767                 if (val != NULL) {
768                         LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
769                         // unsetenv removes *all* instances of the variable from the environment
770                         unsetenv(name);
771
772                         // replace with the value from getenv (in practice appears to be the
773                         // first value in the list)
774                         setenv(name, val, 0);
775                 }
776         }
777 }
778 #endif
779
780
781 static void initTemplatePath()
782 {
783         FileName const package_template_path =
784                 FileName(addName(package().system_support().absFileName(), "templates"));
785
786         if (lyxrc.template_path.empty()) {
787                 lyxrc.template_path = package_template_path.absFileName();
788         }
789 #if defined (USE_MACOSX_PACKAGING)
790         FileName const user_template_path =
791                 FileName(addName(package().user_support().absFileName(), "templates"));
792
793         if (package_template_path != FileName(lyxrc.template_path) &&
794                 user_template_path != FileName(lyxrc.template_path))
795         {
796                 return;
797         }
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();
802         }
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();
809                         }
810                         return;
811                 }
812                 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
813         }
814         lyxrc.template_path = user_template_path.absFileName();
815 #endif
816 }
817
818
819 bool LyX::init()
820 {
821 #ifdef SIGHUP
822         signal(SIGHUP, error_handler);
823 #endif
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.
829
830 #if defined (USE_MACOSX_PACKAGING)
831         cleanDuplicateEnvVars();
832 #endif
833
834         lyxrc.tempdir_path = package().temp_dir().absFileName();
835         lyxrc.document_path = ".";
836
837         if (lyxrc.example_path.empty()) {
838                 lyxrc.example_path = addPath(package().system_support().absFileName(),
839                                               "examples");
840         }
841         initTemplatePath();
842
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!");
848
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
853                 // use it as well.
854                 string const user_dir = package().user_support().absFileName();
855                 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
856                                     << user_dir << '"');
857                 if (!setEnv(LYX_USERDIR_VER, user_dir))
858                         LYXERR(Debug::INIT, "\t... failed!");
859         }
860
861         //
862         // Read configuration files
863         //
864
865         // This one may have been distributed along with LyX.
866         if (!readRcFile("lyxrc.dist"))
867                 return false;
868
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());
875 #endif
876         if (!lyxrc.path_prefix.empty())
877                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
878
879         // Check that user LyX directory is ok.
880         {
881                 string const lock_file = package().getConfigureLockName();
882                 int fd = fileLock(lock_file.c_str());
883
884                 if (queryUserLyXDir(package().explicit_user_support())) {
885                         package().reconfigureUserLyXDir("");
886                         // Now the user directory is present on first start.
887                         initTemplatePath();
888                 }
889                 fileUnlock(fd, lock_file.c_str());
890         }
891
892         if (!use_gui) {
893                 // No need for a splash when there is no GUI
894                 first_start = false;
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");
899                         if (what == "all")
900                                 force_overwrite = ALL_FILES;
901                         else if (what == "none")
902                                 force_overwrite = NO_FILES;
903                         else
904                                 force_overwrite = MAIN_FILE;
905                 }
906         }
907
908         // This one is generated in user_support directory by lib/configure.py.
909         if (!readRcFile("lyxrc.defaults"))
910                 return false;
911
912         // Query the OS to know what formats are viewed natively
913         formats.setAutoOpen();
914
915         // Read lyxrc.dist again to be able to override viewer auto-detection.
916         readRcFile("lyxrc.dist");
917
918         system_lyxrc = lyxrc;
919         system_formats = formats;
920         pimpl_->system_converters_ = pimpl_->converters_;
921         pimpl_->system_movers_ = pimpl_->movers_;
922         system_lcolor = lcolor;
923
924         // This one is edited through the preferences dialog.
925         if (!readRcFile("preferences", true))
926                 return false;
927
928         // The language may have been set to someting useful through prefs
929         setLocale();
930
931         if (!readEncodingsFile("encodings", "unicodesymbols"))
932                 return false;
933         if (!readLanguagesFile("languages"))
934                 return false;
935
936         LYXERR(Debug::INIT, "Reading layouts...");
937         // Load the layouts
938         LayoutFileList::get().read();
939         //... the modules
940         theModuleList.read();
941         //... and the cite engines
942         theCiteEnginesList.read();
943
944         // read keymap and ui files in batch mode as well
945         // because InsetInfo needs to know these to produce
946         // the correct output
947
948         // Set up command definitions
949         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
950
951         // FIXME
952         // Set up bindings
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);
957
958         if (lyxerr.debugging(Debug::LYXRC))
959                 lyxrc.print();
960
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));
965
966         FileName const document_path(lyxrc.document_path);
967         if (document_path.exists() && document_path.isDirectory())
968                 package().document_dir() = document_path;
969
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"
974                                                        "\"%1$s\"\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.
982                 return false;
983         }
984
985         LYXERR(Debug::INIT, "LyX tmp dir: `"
986                             << package().temp_dir().absFileName() << '\'');
987
988         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
989         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
990
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();
994
995         return true;
996 }
997
998
999 void emergencyCleanup()
1000 {
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
1004         // a crash
1005
1006         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1007         if (use_gui) {
1008                 if (singleton_->pimpl_->lyx_server_)
1009                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
1010                 singleton_->pimpl_->lyx_server_.reset();
1011                 singleton_->pimpl_->lyx_socket_.reset();
1012         }
1013 }
1014
1015
1016 bool LyX::queryUserLyXDir(bool explicit_userdir)
1017 {
1018         // Does user directory exist?
1019         FileName const sup = package().user_support();
1020         if (sup.exists() && sup.isDirectory()) {
1021                 first_start = false;
1022
1023                 return configFileNeedsUpdate("lyxrc.defaults")
1024                         || configFileNeedsUpdate("lyxmodules.lst")
1025                         || configFileNeedsUpdate("textclass.lst")
1026                         || configFileNeedsUpdate("packages.lst")
1027                         || configFileNeedsUpdate("lyxciteengines.lst");
1028         }
1029
1030         first_start = !explicit_userdir;
1031
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 &&
1035             Alert::prompt(
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())),
1041                     1, 0,
1042                     _("&Create directory"),
1043                     _("&Exit LyX"))) {
1044                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1045                 earlyExit(EXIT_FAILURE);
1046         }
1047
1048         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1049                           from_utf8(sup.absFileName()))) << endl;
1050
1051         if (!sup.createDirectory(0755)) {
1052                 // Failed, so let's exit.
1053                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1054                        << endl;
1055                 earlyExit(EXIT_FAILURE);
1056         }
1057
1058         return true;
1059 }
1060
1061
1062 bool LyX::readRcFile(string const & name, bool check_format)
1063 {
1064         LYXERR(Debug::INIT, "About to read " << name << "... ");
1065
1066         FileName const lyxrc_path = libFileSearch(string(), name);
1067         if (lyxrc_path.empty()) {
1068                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1069                 // FIXME
1070                 // This was the previous logic, but can it be right??
1071                 return true;
1072         }
1073         LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1074         bool const success = lyxrc.read(lyxrc_path, check_format);
1075         if (!success)
1076                 showFileError(name);
1077         return success;
1078 }
1079
1080 // Read the languages file `name'
1081 bool LyX::readLanguagesFile(string const & name)
1082 {
1083         LYXERR(Debug::INIT, "About to read " << name << "...");
1084
1085         FileName const lang_path = libFileSearch(string(), name);
1086         if (lang_path.empty()) {
1087                 showFileError(name);
1088                 return false;
1089         }
1090         languages.read(lang_path);
1091         return true;
1092 }
1093
1094
1095 // Read the encodings file `name'
1096 bool LyX::readEncodingsFile(string const & enc_name,
1097                             string const & symbols_name)
1098 {
1099         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1100                             << symbols_name << "...");
1101
1102         FileName const symbols_path = libFileSearch(string(), symbols_name);
1103         if (symbols_path.empty()) {
1104                 showFileError(symbols_name);
1105                 return false;
1106         }
1107
1108         FileName const enc_path = libFileSearch(string(), enc_name);
1109         if (enc_path.empty()) {
1110                 showFileError(enc_name);
1111                 return false;
1112         }
1113         encodings.read(enc_path, symbols_path);
1114         return true;
1115 }
1116
1117
1118 namespace {
1119
1120 /// return the the number of arguments consumed
1121 typedef function<int(string const &, string const &, string &)> cmd_helper;
1122
1123 int parse_dbg(string const & arg, string const &, string &)
1124 {
1125         if (arg.empty()) {
1126                 cout << to_utf8(_("List of supported debug flags:")) << endl;
1127                 Debug::showTags(cout);
1128                 exit(0);
1129         }
1130         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1131
1132         lyxerr.setLevel(Debug::value(arg));
1133         Debug::showLevel(lyxerr, lyxerr.level());
1134         return 1;
1135 }
1136
1137
1138 int parse_help(string const &, string const &, string &)
1139 {
1140         cout <<
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"
1171                   "\t-r [--remote]\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;
1179         exit(0);
1180         return 0;
1181 }
1182
1183
1184 int parse_version(string const &, string const &, string &)
1185 {
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;
1192         exit(0);
1193         return 0;
1194 }
1195
1196
1197 int parse_sysdir(string const & arg, string const &, string &)
1198 {
1199         if (arg.empty()) {
1200                 Alert::error(_("No system directory"),
1201                         _("Missing directory for -sysdir switch"));
1202                 exit(1);
1203         }
1204         cl_system_support = arg;
1205         return 1;
1206 }
1207
1208
1209 int parse_userdir(string const & arg, string const &, string &)
1210 {
1211         if (arg.empty()) {
1212                 Alert::error(_("No user directory"),
1213                         _("Missing directory for -userdir switch"));
1214                 exit(1);
1215         }
1216         cl_user_support = arg;
1217         return 1;
1218 }
1219
1220
1221 int parse_execute(string const & arg, string const &, string & batch)
1222 {
1223         if (arg.empty()) {
1224                 Alert::error(_("Incomplete command"),
1225                         _("Missing command string after --execute switch"));
1226                 exit(1);
1227         }
1228         batch = arg;
1229         return 1;
1230 }
1231
1232
1233 int parse_export_to(string const & type, string const & output_file, string & batch)
1234 {
1235         if (type.empty()) {
1236                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1237                                          "--export-to switch")) << endl;
1238                 exit(1);
1239         }
1240         if (output_file.empty()) {
1241                 lyxerr << to_utf8(_("Missing destination filename after "
1242                                          "--export-to switch")) << endl;
1243                 exit(1);
1244         }
1245         batch = "buffer-export " + type + " " + output_file;
1246         use_gui = false;
1247         return 2;
1248 }
1249
1250
1251 int parse_export(string const & type, string const &, string & batch)
1252 {
1253         if (type.empty()) {
1254                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1255                                          "--export switch")) << endl;
1256                 exit(1);
1257         }
1258         batch = "buffer-export " + type;
1259         use_gui = false;
1260         return 1;
1261 }
1262
1263
1264 int parse_import(string const & type, string const & file, string & batch)
1265 {
1266         if (type.empty()) {
1267                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1268                                          "--import switch")) << endl;
1269                 exit(1);
1270         }
1271         if (file.empty()) {
1272                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1273                 exit(1);
1274         }
1275         batch = "buffer-import " + type + ' ' + file;
1276         return 2;
1277 }
1278
1279
1280 int parse_geometry(string const & arg1, string const &, string &)
1281 {
1282         geometryArg = arg1;
1283         // don't remove "-geometry", it will be pruned out later in the
1284         // frontend if need be.
1285         return -1;
1286 }
1287
1288
1289 int parse_batch(string const &, string const &, string &)
1290 {
1291         use_gui = false;
1292         return 0;
1293 }
1294
1295
1296 int parse_noremote(string const &, string const &, string &)
1297 {
1298         run_mode = NEW_INSTANCE;
1299         return 0;
1300 }
1301
1302
1303 int parse_remote(string const &, string const &, string &)
1304 {
1305         run_mode = USE_REMOTE;
1306         return 0;
1307 }
1308
1309
1310 int parse_verbose(string const &, string const &, string &)
1311 {
1312         verbose = true;
1313         return 0;
1314 }
1315
1316
1317 int parse_force(string const & arg, string const &, string &)
1318 {
1319         if (arg == "all") {
1320                 force_overwrite = ALL_FILES;
1321                 return 1;
1322         } else if (arg == "main") {
1323                 force_overwrite = MAIN_FILE;
1324                 return 1;
1325         } else if (arg == "none") {
1326                 force_overwrite = NO_FILES;
1327                 return 1;
1328         }
1329         force_overwrite = ALL_FILES;
1330         return 0;
1331 }
1332
1333
1334 } // namespace anon
1335
1336
1337 void LyX::easyParse(int & argc, char * argv[])
1338 {
1339         map<string, cmd_helper> cmdmap;
1340
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;
1366
1367         for (int i = 1; i < argc; ++i) {
1368                 map<string, cmd_helper>::const_iterator it
1369                         = cmdmap.find(argv[i]);
1370
1371                 // don't complain if not found - may be parsed later
1372                 if (it == cmdmap.end())
1373                         continue;
1374
1375                 string const arg =
1376                         (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1377                 string const arg2 =
1378                         (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1379
1380                 string batch;
1381                 int const remove = 1 + it->second(arg, arg2, batch);
1382                 if (!batch.empty())
1383                         pimpl_->batch_commands.push_back(batch);
1384
1385                 // Now, remove used arguments by shifting
1386                 // the following ones remove places down.
1387                 if (remove > 0) {
1388                         os::remove_internal_args(i, remove);
1389                         argc -= remove;
1390                         for (int j = i; j < argc; ++j)
1391                                 argv[j] = argv[j + remove];
1392                         --i;
1393                 }
1394         }
1395 }
1396
1397
1398 FuncStatus getStatus(FuncRequest const & action)
1399 {
1400         LAPPERR(theApp());
1401         return theApp()->getStatus(action);
1402 }
1403
1404
1405 DispatchResult const & dispatch(FuncRequest const & action)
1406 {
1407         LAPPERR(theApp());
1408         return theApp()->dispatch(action);
1409 }
1410
1411
1412 void dispatch(FuncRequest const & action, DispatchResult & dr)
1413 {
1414         LAPPERR(theApp());
1415         theApp()->dispatch(action, dr);
1416 }
1417
1418
1419 vector<string> & theFilesToLoad()
1420 {
1421         LAPPERR(singleton_);
1422         return singleton_->pimpl_->files_to_load_;
1423 }
1424
1425
1426 BufferList & theBufferList()
1427 {
1428         LAPPERR(singleton_);
1429         return singleton_->pimpl_->buffer_list_;
1430 }
1431
1432
1433 Server & theServer()
1434 {
1435         // FIXME: this should not be use_gui dependent
1436         LWARNIF(use_gui);
1437         LAPPERR(singleton_);
1438         return *singleton_->pimpl_->lyx_server_;
1439 }
1440
1441
1442 ServerSocket & theServerSocket()
1443 {
1444         // FIXME: this should not be use_gui dependent
1445         LWARNIF(use_gui);
1446         LAPPERR(singleton_);
1447         return *singleton_->pimpl_->lyx_socket_;
1448 }
1449
1450
1451 KeyMap & theTopLevelKeymap()
1452 {
1453         LAPPERR(singleton_);
1454         return singleton_->pimpl_->toplevel_keymap_;
1455 }
1456
1457
1458 Converters & theConverters()
1459 {
1460         LAPPERR(singleton_);
1461         return  singleton_->pimpl_->converters_;
1462 }
1463
1464
1465 Converters & theSystemConverters()
1466 {
1467         LAPPERR(singleton_);
1468         return  singleton_->pimpl_->system_converters_;
1469 }
1470
1471
1472 Movers & theMovers()
1473 {
1474         LAPPERR(singleton_);
1475         return singleton_->pimpl_->movers_;
1476 }
1477
1478
1479 Mover const & getMover(string  const & fmt)
1480 {
1481         LAPPERR(singleton_);
1482         return singleton_->pimpl_->movers_(fmt);
1483 }
1484
1485
1486 void setMover(string const & fmt, string const & command)
1487 {
1488         LAPPERR(singleton_);
1489         singleton_->pimpl_->movers_.set(fmt, command);
1490 }
1491
1492
1493 Movers & theSystemMovers()
1494 {
1495         LAPPERR(singleton_);
1496         return singleton_->pimpl_->system_movers_;
1497 }
1498
1499
1500 Messages const & getMessages(string const & language)
1501 {
1502         LAPPERR(singleton_);
1503         return singleton_->messages(language);
1504 }
1505
1506
1507 Messages const & getGuiMessages()
1508 {
1509         LAPPERR(singleton_);
1510         return singleton_->messages(Messages::guiLanguage());
1511 }
1512
1513
1514 Session & theSession()
1515 {
1516         LAPPERR(singleton_);
1517         return *singleton_->pimpl_->session_.get();
1518 }
1519
1520
1521 LaTeXFonts & theLaTeXFonts()
1522 {
1523         LAPPERR(singleton_);
1524         if (!singleton_->pimpl_->latexfonts_)
1525                 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1526         return *singleton_->pimpl_->latexfonts_;
1527 }
1528
1529
1530 CmdDef & theTopLevelCmdDef()
1531 {
1532         LAPPERR(singleton_);
1533         return singleton_->pimpl_->toplevel_cmddef_;
1534 }
1535
1536
1537 SpellChecker * theSpellChecker()
1538 {
1539         if (!singleton_->pimpl_->spell_checker_)
1540                 setSpellChecker();
1541         return singleton_->pimpl_->spell_checker_;
1542 }
1543
1544
1545 void setSpellChecker()
1546 {
1547         SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1548                 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1549
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_;
1555 #else
1556                 singleton_->pimpl_->spell_checker_ = 0;
1557 #endif
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_;
1563 #else
1564                 singleton_->pimpl_->spell_checker_ = 0;
1565 #endif
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_;
1571 #else
1572                 singleton_->pimpl_->spell_checker_ = 0;
1573 #endif
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_;
1579 #else
1580                 singleton_->pimpl_->spell_checker_ = 0;
1581 #endif
1582         } else {
1583                 singleton_->pimpl_->spell_checker_ = 0;
1584         }
1585         if (singleton_->pimpl_->spell_checker_) {
1586                 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1587                 singleton_->pimpl_->spell_checker_->advanceChangeNumber();
1588         }
1589 }
1590
1591 } // namespace lyx