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