]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
Use ASCII number in \char definition
[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/convert.h"
56 #include "support/lassert.h"
57 #include "support/debug.h"
58 #include "support/environment.h"
59 #include "support/ExceptionMessage.h"
60 #include "support/filetools.h"
61 #include "support/gettext.h"
62 #include "support/lstrings.h"
63 #include "support/Messages.h"
64 #include "support/os.h"
65 #include "support/Package.h"
66 #include "support/unique_ptr.h"
67
68 #include <algorithm>
69 #include <csignal>
70 #include <iostream>
71 #include <functional>
72 #include <map>
73 #include <stdlib.h>
74 #include <string>
75 #include <vector>
76
77 #include <qglobal.h> // For QT_VERSION
78
79 using namespace std;
80 using namespace lyx::support;
81
82 #if defined (USE_MACOSX_PACKAGING)
83 #include <crt_externs.h>
84 #endif
85
86 namespace lyx {
87
88 namespace Alert = frontend::Alert;
89 namespace os = support::os;
90
91
92
93 // Are we using the GUI at all?  We default to true and this is changed
94 // to false when the export feature is used.
95
96 bool use_gui = true;
97
98
99 // Report on the terminal about spawned commands. The default is false
100 // and can be changed with the option -v (--verbose).
101
102 bool verbose = false;
103
104
105 // Do not treat the "missing glyphs" warning of fontspec as an error message.
106 // The default is false and can be changed with the option
107 // --ignore-error-message missing_glyphs
108 // This is used in automated testing.
109 bool ignore_missing_glyphs = false;
110
111
112 // We default to open documents in an already running instance, provided that
113 // the lyxpipe has been setup. This can be overridden either on the command
114 // line or through preference settings.
115
116 RunMode run_mode = PREFERRED;
117
118
119 // Tell what files can be silently overwritten during batch export.
120 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
121 // Unless specified on command line (through the -f switch) or through the
122 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
123
124 OverwriteFiles force_overwrite = UNSPECIFIED;
125
126
127 // Scale the GUI by this factor. This works whether we have a HiDpi screen
128 // or not and scales everything, also fonts. Can only be changed by setting
129 // the QT_SCALE_FACTOR environment variable before launching LyX and only
130 // works properly with Qt 5.6 or higher.
131
132 double qt_scale_factor = 1.0;
133
134
135 namespace {
136
137 // Filled with the command line arguments "foo" of "-sysdir foo" or
138 // "-userdir foo".
139 string cl_system_support;
140 string cl_user_support;
141
142 string geometryArg;
143
144 LyX * singleton_ = 0;
145
146 void showFileError(string const & error)
147 {
148         Alert::warning(_("Could not read configuration file"),
149                        bformat(_("Error while reading the configuration file\n%1$s.\n"
150                            "Please check your installation."), from_utf8(error)));
151 }
152
153 } // namespace anon
154
155 /// The main application class private implementation.
156 struct LyX::Impl {
157         Impl()
158                 : latexfonts_(0), spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
159         {}
160
161         ~Impl()
162         {
163                 delete latexfonts_;
164                 delete apple_spell_checker_;
165                 delete aspell_checker_;
166                 delete enchant_checker_;
167                 delete hunspell_checker_;
168         }
169
170         ///
171         BufferList buffer_list_;
172         ///
173         KeyMap toplevel_keymap_;
174         ///
175         CmdDef toplevel_cmddef_;
176         ///
177         unique_ptr<Server> lyx_server_;
178         ///
179         unique_ptr<ServerSocket> lyx_socket_;
180         ///
181         unique_ptr<frontend::Application> application_;
182         /// lyx session, containing lastfiles, lastfilepos, and lastopened
183         unique_ptr<Session> session_;
184
185         /// Files to load at start.
186         vector<string> files_to_load_;
187
188         /// The messages translators.
189         map<string, Messages> messages_;
190
191         /// The file converters.
192         Converters converters_;
193
194         // The system converters copy after reading lyxrc.defaults.
195         Converters system_converters_;
196
197         ///
198         Movers movers_;
199         ///
200         Movers system_movers_;
201
202         /// the parsed command line batch command if any
203         vector<string> batch_commands;
204
205         ///
206         LaTeXFonts * latexfonts_;
207
208         ///
209         SpellChecker * spell_checker_;
210         ///
211         SpellChecker * apple_spell_checker_;
212         ///
213         SpellChecker * aspell_checker_;
214         ///
215         SpellChecker * enchant_checker_;
216         ///
217         SpellChecker * hunspell_checker_;
218 };
219
220
221 /// The main application class for console mode
222 class LyXConsoleApp : public ConsoleApplication
223 {
224 public:
225         LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
226                 : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
227                   argc_(argc), argv_(argv)
228         {
229         }
230         void doExec()
231         {
232                 int const exit_status = lyx_->execWithoutGui(argc_, argv_);
233                 exit(exit_status);
234         }
235 private:
236         LyX * lyx_;
237         int & argc_;
238         char ** argv_;
239 };
240
241
242 ///
243 frontend::Application * theApp()
244 {
245         if (singleton_)
246                 return singleton_->pimpl_->application_.get();
247         else
248                 return 0;
249 }
250
251
252 LyX::~LyX()
253 {
254         delete pimpl_;
255         singleton_ = 0;
256 }
257
258
259 void lyx_exit(int exit_code)
260 {
261         if (exit_code)
262                 // Something wrong happened so better save everything, just in
263                 // case.
264                 emergencyCleanup();
265
266 #ifndef NDEBUG
267         // Properly crash in debug mode in order to get a useful backtrace.
268         abort();
269 #endif
270
271         // In release mode, try to exit gracefully.
272         if (theApp())
273                 theApp()->exit(exit_code);
274         else
275                 exit(exit_code);
276 }
277
278
279 LyX::LyX()
280         : first_start(false)
281 {
282         singleton_ = this;
283         pimpl_ = new Impl;
284 }
285
286
287 Messages & LyX::messages(string const & language)
288 {
289         map<string, Messages>::iterator it = pimpl_->messages_.find(language);
290
291         if (it != pimpl_->messages_.end())
292                 return it->second;
293
294         pair<map<string, Messages>::iterator, bool> result =
295                         pimpl_->messages_.insert(make_pair(language, Messages(language)));
296
297         LATTEST(result.second);
298         return result.first->second;
299 }
300
301
302 int LyX::exec(int & argc, char * argv[])
303 {
304         // Minimal setting of locale before parsing command line
305         try {
306                 init_package(os::utf8_argv(0), string(), string());
307                 // we do not get to this point when init_package throws an exception
308                 setLocale();
309         } catch (ExceptionMessage const & message) {
310                 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
311         }
312
313         // Here we need to parse the command line. At least
314         // we need to parse for "-dbg" and "-help"
315         easyParse(argc, argv);
316
317 #if QT_VERSION >= 0x050600
318         // Check whether Qt will scale all GUI elements and accordingly
319         // set the scale factor so that to avoid blurred images and text
320         char const * const scale_factor = getenv("QT_SCALE_FACTOR");
321         if (scale_factor) {
322                 qt_scale_factor = convert<double>(scale_factor);
323                 if (qt_scale_factor < 1.0)
324                         qt_scale_factor = 1.0;
325         }
326 #endif
327
328         try {
329                 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
330         } catch (ExceptionMessage const & message) {
331                 if (message.type_ == ErrorException) {
332                         Alert::error(message.title_, message.details_);
333                         lyx_exit(1);
334                 } else if (message.type_ == WarningException) {
335                         Alert::warning(message.title_, message.details_);
336                 }
337         }
338
339         // Reinit the messages machinery in case package() knows
340         // something interesting about the locale directory.
341         setLocale();
342
343         if (!use_gui) {
344                 LyXConsoleApp app(this, argc, argv);
345
346                 // Reestablish our defaults, as Qt overwrites them
347                 // after creating app
348                 setLocale();//???
349
350                 return app.exec();
351         }
352
353         // Let the frontend parse and remove all arguments that it knows
354         pimpl_->application_.reset(createApplication(argc, argv));
355
356         // Reestablish our defaults, as Qt overwrites them
357         // after createApplication()
358         setLocale();//???
359
360         // Parse and remove all known arguments in the LyX singleton
361         // Give an error for all remaining ones.
362         int exit_status = init(argc, argv);
363         if (exit_status) {
364                 // Kill the application object before exiting.
365                 pimpl_->application_.reset();
366                 use_gui = false;
367                 prepareExit();
368                 return exit_status;
369         }
370
371         // If not otherwise specified by a command line option or
372         // by preferences, we default to reuse a running instance.
373         if (run_mode == PREFERRED)
374                 run_mode = USE_REMOTE;
375
376         // FIXME
377         /* Create a CoreApplication class that will provide the main event loop
378         * and the socket callback registering. With Qt, only QtCore
379         * library would be needed.
380         * When this is done, a server_mode could be created and the following two
381         * line would be moved out from here.
382         * However, note that the first of the two lines below triggers the
383         * "single instance" behavior, which should occur right at this point.
384         */
385         // Note: socket callback must be registered after init(argc, argv)
386         // such that package().temp_dir() is properly initialized.
387         pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
388         pimpl_->lyx_socket_.reset(new ServerSocket(
389                         FileName(package().temp_dir().absFileName() + "/lyxsocket")));
390
391         // Start the real execution loop.
392         if (!theServer().deferredLoadingToOtherInstance())
393                 exit_status = pimpl_->application_->exec();
394         else if (!pimpl_->files_to_load_.empty()) {
395                 vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
396                 vector<string>::const_iterator end = pimpl_->files_to_load_.end();
397                 lyxerr << _("The following files could not be loaded:") << endl;
398                 for (; it != end; ++it)
399                         lyxerr << *it << endl;
400         }
401
402         prepareExit();
403
404         return exit_status;
405 }
406
407
408 void LyX::prepareExit()
409 {
410         // Clear the clipboard and selection stack:
411         cap::clearCutStack();
412         cap::clearSelection();
413
414         // Write the index file of the converter cache
415         ConverterCache::get().writeIndex();
416
417         // close buffers first
418         pimpl_->buffer_list_.closeAll();
419
420         // register session changes and shutdown server and socket
421         if (use_gui) {
422                 if (pimpl_->session_)
423                         pimpl_->session_->writeFile();
424                 pimpl_->session_.reset();
425                 pimpl_->lyx_server_.reset();
426                 pimpl_->lyx_socket_.reset();
427         }
428
429         // do any other cleanup procedures now
430         if (package().temp_dir() != package().system_temp_dir()) {
431                 string const abs_tmpdir = package().temp_dir().absFileName();
432                 if (!contains(package().temp_dir().absFileName(), "lyx_tmpdir")) {
433                         docstring const msg =
434                                 bformat(_("%1$s does not appear like a LyX created temporary directory."),
435                                 from_utf8(abs_tmpdir));
436                         Alert::warning(_("Cannot remove temporary directory"), msg);
437                 } else {
438                         LYXERR(Debug::INFO, "Deleting tmp dir "
439                                 << package().temp_dir().absFileName());
440                         if (!package().temp_dir().destroyDirectory()) {
441                                 LYXERR0(bformat(_("Unable to remove the temporary directory %1$s"),
442                                         from_utf8(package().temp_dir().absFileName())));
443                         }
444                 }
445         }
446
447         // Kill the application object before exiting. This avoids crashes
448         // when exiting on Linux.
449         pimpl_->application_.reset();
450 }
451
452
453 void LyX::earlyExit(int status)
454 {
455         LATTEST(pimpl_->application_.get());
456         // LyX::pimpl_::application_ is not initialised at this
457         // point so it's safe to just exit after some cleanup.
458         prepareExit();
459         exit(status);
460 }
461
462
463 int LyX::init(int & argc, char * argv[])
464 {
465         // check for any spurious extra arguments
466         // other than documents
467         for (int argi = 1; argi < argc ; ++argi) {
468                 if (argv[argi][0] == '-') {
469                         lyxerr << to_utf8(
470                                 bformat(_("Wrong command line option `%1$s'. Exiting."),
471                                 from_utf8(os::utf8_argv(argi)))) << endl;
472                         return EXIT_FAILURE;
473                 }
474         }
475
476         // Initialization of LyX (reads lyxrc and more)
477         LYXERR(Debug::INIT, "Initializing LyX::init...");
478         bool success = init();
479         LYXERR(Debug::INIT, "Initializing LyX::init...done");
480         if (!success)
481                 return EXIT_FAILURE;
482
483         // Remaining arguments are assumed to be files to load.
484         for (int argi = 1; argi < argc; ++argi)
485                 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
486
487         if (!use_gui && pimpl_->files_to_load_.empty()) {
488                 lyxerr << to_utf8(_("Missing filename for this operation.")) << endl;
489                 return EXIT_FAILURE;
490         }
491
492         if (first_start) {
493                 pimpl_->files_to_load_.push_back(
494                         i18nLibFileSearch("examples", "splash.lyx").absFileName());
495         }
496
497         return EXIT_SUCCESS;
498 }
499
500
501 int LyX::execWithoutGui(int & argc, char * argv[])
502 {
503         int exit_status = init(argc, argv);
504         if (exit_status) {
505                 prepareExit();
506                 return exit_status;
507         }
508
509         // Used to keep track of which buffers were explicitly loaded by user request.
510         // This is necessary because master and child document buffers are loaded, even 
511         // if they were not named on the command line. We do not want to dispatch to
512         // those.
513         vector<Buffer *> command_line_buffers;
514
515         // Load the files specified on the command line
516         vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
517         vector<string>::const_iterator end = pimpl_->files_to_load_.end();
518         for (; it != end; ++it) {
519                 // get absolute path of file and add ".lyx" to the filename if necessary
520                 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
521                                                                                                                                 may_not_exist);
522
523                 if (fname.empty())
524                         continue;
525
526                 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName());
527                 LYXERR(Debug::FILES, "Loading " << fname);
528                 if (buf && buf->loadLyXFile() == Buffer::ReadSuccess) {
529                         ErrorList const & el = buf->errorList("Parse");
530                         for(ErrorItem const & e : el)
531                                 printError(e);
532                         command_line_buffers.push_back(buf);
533                 } else {
534                         if (buf)
535                                 pimpl_->buffer_list_.release(buf);
536                         docstring const error_message =
537                                         bformat(_("LyX failed to load the following file: %1$s"),
538                                                                         from_utf8(fname.absFileName()));
539                         lyxerr << to_utf8(error_message) << endl;
540                         exit_status = 1; // failed
541                 }
542         }
543
544         if (exit_status || pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
545                 prepareExit();
546                 return exit_status;
547         }
548
549         // Iterate through the buffers that were specified on the command line
550         bool final_success = false;
551         vector<Buffer *>::iterator buf_it = command_line_buffers.begin();
552         for (; buf_it != command_line_buffers.end(); ++buf_it) {
553                 Buffer * buf = *buf_it;
554                 vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
555                 vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
556                 DispatchResult dr;
557                 for (; bcit != bcend; ++bcit) {
558                         LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
559                         buf->dispatch(*bcit, dr);
560                         final_success |= !dr.error();
561                 }
562         }
563         prepareExit();
564         return !final_success;
565 }
566
567
568 void execBatchCommands()
569 {
570         LAPPERR(singleton_);
571         singleton_->execCommands();
572 }
573
574
575 void LyX::execCommands()
576 {
577         // The advantage of doing this here is that the event loop
578         // is already started. So any need for interaction will be
579         // aknowledged.
580
581         // if reconfiguration is needed.
582         if (LayoutFileList::get().empty()) {
583                 switch (Alert::prompt(
584                         _("No textclass is found"),
585                         _("LyX will only have minimal functionality because no textclasses "
586                                 "have been found. You can either try to reconfigure LyX normally, "
587                                 "try to reconfigure without checking your LaTeX installation, or continue."),
588                         0, 2,
589                         _("&Reconfigure"),
590                         _("&Without LaTeX"),
591                         _("&Continue")))
592                 {
593                 case 0:
594                         // regular reconfigure
595                         lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
596                         break;
597                 case 1:
598                         // reconfigure --without-latex-config
599                         lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
600                                 " --without-latex-config"));
601                         break;
602                 default:
603                         break;
604                 }
605         }
606
607         // create the first main window
608         lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
609
610         if (!pimpl_->files_to_load_.empty()) {
611                 // if some files were specified at command-line we assume that the
612                 // user wants to edit *these* files and not to restore the session.
613                 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
614                         lyx::dispatch(
615                                 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
616                 }
617                 // clear this list to save a few bytes of RAM
618                 pimpl_->files_to_load_.clear();
619         } else
620                 pimpl_->application_->restoreGuiSession();
621
622         // Execute batch commands if available
623         if (pimpl_->batch_commands.empty())
624                 return;
625
626         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
627         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
628         for (; bcit != bcend; ++bcit) {
629                 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
630                 lyx::dispatch(lyxaction.lookupFunc(*bcit));
631         }
632 }
633
634
635 /*
636 Signals and Windows
637 ===================
638 The SIGHUP signal does not exist on Windows and does not need to be handled.
639
640 Windows handles SIGFPE and SIGSEGV signals as expected.
641
642 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
643 cause a new thread to be spawned. This may well result in unexpected
644 behaviour by the single-threaded LyX.
645
646 SIGTERM signals will come only from another process actually sending
647 that signal using 'raise' in Windows' POSIX compatability layer. It will
648 not come from the general "terminate process" methods that everyone
649 actually uses (and which can't be trapped). Killing an app 'politely' on
650 Windows involves first sending a WM_CLOSE message, something that is
651 caught already by the Qt frontend.
652
653 For more information see:
654
655 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
656 ...signals are mostly useless on Windows for a variety of reasons that are
657 Windows specific...
658
659 'UNIX Application Migration Guide, Chapter 9'
660 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
661
662 'How To Terminate an Application "Cleanly" in Win32'
663 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
664 */
665 extern "C" {
666
667 static void error_handler(int err_sig)
668 {
669         // Throw away any signals other than the first one received.
670         static sig_atomic_t handling_error = false;
671         if (handling_error)
672                 return;
673         handling_error = true;
674
675         // We have received a signal indicating a fatal error, so
676         // try and save the data ASAP.
677         emergencyCleanup();
678
679         // These lyxerr calls may or may not work:
680
681         // Signals are asynchronous, so the main program may be in a very
682         // fragile state when a signal is processed and thus while a signal
683         // handler function executes.
684         // In general, therefore, we should avoid performing any
685         // I/O operations or calling most library and system functions from
686         // signal handlers.
687
688         // This shouldn't matter here, however, as we've already invoked
689         // emergencyCleanup.
690         docstring msg;
691         switch (err_sig) {
692 #ifdef SIGHUP
693         case SIGHUP:
694                 msg = _("SIGHUP signal caught!\nBye.");
695                 break;
696 #endif
697         case SIGFPE:
698                 msg = _("SIGFPE signal caught!\nBye.");
699                 break;
700         case SIGSEGV:
701                 msg = _("SIGSEGV signal caught!\n"
702                           "Sorry, you have found a bug in LyX, "
703                           "hope you have not lost any data.\n"
704                           "Please read the bug-reporting instructions "
705                           "in 'Help->Introduction' and send us a bug report, "
706                           "if necessary. Thanks!\nBye.");
707                 break;
708         case SIGINT:
709         case SIGTERM:
710                 // no comments
711                 break;
712         }
713
714         if (!msg.empty()) {
715                 lyxerr << "\nlyx: " << msg << endl;
716                 // try to make a GUI message
717                 Alert::error(_("LyX crashed!"), msg, true);
718         }
719
720         // Deinstall the signal handlers
721 #ifdef SIGHUP
722         signal(SIGHUP, SIG_DFL);
723 #endif
724         signal(SIGINT, SIG_DFL);
725         signal(SIGFPE, SIG_DFL);
726         signal(SIGSEGV, SIG_DFL);
727         signal(SIGTERM, SIG_DFL);
728
729 #ifdef SIGHUP
730         if (err_sig == SIGSEGV ||
731                 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
732 #else
733         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
734 #endif
735 #ifdef _MSC_VER
736                 // with abort() it crashes again.
737                 exit(err_sig);
738 #else
739                 abort();
740 #endif
741         }
742
743         exit(0);
744 }
745
746 }
747
748
749 void LyX::printError(ErrorItem const & ei)
750 {
751         docstring tmp = _("LyX: ") + ei.error + char_type(':')
752                 + ei.description;
753         cerr << to_utf8(tmp) << endl;
754 }
755
756 #if defined (USE_MACOSX_PACKAGING)
757 namespace {
758         // Unexposed--extract an environment variable name from its NAME=VALUE
759         // representation
760         std::string varname(const char* line)
761         {
762                 size_t nameLen = strcspn(line, "=");
763                 if (nameLen == strlen(line)) {
764                         return std::string();
765                 } else {
766                         return std::string(line, nameLen);
767                 }
768         }
769 }
770
771 void cleanDuplicateEnvVars()
772 {
773         std::set<std::string> seen;
774         std::set<std::string> dupes;
775
776         // Create a list of the environment variables that appear more than once
777         for (char **read = *_NSGetEnviron(); *read; read++) {
778                 std::string name = varname(*read);
779                 if (name.size() == 0) {
780                         continue;
781                 }
782                 if (seen.find(name) != seen.end()) {
783                         dupes.insert(name);
784                 } else {
785                         seen.insert(name);
786                 }
787         }
788
789         // Loop over the list of duplicated variables
790         std::set<std::string>::iterator dupe = dupes.begin();
791         std::set<std::string>::iterator const dend = dupes.end();
792         for (; dupe != dend; ++dupe) {
793                 const char *name = (*dupe).c_str();
794                 char *val = getenv(name);
795                 if (val != NULL) {
796                         LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
797                         // unsetenv removes *all* instances of the variable from the environment
798                         unsetenv(name);
799
800                         // replace with the value from getenv (in practice appears to be the
801                         // first value in the list)
802                         setenv(name, val, 0);
803                 }
804         }
805 }
806 #endif
807
808
809 static void initTemplatePath()
810 {
811         FileName const package_template_path =
812                 FileName(addName(package().system_support().absFileName(), "templates"));
813
814         if (lyxrc.template_path.empty()) {
815                 lyxrc.template_path = package_template_path.absFileName();
816         }
817 #if defined (USE_MACOSX_PACKAGING)
818         FileName const user_template_path =
819                 FileName(addName(package().user_support().absFileName(), "templates"));
820
821         if (package_template_path != FileName(lyxrc.template_path) &&
822                 user_template_path != FileName(lyxrc.template_path))
823         {
824                 return;
825         }
826         FileName const user_template_link =
827                 FileName(addName(user_template_path.absFileName(),"SystemTemplates"));
828         if (user_template_link.isSymLink() && !equivalent(user_template_link, package_template_path)) {
829                 user_template_link.removeFile();
830         }
831         if (!user_template_link.exists()) {
832                 if (!package_template_path.link(user_template_link)) {
833                         FileName const user_support = package().user_support();
834                         if (user_support.exists() && user_support.isDirectory()) {
835                                 LYXERR(Debug::INIT, "Cannot create symlink " + user_template_link.absFileName());
836                                 lyxrc.template_path = package_template_path.absFileName();
837                         }
838                         return;
839                 }
840                 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
841         }
842         lyxrc.template_path = user_template_path.absFileName();
843 #endif
844 }
845
846
847 bool LyX::init()
848 {
849 #ifdef SIGHUP
850         signal(SIGHUP, error_handler);
851 #endif
852         signal(SIGFPE, error_handler);
853         signal(SIGSEGV, error_handler);
854         signal(SIGINT, error_handler);
855         signal(SIGTERM, error_handler);
856         // SIGPIPE can be safely ignored.
857
858 #if defined (USE_MACOSX_PACKAGING)
859         cleanDuplicateEnvVars();
860 #endif
861
862         lyxrc.tempdir_path = package().temp_dir().absFileName();
863         lyxrc.document_path = ".";
864
865         if (lyxrc.example_path.empty()) {
866                 lyxrc.example_path = addPath(package().system_support().absFileName(),
867                                               "examples");
868         }
869         initTemplatePath();
870
871         // init LyXDir environment variable
872         string const lyx_dir = package().lyx_dir().absFileName();
873         LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
874         if (!setEnv("LyXDir", lyx_dir))
875                 LYXERR(Debug::INIT, "\t... failed!");
876
877         if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
878                 // -userdir was given on the command line.
879                 // Make it available to child processes, otherwise tex2lyx
880                 // would not find all layout files, and other converters might
881                 // use it as well.
882                 string const user_dir = package().user_support().absFileName();
883                 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
884                                     << user_dir << '"');
885                 if (!setEnv(LYX_USERDIR_VER, user_dir))
886                         LYXERR(Debug::INIT, "\t... failed!");
887         }
888
889         //
890         // Read configuration files
891         //
892
893         // This one may have been distributed along with LyX.
894         if (!readRcFile("lyxrc.dist"))
895                 return false;
896
897         // Set the PATH correctly.
898 #if !defined (USE_POSIX_PACKAGING) && !defined (USE_HAIKU_PACKAGING)
899         // Add the directory containing the LyX executable to the path
900         // so that LyX can find things like tex2lyx.
901         if (package().build_support().empty())
902                 prependEnvPath("PATH", package().binary_dir().absFileName());
903 #endif
904         if (!lyxrc.path_prefix.empty())
905                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
906
907         // Check that user LyX directory is ok.
908         {
909                 string const lock_file = package().getConfigureLockName();
910                 int fd = fileLock(lock_file.c_str());
911
912                 if (queryUserLyXDir(package().explicit_user_support())) {
913                         package().reconfigureUserLyXDir("");
914                         // Now the user directory is present on first start.
915                         initTemplatePath();
916                 }
917                 fileUnlock(fd, lock_file.c_str());
918         }
919
920         if (!use_gui) {
921                 // No need for a splash when there is no GUI
922                 first_start = false;
923                 // Default is to overwrite the main file during export, unless
924                 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
925                 if (force_overwrite == UNSPECIFIED) {
926                         string const what = getEnv("LYX_FORCE_OVERWRITE");
927                         if (what == "all")
928                                 force_overwrite = ALL_FILES;
929                         else if (what == "none")
930                                 force_overwrite = NO_FILES;
931                         else
932                                 force_overwrite = MAIN_FILE;
933                 }
934         }
935
936         // This one is generated in user_support directory by lib/configure.py.
937         if (!readRcFile("lyxrc.defaults"))
938                 return false;
939
940         // Query the OS to know what formats are viewed natively
941         formats.setAutoOpen();
942
943         // Read lyxrc.dist again to be able to override viewer auto-detection.
944         readRcFile("lyxrc.dist");
945
946         system_lyxrc = lyxrc;
947         system_formats = formats;
948         pimpl_->system_converters_ = pimpl_->converters_;
949         pimpl_->system_movers_ = pimpl_->movers_;
950         system_lcolor = lcolor;
951
952         // This one is edited through the preferences dialog.
953         if (!readRcFile("preferences", true))
954                 return false;
955
956         // The language may have been set to someting useful through prefs
957         setLocale();
958
959         if (!readEncodingsFile("encodings", "unicodesymbols"))
960                 return false;
961         if (!readLanguagesFile("languages"))
962                 return false;
963
964         LYXERR(Debug::INIT, "Reading layouts...");
965         // Load the layouts
966         LayoutFileList::get().read();
967         //... the modules
968         theModuleList.read();
969         //... and the cite engines
970         theCiteEnginesList.read();
971
972         // read keymap and ui files in batch mode as well
973         // because InsetInfo needs to know these to produce
974         // the correct output
975
976         // Set up command definitions
977         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
978
979         // FIXME
980         // Set up bindings
981         pimpl_->toplevel_keymap_.read("site");
982         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
983         // load user bind file user.bind
984         pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
985
986         if (lyxerr.debugging(Debug::LYXRC))
987                 lyxrc.print();
988
989         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
990         // Prepend path prefix a second time to take the user preferences into a account
991         if (!lyxrc.path_prefix.empty())
992                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
993
994         FileName const document_path(lyxrc.document_path);
995         if (document_path.exists() && document_path.isDirectory())
996                 package().document_dir() = document_path;
997
998         package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
999         if (package().temp_dir().empty()) {
1000                 Alert::error(_("Could not create temporary directory"),
1001                              bformat(_("Could not create a temporary directory in\n"
1002                                                        "\"%1$s\"\n"
1003                                                            "Make sure that this path exists and is writable and try again."),
1004                                      from_utf8(lyxrc.tempdir_path)));
1005                 // createLyXTmpDir() tries sufficiently hard to create a
1006                 // usable temp dir, so the probability to come here is
1007                 // close to zero. We therefore don't try to overcome this
1008                 // problem with e.g. asking the user for a new path and
1009                 // trying again but simply exit.
1010                 return false;
1011         }
1012
1013         LYXERR(Debug::INIT, "LyX tmp dir: `"
1014                             << package().temp_dir().absFileName() << '\'');
1015
1016         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
1017         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1018
1019         // This must happen after package initialization and after lyxrc is
1020         // read, therefore it can't be done by a static object.
1021         ConverterCache::init();
1022
1023         return true;
1024 }
1025
1026
1027 void emergencyCleanup()
1028 {
1029         // what to do about tmpfiles is non-obvious. we would
1030         // like to delete any we find, but our lyxdir might
1031         // contain documents etc. which might be helpful on
1032         // a crash
1033
1034         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1035         if (use_gui) {
1036                 if (singleton_->pimpl_->lyx_server_)
1037                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
1038                 singleton_->pimpl_->lyx_server_.reset();
1039                 singleton_->pimpl_->lyx_socket_.reset();
1040         }
1041 }
1042
1043
1044 bool LyX::queryUserLyXDir(bool explicit_userdir)
1045 {
1046         // Does user directory exist?
1047         FileName const sup = package().user_support();
1048         if (sup.exists() && sup.isDirectory()) {
1049                 first_start = false;
1050
1051                 return configFileNeedsUpdate("lyxrc.defaults")
1052                         || configFileNeedsUpdate("lyxmodules.lst")
1053                         || configFileNeedsUpdate("textclass.lst")
1054                         || configFileNeedsUpdate("packages.lst")
1055                         || configFileNeedsUpdate("lyxciteengines.lst");
1056         }
1057
1058         first_start = !explicit_userdir;
1059
1060         // If the user specified explicitly a directory, ask whether
1061         // to create it. If the user says "no", then exit.
1062         if (explicit_userdir &&
1063             Alert::prompt(
1064                     _("Missing user LyX directory"),
1065                     bformat(_("You have specified a non-existent user "
1066                                            "LyX directory, %1$s.\n"
1067                                            "It is needed to keep your own configuration."),
1068                             from_utf8(package().user_support().absFileName())),
1069                     1, 0,
1070                     _("&Create directory"),
1071                     _("&Exit LyX"))) {
1072                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1073                 earlyExit(EXIT_FAILURE);
1074         }
1075
1076         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1077                           from_utf8(sup.absFileName()))) << endl;
1078
1079         if (!sup.createDirectory(0755)) {
1080                 // Failed, so let's exit.
1081                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
1082                        << endl;
1083                 earlyExit(EXIT_FAILURE);
1084         }
1085
1086         return true;
1087 }
1088
1089
1090 bool LyX::readRcFile(string const & name, bool check_format)
1091 {
1092         LYXERR(Debug::INIT, "About to read " << name << "... ");
1093
1094         FileName const lyxrc_path = libFileSearch(string(), name);
1095         if (lyxrc_path.empty()) {
1096                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1097                 // FIXME
1098                 // This was the previous logic, but can it be right??
1099                 return true;
1100         }
1101         LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1102         bool const success = lyxrc.read(lyxrc_path, check_format);
1103         if (!success)
1104                 showFileError(name);
1105         return success;
1106 }
1107
1108 // Read the languages file `name'
1109 bool LyX::readLanguagesFile(string const & name)
1110 {
1111         LYXERR(Debug::INIT, "About to read " << name << "...");
1112
1113         FileName const lang_path = libFileSearch(string(), name);
1114         if (lang_path.empty()) {
1115                 showFileError(name);
1116                 return false;
1117         }
1118         languages.read(lang_path);
1119         return true;
1120 }
1121
1122
1123 // Read the encodings file `name'
1124 bool LyX::readEncodingsFile(string const & enc_name,
1125                             string const & symbols_name)
1126 {
1127         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1128                             << symbols_name << "...");
1129
1130         FileName const symbols_path = libFileSearch(string(), symbols_name);
1131         if (symbols_path.empty()) {
1132                 showFileError(symbols_name);
1133                 return false;
1134         }
1135
1136         FileName const enc_path = libFileSearch(string(), enc_name);
1137         if (enc_path.empty()) {
1138                 showFileError(enc_name);
1139                 return false;
1140         }
1141         encodings.read(enc_path, symbols_path);
1142         return true;
1143 }
1144
1145
1146 namespace {
1147
1148 /// return the the number of arguments consumed
1149 typedef function<int(string const &, string const &, string &)> cmd_helper;
1150
1151 int parse_dbg(string const & arg, string const &, string &)
1152 {
1153         if (arg.empty()) {
1154                 cout << to_utf8(_("List of supported debug flags:")) << endl;
1155                 Debug::showTags(cout);
1156                 exit(0);
1157         }
1158         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1159
1160         lyxerr.setLevel(Debug::value(arg));
1161         Debug::showLevel(lyxerr, lyxerr.level());
1162         return 1;
1163 }
1164
1165
1166 int parse_help(string const &, string const &, string &)
1167 {
1168         cout <<
1169                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1170                   "Command line switches (case sensitive):\n"
1171                   "\t-help              summarize LyX usage\n"
1172                   "\t-userdir dir       set user directory to dir\n"
1173                   "\t-sysdir dir        set system directory to dir\n"
1174                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1175                   "\t-dbg feature[,feature]...\n"
1176                   "                  select the features to debug.\n"
1177                   "                  Type `lyx -dbg' to see the list of features\n"
1178                   "\t-x [--execute] command\n"
1179                   "                  where command is a lyx command.\n"
1180                   "\t-e [--export] fmt\n"
1181                   "                  where fmt is the export format of choice. Look in\n"
1182                   "                  Tools->Preferences->File Handling->File Formats->Short Name\n"
1183                   "                  to see which parameter (which differs from the format name\n"
1184                   "                  in the File->Export menu) should be passed.\n"
1185                   "                  Note that the order of -e and -x switches matters.\n"
1186                   "\t-E [--export-to] fmt filename\n"
1187                   "                  where fmt is the export format of choice (see --export),\n"
1188                   "                  and filename is the destination filename.\n"
1189                   "\t-i [--import] fmt file.xxx\n"
1190                   "                  where fmt is the import format of choice\n"
1191                   "                  and file.xxx is the file to be imported.\n"
1192                   "\t-f [--force-overwrite] what\n"
1193                   "                  where what is either `all', `main' or `none',\n"
1194                   "                  specifying whether all files, main file only, or no files,\n"
1195                   "                  respectively, are to be overwritten during a batch export.\n"
1196                   "                  Anything else is equivalent to `all', but is not consumed.\n"
1197                   "\t--ignore-error-message which\n"
1198                   "                  allows you to ignore specific LaTeX error messages.\n"
1199                   "                  Do not use for final documents! Currently supported values:\n"
1200                   "                  * missing_glyphs: Fontspec `missing glyphs' error.\n"
1201                   "\t-n [--no-remote]\n"
1202                   "                  open documents in a new instance\n"
1203                   "\t-r [--remote]\n"
1204                   "                  open documents in an already running instance\n"
1205                   "                  (a working lyxpipe is needed)\n"
1206                   "\t-v [--verbose]\n"
1207                   "                  report on terminal about spawned commands.\n"
1208                   "\t-batch    execute commands without launching GUI and exit.\n"
1209                   "\t-version  summarize version and build info\n"
1210                                "Check the LyX man page for more details.")) << endl;
1211         exit(0);
1212         return 0;
1213 }
1214
1215
1216 int parse_version(string const &, string const &, string &)
1217 {
1218         cout << "LyX " << lyx_version
1219                << " (" << lyx_release_date << ")" << endl;
1220         if (string(lyx_git_commit_hash) != "none")
1221                 cout << to_utf8(_("  Git commit hash "))
1222                      << string(lyx_git_commit_hash).substr(0,8) << endl;
1223         cout << lyx_version_info << endl;
1224         exit(0);
1225         return 0;
1226 }
1227
1228
1229 int parse_sysdir(string const & arg, string const &, string &)
1230 {
1231         if (arg.empty()) {
1232                 Alert::error(_("No system directory"),
1233                         _("Missing directory for -sysdir switch"));
1234                 exit(1);
1235         }
1236         cl_system_support = arg;
1237         return 1;
1238 }
1239
1240
1241 int parse_userdir(string const & arg, string const &, string &)
1242 {
1243         if (arg.empty()) {
1244                 Alert::error(_("No user directory"),
1245                         _("Missing directory for -userdir switch"));
1246                 exit(1);
1247         }
1248         cl_user_support = arg;
1249         return 1;
1250 }
1251
1252
1253 int parse_execute(string const & arg, string const &, string & batch)
1254 {
1255         if (arg.empty()) {
1256                 Alert::error(_("Incomplete command"),
1257                         _("Missing command string after --execute switch"));
1258                 exit(1);
1259         }
1260         batch = arg;
1261         return 1;
1262 }
1263
1264
1265 int parse_export_to(string const & type, string const & output_file, string & batch)
1266 {
1267         if (type.empty()) {
1268                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1269                                          "--export-to switch")) << endl;
1270                 exit(1);
1271         }
1272         if (output_file.empty()) {
1273                 lyxerr << to_utf8(_("Missing destination filename after "
1274                                          "--export-to switch")) << endl;
1275                 exit(1);
1276         }
1277         batch = "buffer-export " + type + " " + output_file;
1278         use_gui = false;
1279         return 2;
1280 }
1281
1282
1283 int parse_export(string const & type, string const &, string & batch)
1284 {
1285         if (type.empty()) {
1286                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1287                                          "--export switch")) << endl;
1288                 exit(1);
1289         }
1290         batch = "buffer-export " + type;
1291         use_gui = false;
1292         return 1;
1293 }
1294
1295
1296 int parse_import(string const & type, string const & file, string & batch)
1297 {
1298         if (type.empty()) {
1299                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1300                                          "--import switch")) << endl;
1301                 exit(1);
1302         }
1303         if (file.empty()) {
1304                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1305                 exit(1);
1306         }
1307         batch = "buffer-import " + type + ' ' + file;
1308         return 2;
1309 }
1310
1311
1312 int parse_geometry(string const & arg1, string const &, string &)
1313 {
1314         geometryArg = arg1;
1315         // don't remove "-geometry", it will be pruned out later in the
1316         // frontend if need be.
1317         return -1;
1318 }
1319
1320
1321 int parse_batch(string const &, string const &, string &)
1322 {
1323         use_gui = false;
1324         return 0;
1325 }
1326
1327
1328 int parse_noremote(string const &, string const &, string &)
1329 {
1330         run_mode = NEW_INSTANCE;
1331         return 0;
1332 }
1333
1334
1335 int parse_remote(string const &, string const &, string &)
1336 {
1337         run_mode = USE_REMOTE;
1338         return 0;
1339 }
1340
1341
1342 int parse_verbose(string const &, string const &, string &)
1343 {
1344         verbose = true;
1345         return 0;
1346 }
1347
1348
1349 int parse_ignore_error_message(string const & arg1, string const &, string &)
1350 {
1351         if (arg1 == "missing_glyphs") {
1352                 ignore_missing_glyphs = true;
1353                 return 1;
1354         }
1355         return 0;
1356 }
1357
1358
1359 int parse_force(string const & arg, string const &, string &)
1360 {
1361         if (arg == "all") {
1362                 force_overwrite = ALL_FILES;
1363                 return 1;
1364         } else if (arg == "main") {
1365                 force_overwrite = MAIN_FILE;
1366                 return 1;
1367         } else if (arg == "none") {
1368                 force_overwrite = NO_FILES;
1369                 return 1;
1370         }
1371         force_overwrite = ALL_FILES;
1372         return 0;
1373 }
1374
1375
1376 } // namespace anon
1377
1378
1379 void LyX::easyParse(int & argc, char * argv[])
1380 {
1381         map<string, cmd_helper> cmdmap;
1382
1383         cmdmap["-dbg"] = parse_dbg;
1384         cmdmap["-help"] = parse_help;
1385         cmdmap["--help"] = parse_help;
1386         cmdmap["-version"] = parse_version;
1387         cmdmap["--version"] = parse_version;
1388         cmdmap["-sysdir"] = parse_sysdir;
1389         cmdmap["-userdir"] = parse_userdir;
1390         cmdmap["-x"] = parse_execute;
1391         cmdmap["--execute"] = parse_execute;
1392         cmdmap["-e"] = parse_export;
1393         cmdmap["--export"] = parse_export;
1394         cmdmap["-E"] = parse_export_to;
1395         cmdmap["--export-to"] = parse_export_to;
1396         cmdmap["-i"] = parse_import;
1397         cmdmap["--import"] = parse_import;
1398         cmdmap["-geometry"] = parse_geometry;
1399         cmdmap["-batch"] = parse_batch;
1400         cmdmap["-f"] = parse_force;
1401         cmdmap["--force-overwrite"] = parse_force;
1402         cmdmap["-n"] = parse_noremote;
1403         cmdmap["--no-remote"] = parse_noremote;
1404         cmdmap["-r"] = parse_remote;
1405         cmdmap["--remote"] = parse_remote;
1406         cmdmap["-v"] = parse_verbose;
1407         cmdmap["--verbose"] = parse_verbose;
1408         cmdmap["--ignore-error-message"] = parse_ignore_error_message;
1409
1410         for (int i = 1; i < argc; ++i) {
1411                 map<string, cmd_helper>::const_iterator it
1412                         = cmdmap.find(argv[i]);
1413
1414                 // don't complain if not found - may be parsed later
1415                 if (it == cmdmap.end())
1416                         continue;
1417
1418                 string const arg =
1419                         (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1420                 string const arg2 =
1421                         (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1422
1423                 string batch;
1424                 int const remove = 1 + it->second(arg, arg2, batch);
1425                 if (!batch.empty())
1426                         pimpl_->batch_commands.push_back(batch);
1427
1428                 // Now, remove used arguments by shifting
1429                 // the following ones remove places down.
1430                 if (remove > 0) {
1431                         os::remove_internal_args(i, remove);
1432                         argc -= remove;
1433                         for (int j = i; j < argc; ++j)
1434                                 argv[j] = argv[j + remove];
1435                         --i;
1436                 }
1437         }
1438 }
1439
1440
1441 FuncStatus getStatus(FuncRequest const & action)
1442 {
1443         LAPPERR(theApp());
1444         return theApp()->getStatus(action);
1445 }
1446
1447
1448 DispatchResult const & dispatch(FuncRequest const & action)
1449 {
1450         LAPPERR(theApp());
1451         return theApp()->dispatch(action);
1452 }
1453
1454
1455 void dispatch(FuncRequest const & action, DispatchResult & dr)
1456 {
1457         LAPPERR(theApp());
1458         theApp()->dispatch(action, dr);
1459 }
1460
1461
1462 vector<string> & theFilesToLoad()
1463 {
1464         LAPPERR(singleton_);
1465         return singleton_->pimpl_->files_to_load_;
1466 }
1467
1468
1469 BufferList & theBufferList()
1470 {
1471         LAPPERR(singleton_);
1472         return singleton_->pimpl_->buffer_list_;
1473 }
1474
1475
1476 Server & theServer()
1477 {
1478         // FIXME: this should not be use_gui dependent
1479         LWARNIF(use_gui);
1480         LAPPERR(singleton_);
1481         return *singleton_->pimpl_->lyx_server_;
1482 }
1483
1484
1485 ServerSocket & theServerSocket()
1486 {
1487         // FIXME: this should not be use_gui dependent
1488         LWARNIF(use_gui);
1489         LAPPERR(singleton_);
1490         return *singleton_->pimpl_->lyx_socket_;
1491 }
1492
1493
1494 KeyMap & theTopLevelKeymap()
1495 {
1496         LAPPERR(singleton_);
1497         return singleton_->pimpl_->toplevel_keymap_;
1498 }
1499
1500
1501 Converters & theConverters()
1502 {
1503         LAPPERR(singleton_);
1504         return  singleton_->pimpl_->converters_;
1505 }
1506
1507
1508 Converters & theSystemConverters()
1509 {
1510         LAPPERR(singleton_);
1511         return  singleton_->pimpl_->system_converters_;
1512 }
1513
1514
1515 Movers & theMovers()
1516 {
1517         LAPPERR(singleton_);
1518         return singleton_->pimpl_->movers_;
1519 }
1520
1521
1522 Mover const & getMover(string  const & fmt)
1523 {
1524         LAPPERR(singleton_);
1525         return singleton_->pimpl_->movers_(fmt);
1526 }
1527
1528
1529 void setMover(string const & fmt, string const & command)
1530 {
1531         LAPPERR(singleton_);
1532         singleton_->pimpl_->movers_.set(fmt, command);
1533 }
1534
1535
1536 Movers & theSystemMovers()
1537 {
1538         LAPPERR(singleton_);
1539         return singleton_->pimpl_->system_movers_;
1540 }
1541
1542
1543 Messages const & getMessages(string const & language)
1544 {
1545         LAPPERR(singleton_);
1546         return singleton_->messages(language);
1547 }
1548
1549
1550 Messages const & getGuiMessages()
1551 {
1552         LAPPERR(singleton_);
1553         return singleton_->messages(Messages::guiLanguage());
1554 }
1555
1556
1557 Session & theSession()
1558 {
1559         LAPPERR(singleton_);
1560         return *singleton_->pimpl_->session_.get();
1561 }
1562
1563
1564 LaTeXFonts & theLaTeXFonts()
1565 {
1566         LAPPERR(singleton_);
1567         if (!singleton_->pimpl_->latexfonts_)
1568                 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1569         return *singleton_->pimpl_->latexfonts_;
1570 }
1571
1572
1573 CmdDef & theTopLevelCmdDef()
1574 {
1575         LAPPERR(singleton_);
1576         return singleton_->pimpl_->toplevel_cmddef_;
1577 }
1578
1579
1580 SpellChecker * theSpellChecker()
1581 {
1582         if (!singleton_->pimpl_->spell_checker_)
1583                 setSpellChecker();
1584         return singleton_->pimpl_->spell_checker_;
1585 }
1586
1587
1588 void setSpellChecker()
1589 {
1590         SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1591                 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1592
1593         if (lyxrc.spellchecker == "native") {
1594 #if defined(USE_MACOSX_PACKAGING)
1595                 if (!singleton_->pimpl_->apple_spell_checker_)
1596                         singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1597                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1598 #else
1599                 singleton_->pimpl_->spell_checker_ = 0;
1600 #endif
1601         } else if (lyxrc.spellchecker == "aspell") {
1602 #if defined(USE_ASPELL)
1603                 if (!singleton_->pimpl_->aspell_checker_)
1604                         singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1605                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1606 #else
1607                 singleton_->pimpl_->spell_checker_ = 0;
1608 #endif
1609         } else if (lyxrc.spellchecker == "enchant") {
1610 #if defined(USE_ENCHANT)
1611                 if (!singleton_->pimpl_->enchant_checker_)
1612                         singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1613                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1614 #else
1615                 singleton_->pimpl_->spell_checker_ = 0;
1616 #endif
1617         } else if (lyxrc.spellchecker == "hunspell") {
1618 #if defined(USE_HUNSPELL)
1619                 if (!singleton_->pimpl_->hunspell_checker_)
1620                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1621                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1622 #else
1623                 singleton_->pimpl_->spell_checker_ = 0;
1624 #endif
1625         } else {
1626                 singleton_->pimpl_->spell_checker_ = 0;
1627         }
1628         if (singleton_->pimpl_->spell_checker_) {
1629                 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1630                 singleton_->pimpl_->spell_checker_->advanceChangeNumber();
1631         }
1632 }
1633
1634 } // namespace lyx