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