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