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