]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
Warn if buffer is switched with unapplied document changes (#9369)
[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 "DispatchResult.h"
31 #include "EnchantChecker.h"
32 #include "Encoding.h"
33 #include "ErrorList.h"
34 #include "Format.h"
35 #include "FuncStatus.h"
36 #include "HunspellChecker.h"
37 #include "KeyMap.h"
38 #include "Language.h"
39 #include "LaTeXFonts.h"
40 #include "LayoutFile.h"
41 #include "LyXAction.h"
42 #include "LyXRC.h"
43 #include "ModuleList.h"
44 #include "Mover.h"
45 #include "Server.h"
46 #include "ServerSocket.h"
47 #include "Session.h"
48
49 #include "frontends/alert.h"
50 #include "frontends/Application.h"
51
52 #include "support/ConsoleApplication.h"
53 #include "support/convert.h"
54 #include "support/lassert.h"
55 #include "support/debug.h"
56 #include "support/environment.h"
57 #include "support/ExceptionMessage.h"
58 #include "support/filetools.h"
59 #include "support/gettext.h"
60 #include "support/lstrings.h"
61 #include "support/Messages.h"
62 #include "support/os.h"
63 #include "support/Package.h"
64 #include "support/unique_ptr.h"
65
66 #include <csignal>
67 #include <iostream>
68 #include <functional>
69 #include <map>
70 #include <stdlib.h>
71 #include <string>
72 #include <vector>
73
74 #include <qglobal.h> // For QT_VERSION
75
76 using namespace std;
77 using namespace lyx::support;
78
79 #if defined (USE_MACOSX_PACKAGING)
80 #include <crt_externs.h>
81 #endif
82
83 namespace lyx {
84
85 namespace Alert = frontend::Alert;
86 namespace os = support::os;
87
88
89
90 // Are we using the GUI at all?  We default to true and this is changed
91 // to false when the export feature is used.
92
93 bool use_gui = true;
94
95
96 // Report on the terminal about spawned commands. The default is false
97 // and can be changed with the option -v (--verbose).
98
99 bool verbose = false;
100
101
102 // Do not treat the "missing glyphs" warning of fontspec as an error message.
103 // The default is false and can be changed with the option
104 // --ignore-error-message missing_glyphs
105 // This is used in automated testing.
106 bool ignore_missing_glyphs = false;
107
108
109 // We default to open documents in an already running instance, provided that
110 // the lyxpipe has been setup. This can be overridden either on the command
111 // line or through preference settings.
112
113 RunMode run_mode = PREFERRED;
114
115
116 // Tell what files can be silently overwritten during batch export.
117 // Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
118 // Unless specified on command line (through the -f switch) or through the
119 // environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
120
121 OverwriteFiles force_overwrite = UNSPECIFIED;
122
123
124 // Scale the GUI by this factor. This works whether we have a HiDpi screen
125 // or not and scales everything, also fonts. Can only be changed by setting
126 // the QT_SCALE_FACTOR environment variable before launching LyX and only
127 // works properly with Qt 5.6 or higher.
128
129 double qt_scale_factor = 1.0;
130
131
132 namespace {
133
134 // Filled with the command line arguments "foo" of "-sysdir foo" or
135 // "-userdir foo".
136 string cl_system_support;
137 string cl_user_support;
138
139 string geometryArg;
140
141 LyX * singleton_ = nullptr;
142
143 void showFileError(string const & error)
144 {
145         Alert::warning(_("Could not read configuration file"),
146                        bformat(_("Error while reading the configuration file\n%1$s.\n"
147                            "Please check your installation."), from_utf8(error)));
148 }
149
150 } // namespace
151
152 /// The main application class private implementation.
153 struct LyX::Impl {
154         Impl()
155                 : latexfonts_(nullptr), spell_checker_(nullptr),
156                   apple_spell_checker_(nullptr), aspell_checker_(nullptr),
157                   enchant_checker_(nullptr), hunspell_checker_(nullptr)
158         {}
159
160         ~Impl()
161         {
162                 delete latexfonts_;
163                 delete apple_spell_checker_;
164                 delete aspell_checker_;
165                 delete enchant_checker_;
166                 delete hunspell_checker_;
167         }
168
169         ///
170         BufferList buffer_list_;
171         ///
172         KeyMap toplevel_keymap_;
173         ///
174         CmdDef toplevel_cmddef_;
175         ///
176         unique_ptr<Server> lyx_server_;
177         ///
178         unique_ptr<ServerSocket> lyx_socket_;
179         ///
180         unique_ptr<frontend::Application> application_;
181         /// lyx session, containing lastfiles, lastfilepos, and lastopened
182         unique_ptr<Session> session_;
183
184         /// Files to load at start.
185         vector<string> files_to_load_;
186
187         /// The messages translators.
188         map<string, Messages> messages_;
189
190         /// The file converters.
191         Converters converters_;
192         /// The system converters after reading lyxrc.defaults.
193         Converters system_converters_;
194
195         /// Global format information
196         Formats formats_;
197         /// The system formats after reading lyxrc.defaults.
198         Formats system_formats_;
199
200
201         ///
202         Movers movers_;
203         ///
204         Movers system_movers_;
205
206         /// the parsed command line batch command if any
207         vector<string> batch_commands;
208
209         ///
210         LaTeXFonts * latexfonts_;
211
212         ///
213         SpellChecker * spell_checker_;
214         ///
215         SpellChecker * apple_spell_checker_;
216         ///
217         SpellChecker * aspell_checker_;
218         ///
219         SpellChecker * enchant_checker_;
220         ///
221         SpellChecker * hunspell_checker_;
222 };
223
224
225 /// The main application class for console mode
226 class LyXConsoleApp : public ConsoleApplication
227 {
228 public:
229         LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
230                 : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
231                   argc_(argc), argv_(argv)
232         {
233         }
234         void doExec() override
235         {
236                 int const exit_status = lyx_->execWithoutGui(argc_, argv_);
237                 exit(exit_status);
238         }
239 private:
240         LyX * lyx_;
241         int & argc_;
242         char ** argv_;
243 };
244
245
246 ///
247 frontend::Application * theApp()
248 {
249         if (singleton_)
250                 return singleton_->pimpl_->application_.get();
251         else
252                 return nullptr;
253 }
254
255
256 LyX::~LyX()
257 {
258         delete pimpl_;
259         singleton_ = nullptr;
260 }
261
262
263 void lyx_exit(int exit_code)
264 {
265         if (exit_code)
266                 // Something wrong happened so better save everything, just in
267                 // case.
268                 emergencyCleanup();
269
270 #ifndef NDEBUG
271         // Properly crash in debug mode in order to get a useful backtrace.
272         abort();
273 #endif
274
275         // In release mode, try to exit gracefully.
276         if (theApp())
277                 theApp()->exit(exit_code);
278         else
279                 exit(exit_code);
280 }
281
282
283 LyX::LyX()
284         : first_start(false)
285 {
286         singleton_ = this;
287         pimpl_ = new Impl;
288 }
289
290
291 Messages & LyX::messages(string const & language)
292 {
293         map<string, Messages>::iterator it = pimpl_->messages_.find(language);
294
295         if (it != pimpl_->messages_.end())
296                 return it->second;
297
298         pair<map<string, Messages>::iterator, bool> result =
299                         pimpl_->messages_.insert(make_pair(language, Messages(language)));
300
301         LATTEST(result.second);
302         return result.first->second;
303 }
304
305
306 int LyX::exec(int & argc, char * argv[])
307 {
308         // Minimal setting of locale before parsing command line
309         try {
310                 init_package(os::utf8_argv(0), string(), string());
311                 // we do not get to this point when init_package throws an exception
312                 setLocale();
313         } catch (ExceptionMessage const & message) {
314                 LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
315         }
316
317         // Here we need to parse the command line. At least
318         // we need to parse for "-dbg" and "-help"
319         easyParse(argc, argv);
320
321 #if QT_VERSION >= 0x050600
322         // Check whether Qt will scale all GUI elements and accordingly
323         // set the scale factor so that to avoid blurred images and text
324         char const * const scale_factor = getenv("QT_SCALE_FACTOR");
325         if (scale_factor) {
326                 qt_scale_factor = convert<double>(scale_factor);
327                 if (qt_scale_factor < 1.0)
328                         qt_scale_factor = 1.0;
329         }
330 #endif
331
332         try {
333                 init_package(os::utf8_argv(0), cl_system_support, cl_user_support);
334         } catch (ExceptionMessage const & message) {
335                 if (message.type_ == ErrorException) {
336                         Alert::error(message.title_, message.details_);
337                         lyx_exit(1);
338                 } else if (message.type_ == WarningException) {
339                         Alert::warning(message.title_, message.details_);
340                 }
341         }
342
343         // Reinit the messages machinery in case package() knows
344         // something interesting about the locale directory.
345         setLocale();
346
347         if (!use_gui) {
348                 LyXConsoleApp app(this, argc, argv);
349
350                 // Reestablish our defaults, as Qt overwrites them
351                 // after creating app
352                 setLocale();//???
353
354                 return app.exec();
355         }
356
357         // Let the frontend parse and remove all arguments that it knows
358         pimpl_->application_.reset(createApplication(argc, argv));
359
360         // Reestablish our defaults, as Qt overwrites them
361         // after createApplication()
362         setLocale();//???
363
364         // Parse and remove all known arguments in the LyX singleton
365         // Give an error for all remaining ones.
366         int exit_status = init(argc, argv);
367         if (exit_status) {
368                 // Kill the application object before exiting.
369                 pimpl_->application_.reset();
370                 use_gui = false;
371                 prepareExit();
372                 return exit_status;
373         }
374
375         // If not otherwise specified by a command line option or
376         // by preferences, we default to reuse a running instance.
377         if (run_mode == PREFERRED)
378                 run_mode = USE_REMOTE;
379
380         // FIXME
381         /* Create a CoreApplication class that will provide the main event loop
382         * and the socket callback registering. With Qt, only QtCore
383         * library would be needed.
384         * When this is done, a server_mode could be created and the following two
385         * line would be moved out from here.
386         * However, note that the first of the two lines below triggers the
387         * "single instance" behavior, which should occur right at this point.
388         */
389         // Note: socket callback must be registered after init(argc, argv)
390         // such that package().temp_dir() is properly initialized.
391         pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
392         pimpl_->lyx_socket_.reset(new ServerSocket(
393                         FileName(package().temp_dir().absFileName() + "/lyxsocket")));
394
395         // Start the real execution loop.
396         if (!pimpl_->lyx_server_->deferredLoadingToOtherInstance())
397                 exit_status = pimpl_->application_->exec();
398         else if (!pimpl_->files_to_load_.empty()) {
399                 lyxerr << _("The following files could not be loaded:") << endl;
400                 for (auto const & f : pimpl_->files_to_load_)
401                         lyxerr << f << endl;
402         }
403
404         prepareExit();
405
406         return exit_status;
407 }
408
409
410 void LyX::prepareExit()
411 {
412         // Clear the clipboard and selection stack:
413         cap::clearCutStack();
414         cap::clearSelection();
415
416         // Write the index file of the converter cache
417         ConverterCache::get().writeIndex();
418
419         // closing buffer may throw exceptions, but we ignore them since we
420         // are quitting.
421         try {
422                 // close buffers first
423                 pimpl_->buffer_list_.closeAll();
424         } catch (ExceptionMessage const &) {}
425
426         // register session changes and shutdown server and socket
427         if (use_gui) {
428                 if (pimpl_->session_)
429                         pimpl_->session_->writeFile();
430                 pimpl_->session_.reset();
431                 pimpl_->lyx_server_.reset();
432                 pimpl_->lyx_socket_.reset();
433         }
434
435         // do any other cleanup procedures now
436         if (package().temp_dir() != package().system_temp_dir()) {
437                 string const abs_tmpdir = package().temp_dir().absFileName();
438                 if (!contains(abs_tmpdir, "lyx_tmpdir")) {
439                         docstring const msg =
440                                 bformat(_("%1$s does not appear like a LyX created temporary directory."),
441                                 from_utf8(abs_tmpdir));
442                         Alert::warning(_("Cannot remove temporary directory"), msg);
443                 } else {
444                         LYXERR(Debug::INFO, "Deleting tmp dir " << abs_tmpdir);
445                         if (!package().temp_dir().destroyDirectory()) {
446                                 LYXERR0(bformat(_("Unable to remove the temporary directory %1$s"),
447                                         from_utf8(abs_tmpdir)));
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", "Welcome.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 } // namespace
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         {
910                 // Add the directory containing the dt2dv and dv2dt executables to the path
911                 FileName dtldir;
912                 if (!package().build_support().empty()) {
913                         // dtl executables should be in the same dir ar tex2lyx
914                         dtldir = package().binary_dir();
915                 }
916                 else {
917                         dtldir = FileName(addName(package().system_support().absFileName(), "extratools"));
918                 }
919 #if defined(_WIN32)
920                 string dtlexe = "dt2dv.exe";
921 #else
922                 string dtlexe = "dt2dv";
923 #endif
924                 FileName const dt2dv = FileName(addName(dtldir.absFileName(), dtlexe));
925                 if (dt2dv.exists())
926                         prependEnvPath("PATH", dtldir.absFileName());
927         }
928         if (!lyxrc.path_prefix.empty())
929                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
930
931         // Check that user LyX directory is ok.
932         {
933                 string const lock_file = package().getConfigureLockName();
934                 int fd = fileLock(lock_file.c_str());
935
936                 if (queryUserLyXDir(package().explicit_user_support())) {
937                         package().reconfigureUserLyXDir("");
938                         // Now the user directory is present on first start.
939                         initTemplatePath();
940                 }
941                 fileUnlock(fd, lock_file.c_str());
942         }
943
944         if (!use_gui) {
945                 // No need for a splash when there is no GUI
946                 first_start = false;
947                 // Default is to overwrite the main file during export, unless
948                 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
949                 if (force_overwrite == UNSPECIFIED) {
950                         string const what = getEnv("LYX_FORCE_OVERWRITE");
951                         if (what == "all")
952                                 force_overwrite = ALL_FILES;
953                         else if (what == "none")
954                                 force_overwrite = NO_FILES;
955                         else
956                                 force_overwrite = MAIN_FILE;
957                 }
958         }
959
960         // This one is generated in user_support directory by lib/configure.py.
961         if (!readRcFile("lyxrc.defaults"))
962                 return false;
963
964         // Query the OS to know what formats are viewed natively
965         theFormats().setAutoOpen();
966
967         // Read lyxrc.dist again to be able to override viewer auto-detection.
968         readRcFile("lyxrc.dist");
969
970         system_lyxrc = lyxrc;
971         theSystemFormats() = theFormats();
972         pimpl_->system_converters_ = pimpl_->converters_;
973         pimpl_->system_movers_ = pimpl_->movers_;
974         system_lcolor = lcolor;
975
976         // This one is edited through the preferences dialog.
977         if (!readRcFile("preferences", true))
978                 return false;
979
980         // The language may have been set to someting useful through prefs
981         setLocale();
982
983         if (!readEncodingsFile("encodings", "unicodesymbols"))
984                 return false;
985         if (!readLanguagesFile("languages"))
986                 return false;
987
988         LYXERR(Debug::INIT, "Reading layouts...");
989         // Load the layouts
990         LayoutFileList::get().read();
991         //... the modules
992         theModuleList.read();
993         //... and the cite engines
994         theCiteEnginesList.read();
995
996         // read keymap and ui files in batch mode as well
997         // because InsetInfo needs to know these to produce
998         // the correct output
999
1000         // Set up command definitions
1001         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
1002
1003         // FIXME
1004         // Set up bindings
1005         pimpl_->toplevel_keymap_.read("site");
1006         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
1007         // load user bind file user.bind
1008         pimpl_->toplevel_keymap_.read("user", nullptr, KeyMap::MissingOK);
1009
1010         if (lyxerr.debugging(Debug::LYXRC))
1011                 lyxrc.print();
1012
1013         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
1014         // Prepend path prefix a second time to take the user preferences into a account
1015         if (!lyxrc.path_prefix.empty())
1016                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
1017
1018         FileName const document_path(lyxrc.document_path);
1019         if (document_path.exists() && document_path.isDirectory())
1020                 package().document_dir() = document_path;
1021
1022         package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
1023         if (package().temp_dir().empty()) {
1024                 Alert::error(_("Could not create temporary directory"),
1025                              bformat(_("Could not create a temporary directory in\n"
1026                                                        "\"%1$s\"\n"
1027                                                            "Make sure that this path exists and is writable and try again."),
1028                                      from_utf8(lyxrc.tempdir_path)));
1029                 // createLyXTmpDir() tries sufficiently hard to create a
1030                 // usable temp dir, so the probability to come here is
1031                 // close to zero. We therefore don't try to overcome this
1032                 // problem with e.g. asking the user for a new path and
1033                 // trying again but simply exit.
1034                 return false;
1035         }
1036
1037         LYXERR(Debug::INIT, "LyX tmp dir: `"
1038                             << package().temp_dir().absFileName() << '\'');
1039
1040         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
1041         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1042
1043         // This must happen after package initialization and after lyxrc is
1044         // read, therefore it can't be done by a static object.
1045         ConverterCache::init();
1046
1047         return true;
1048 }
1049
1050
1051 void emergencyCleanup()
1052 {
1053         // what to do about tmpfiles is non-obvious. we would
1054         // like to delete any we find, but our lyxdir might
1055         // contain documents etc. which might be helpful on
1056         // a crash
1057
1058         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1059         if (use_gui) {
1060                 if (singleton_->pimpl_->lyx_server_)
1061                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
1062                 singleton_->pimpl_->lyx_server_.reset();
1063                 singleton_->pimpl_->lyx_socket_.reset();
1064         }
1065 }
1066
1067
1068 bool LyX::queryUserLyXDir(bool explicit_userdir)
1069 {
1070         // Does user directory exist?
1071         FileName const sup = package().user_support();
1072         if (sup.exists() && sup.isDirectory()) {
1073                 first_start = false;
1074
1075                 return configFileNeedsUpdate("lyxrc.defaults")
1076                         || configFileNeedsUpdate("lyxmodules.lst")
1077                         || configFileNeedsUpdate("textclass.lst")
1078                         || configFileNeedsUpdate("packages.lst")
1079                         || configFileNeedsUpdate("lyxciteengines.lst")
1080                         || configFileNeedsUpdate("xtemplates.lst");
1081         }
1082
1083         first_start = !explicit_userdir;
1084
1085         // If the user specified explicitly a directory, ask whether
1086         // to create it. If the user says "no", then exit.
1087         if (explicit_userdir &&
1088             Alert::prompt(
1089                     _("Missing user LyX directory"),
1090                     bformat(_("You have specified a non-existent user "
1091                                            "LyX directory, %1$s.\n"
1092                                            "It is needed to keep your own configuration."),
1093                             from_utf8(package().user_support().absFileName())),
1094                     1, 0,
1095                     _("&Create directory"),
1096                     _("&Exit LyX"))) {
1097                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1098                 earlyExit(EXIT_FAILURE);
1099         }
1100
1101         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1102                           from_utf8(sup.absFileName()))) << endl;
1103
1104         if (!sup.createDirectory(0755)) {
1105                 // Failed, so let's exit.
1106                 lyxerr << to_utf8(_("Failed to create directory. Perhaps wrong -userdir option?\nExiting."))
1107                        << endl;
1108                 earlyExit(EXIT_FAILURE);
1109         }
1110
1111         return true;
1112 }
1113
1114
1115 bool LyX::readRcFile(string const & name, bool check_format)
1116 {
1117         LYXERR(Debug::INIT, "About to read " << name << "... ");
1118
1119         FileName const lyxrc_path = libFileSearch(string(), name);
1120         if (lyxrc_path.empty()) {
1121                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1122                 // FIXME
1123                 // This was the previous logic, but can it be right??
1124                 return true;
1125         }
1126         LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1127         bool const success = lyxrc.read(lyxrc_path, check_format);
1128         if (!success)
1129                 showFileError(name);
1130         return success;
1131 }
1132
1133 // Read the languages file `name'
1134 bool LyX::readLanguagesFile(string const & name)
1135 {
1136         LYXERR(Debug::INIT, "About to read " << name << "...");
1137
1138         FileName const lang_path = libFileSearch(string(), name);
1139         if (lang_path.empty()) {
1140                 showFileError(name);
1141                 return false;
1142         }
1143         languages.read(lang_path);
1144         return true;
1145 }
1146
1147
1148 // Read the encodings file `name'
1149 bool LyX::readEncodingsFile(string const & enc_name,
1150                             string const & symbols_name)
1151 {
1152         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1153                             << symbols_name << "...");
1154
1155         FileName const symbols_path = libFileSearch(string(), symbols_name);
1156         if (symbols_path.empty()) {
1157                 showFileError(symbols_name);
1158                 return false;
1159         }
1160
1161         FileName const enc_path = libFileSearch(string(), enc_name);
1162         if (enc_path.empty()) {
1163                 showFileError(enc_name);
1164                 return false;
1165         }
1166         encodings.read(enc_path, symbols_path);
1167         return true;
1168 }
1169
1170
1171 namespace {
1172
1173 /// return the the number of arguments consumed
1174 typedef function<int(string const &, string const &, string &)> cmd_helper;
1175
1176 int parse_dbg(string const & arg, string const &, string &)
1177 {
1178         if (arg.empty()) {
1179                 cout << to_utf8(_("List of supported debug flags:")) << endl;
1180                 Debug::showTags(cout);
1181                 exit(0);
1182         }
1183         string bad = Debug::badValue(arg);
1184         if (bad.empty()) {
1185                 lyxerr.setLevel(Debug::value(arg));
1186                 Debug::showLevel(lyxerr, lyxerr.level());
1187         } else {
1188                 cout << to_utf8(bformat(_("Bad debug value `%1$s'. Exiting."),
1189                                         from_utf8(bad))) << endl;
1190                 exit(1);
1191         }
1192         return 1;
1193 }
1194
1195
1196 int parse_help(string const &, string const &, string &)
1197 {
1198         cout <<
1199                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1200                   "Command line switches (case sensitive):\n"
1201                   "\t-help              summarize LyX usage\n"
1202                   "\t-userdir dir       set user directory to dir\n"
1203                   "\t-sysdir dir        set system directory to dir\n"
1204                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1205                   "\t-dbg feature[,feature]...\n"
1206                   "                  select the features to debug.\n"
1207                   "                  Type `lyx -dbg' to see the list of features\n"
1208                   "\t-x [--execute] command\n"
1209                   "                  where command is a lyx command.\n"
1210                   "\t-e [--export] fmt\n"
1211                   "                  where fmt is the export format of choice. Look in\n"
1212                   "                  Tools->Preferences->File Handling->File Formats->Short Name\n"
1213                   "                  to see which parameter (which differs from the format name\n"
1214                   "                  in the File->Export menu) should be passed. To export to\n"
1215                   "                  the document's default output format, use 'default'.\n"
1216                   "                  Note that the order of -e and -x switches matters.\n"
1217                   "\t-E [--export-to] fmt filename\n"
1218                   "                  where fmt is the export format of choice (see --export),\n"
1219                   "                  and filename is the destination filename.\n"
1220                   "\t-i [--import] fmt file.xxx\n"
1221                   "                  where fmt is the import format of choice\n"
1222                   "                  and file.xxx is the file to be imported.\n"
1223                   "\t-f [--force-overwrite] what\n"
1224                   "                  where what is either `all', `main' or `none',\n"
1225                   "                  specifying whether all files, main file only, or no files,\n"
1226                   "                  respectively, are to be overwritten during a batch export.\n"
1227                   "                  Anything else is equivalent to `all', but is not consumed.\n"
1228                   "\t--ignore-error-message which\n"
1229                   "                  allows you to ignore specific LaTeX error messages.\n"
1230                   "                  Do not use for final documents! Currently supported values:\n"
1231                   "                  * missing_glyphs: Fontspec `missing glyphs' error.\n"
1232                   "\t-n [--no-remote]\n"
1233                   "                  open documents in a new instance\n"
1234                   "\t-r [--remote]\n"
1235                   "                  open documents in an already running instance\n"
1236                   "                  (a working lyxpipe is needed)\n"
1237                   "\t-v [--verbose]\n"
1238                   "                  report on terminal about spawned commands.\n"
1239                   "\t-batch    execute commands without launching GUI and exit.\n"
1240                   "\t-version  summarize version and build info\n"
1241                                "Check the LyX man page for more details.")) << endl;
1242         exit(0);
1243         return 0;
1244 }
1245
1246
1247 int parse_version(string const &, string const &, string &)
1248 {
1249         cout << "LyX " << lyx_version
1250                << " (" << lyx_release_date << ")" << endl;
1251         if (string(lyx_git_commit_hash) != "none")
1252                 cout << to_utf8(_("  Git commit hash "))
1253                      << string(lyx_git_commit_hash).substr(0,8) << endl;
1254         cout << lyx_version_info << endl;
1255         exit(0);
1256         return 0;
1257 }
1258
1259
1260 int parse_sysdir(string const & arg, string const &, string &)
1261 {
1262         if (arg.empty()) {
1263                 Alert::error(_("No system directory"),
1264                         _("Missing directory for -sysdir switch"));
1265                 exit(1);
1266         }
1267         cl_system_support = arg;
1268         return 1;
1269 }
1270
1271
1272 int parse_userdir(string const & arg, string const &, string &)
1273 {
1274         if (arg.empty()) {
1275                 Alert::error(_("No user directory"),
1276                         _("Missing directory for -userdir switch"));
1277                 exit(1);
1278         }
1279         cl_user_support = arg;
1280         return 1;
1281 }
1282
1283
1284 int parse_execute(string const & arg, string const &, string & batch)
1285 {
1286         if (arg.empty()) {
1287                 Alert::error(_("Incomplete command"),
1288                         _("Missing command string after --execute switch"));
1289                 exit(1);
1290         }
1291         batch = arg;
1292         return 1;
1293 }
1294
1295
1296 int parse_export_to(string const & type, string const & output_file, string & batch)
1297 {
1298         if (type.empty()) {
1299                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1300                                          "--export-to switch")) << endl;
1301                 exit(1);
1302         }
1303         if (output_file.empty()) {
1304                 lyxerr << to_utf8(_("Missing destination filename after "
1305                                          "--export-to switch")) << endl;
1306                 exit(1);
1307         }
1308         batch = "buffer-export " + type + " " + output_file;
1309         use_gui = false;
1310         return 2;
1311 }
1312
1313
1314 int parse_export(string const & type, string const &, string & batch)
1315 {
1316         if (type.empty()) {
1317                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1318                                          "--export switch")) << endl;
1319                 exit(1);
1320         }
1321         batch = "buffer-export " + type;
1322         use_gui = false;
1323         return 1;
1324 }
1325
1326
1327 int parse_import(string const & type, string const & file, string & batch)
1328 {
1329         if (type.empty()) {
1330                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1331                                          "--import switch")) << endl;
1332                 exit(1);
1333         }
1334         if (file.empty()) {
1335                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1336                 exit(1);
1337         }
1338         batch = "buffer-import " + type + ' ' + file;
1339         return 2;
1340 }
1341
1342
1343 int parse_geometry(string const & arg1, string const &, string &)
1344 {
1345         geometryArg = arg1;
1346         // don't remove "-geometry", it will be pruned out later in the
1347         // frontend if need be.
1348         return -1;
1349 }
1350
1351
1352 int parse_batch(string const &, string const &, string &)
1353 {
1354         use_gui = false;
1355         return 0;
1356 }
1357
1358
1359 int parse_noremote(string const &, string const &, string &)
1360 {
1361         run_mode = NEW_INSTANCE;
1362         return 0;
1363 }
1364
1365
1366 int parse_remote(string const &, string const &, string &)
1367 {
1368         run_mode = USE_REMOTE;
1369         return 0;
1370 }
1371
1372
1373 int parse_verbose(string const &, string const &, string &)
1374 {
1375         verbose = true;
1376         return 0;
1377 }
1378
1379
1380 int parse_ignore_error_message(string const & arg1, string const &, string &)
1381 {
1382         if (arg1 == "missing_glyphs") {
1383                 ignore_missing_glyphs = true;
1384                 return 1;
1385         }
1386         return 0;
1387 }
1388
1389
1390 int parse_force(string const & arg, string const &, string &)
1391 {
1392         if (arg == "all") {
1393                 force_overwrite = ALL_FILES;
1394                 return 1;
1395         } else if (arg == "main") {
1396                 force_overwrite = MAIN_FILE;
1397                 return 1;
1398         } else if (arg == "none") {
1399                 force_overwrite = NO_FILES;
1400                 return 1;
1401         }
1402         force_overwrite = ALL_FILES;
1403         return 0;
1404 }
1405
1406
1407 } // namespace
1408
1409
1410 void LyX::easyParse(int & argc, char * argv[])
1411 {
1412         map<string, cmd_helper> cmdmap;
1413
1414         cmdmap["-dbg"] = parse_dbg;
1415         cmdmap["-help"] = parse_help;
1416         cmdmap["--help"] = parse_help;
1417         cmdmap["-version"] = parse_version;
1418         cmdmap["--version"] = parse_version;
1419         cmdmap["-sysdir"] = parse_sysdir;
1420         cmdmap["-userdir"] = parse_userdir;
1421         cmdmap["-x"] = parse_execute;
1422         cmdmap["--execute"] = parse_execute;
1423         cmdmap["-e"] = parse_export;
1424         cmdmap["--export"] = parse_export;
1425         cmdmap["-E"] = parse_export_to;
1426         cmdmap["--export-to"] = parse_export_to;
1427         cmdmap["-i"] = parse_import;
1428         cmdmap["--import"] = parse_import;
1429         cmdmap["-geometry"] = parse_geometry;
1430         cmdmap["-batch"] = parse_batch;
1431         cmdmap["-f"] = parse_force;
1432         cmdmap["--force-overwrite"] = parse_force;
1433         cmdmap["-n"] = parse_noremote;
1434         cmdmap["--no-remote"] = parse_noremote;
1435         cmdmap["-r"] = parse_remote;
1436         cmdmap["--remote"] = parse_remote;
1437         cmdmap["-v"] = parse_verbose;
1438         cmdmap["--verbose"] = parse_verbose;
1439         cmdmap["--ignore-error-message"] = parse_ignore_error_message;
1440
1441         for (int i = 1; i < argc; ++i) {
1442                 map<string, cmd_helper>::const_iterator it
1443                         = cmdmap.find(argv[i]);
1444
1445                 // don't complain if not found - may be parsed later
1446                 if (it == cmdmap.end())
1447                         continue;
1448
1449                 string const arg =
1450                         (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1451                 string const arg2 =
1452                         (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1453
1454                 string batch;
1455                 int const remove = 1 + it->second(arg, arg2, batch);
1456                 if (!batch.empty())
1457                         pimpl_->batch_commands.push_back(batch);
1458
1459                 // Now, remove used arguments by shifting
1460                 // the following ones remove places down.
1461                 if (remove > 0) {
1462                         os::remove_internal_args(i, remove);
1463                         argc -= remove;
1464                         for (int j = i; j < argc; ++j)
1465                                 argv[j] = argv[j + remove];
1466                         --i;
1467                 }
1468         }
1469 }
1470
1471
1472 FuncStatus getStatus(FuncRequest const & action)
1473 {
1474         LAPPERR(theApp());
1475         return theApp()->getStatus(action);
1476 }
1477
1478
1479 DispatchResult const & dispatch(FuncRequest const & action)
1480 {
1481         LAPPERR(theApp());
1482         return theApp()->dispatch(action);
1483 }
1484
1485
1486 void dispatch(FuncRequest const & action, DispatchResult & dr)
1487 {
1488         LAPPERR(theApp());
1489         theApp()->dispatch(action, dr);
1490 }
1491
1492
1493 vector<string> & theFilesToLoad()
1494 {
1495         LAPPERR(singleton_);
1496         return singleton_->pimpl_->files_to_load_;
1497 }
1498
1499
1500 BufferList & theBufferList()
1501 {
1502         LAPPERR(singleton_);
1503         return singleton_->pimpl_->buffer_list_;
1504 }
1505
1506
1507 Server & theServer()
1508 {
1509         // FIXME: this should not be use_gui dependent
1510         LWARNIF(use_gui);
1511         LAPPERR(singleton_);
1512         return *singleton_->pimpl_->lyx_server_;
1513 }
1514
1515
1516 ServerSocket & theServerSocket()
1517 {
1518         // FIXME: this should not be use_gui dependent
1519         LWARNIF(use_gui);
1520         LAPPERR(singleton_);
1521         return *singleton_->pimpl_->lyx_socket_;
1522 }
1523
1524
1525 KeyMap & theTopLevelKeymap()
1526 {
1527         LAPPERR(singleton_);
1528         return singleton_->pimpl_->toplevel_keymap_;
1529 }
1530
1531
1532 Formats & theFormats()
1533 {
1534         LAPPERR(singleton_);
1535         return singleton_->pimpl_->formats_;
1536 }
1537
1538
1539 Formats & theSystemFormats()
1540 {
1541         LAPPERR(singleton_);
1542         return singleton_->pimpl_->system_formats_;
1543 }
1544
1545
1546 Converters & theConverters()
1547 {
1548         LAPPERR(singleton_);
1549         return singleton_->pimpl_->converters_;
1550 }
1551
1552
1553 Converters & theSystemConverters()
1554 {
1555         LAPPERR(singleton_);
1556         return singleton_->pimpl_->system_converters_;
1557 }
1558
1559
1560 Movers & theMovers()
1561 {
1562         LAPPERR(singleton_);
1563         return singleton_->pimpl_->movers_;
1564 }
1565
1566
1567 Mover const & getMover(string  const & fmt)
1568 {
1569         LAPPERR(singleton_);
1570         return singleton_->pimpl_->movers_(fmt);
1571 }
1572
1573
1574 void setMover(string const & fmt, string const & command)
1575 {
1576         LAPPERR(singleton_);
1577         singleton_->pimpl_->movers_.set(fmt, command);
1578 }
1579
1580
1581 Movers & theSystemMovers()
1582 {
1583         LAPPERR(singleton_);
1584         return singleton_->pimpl_->system_movers_;
1585 }
1586
1587
1588 Messages const & getMessages(string const & language)
1589 {
1590         LAPPERR(singleton_);
1591         return singleton_->messages(language);
1592 }
1593
1594
1595 Messages const & getGuiMessages()
1596 {
1597         LAPPERR(singleton_);
1598         return singleton_->messages(Messages::guiLanguage());
1599 }
1600
1601
1602 Session & theSession()
1603 {
1604         LAPPERR(singleton_);
1605         return *singleton_->pimpl_->session_.get();
1606 }
1607
1608
1609 LaTeXFonts & theLaTeXFonts()
1610 {
1611         LAPPERR(singleton_);
1612         if (!singleton_->pimpl_->latexfonts_)
1613                 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1614         return *singleton_->pimpl_->latexfonts_;
1615 }
1616
1617
1618 CmdDef & theTopLevelCmdDef()
1619 {
1620         LAPPERR(singleton_);
1621         return singleton_->pimpl_->toplevel_cmddef_;
1622 }
1623
1624
1625 SpellChecker * theSpellChecker()
1626 {
1627         if (!singleton_->pimpl_->spell_checker_)
1628                 setSpellChecker();
1629         return singleton_->pimpl_->spell_checker_;
1630 }
1631
1632
1633 void setSpellChecker()
1634 {
1635         SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1636                 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1637
1638         if (lyxrc.spellchecker == "native") {
1639 #if defined(USE_MACOSX_PACKAGING)
1640                 if (!singleton_->pimpl_->apple_spell_checker_)
1641                         singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1642                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1643 #else
1644                 singleton_->pimpl_->spell_checker_ = nullptr;
1645 #endif
1646         } else if (lyxrc.spellchecker == "aspell") {
1647 #if defined(USE_ASPELL)
1648                 if (!singleton_->pimpl_->aspell_checker_)
1649                         singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1650                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1651 #else
1652                 singleton_->pimpl_->spell_checker_ = nullptr;
1653 #endif
1654         } else if (lyxrc.spellchecker == "enchant") {
1655 #if defined(USE_ENCHANT)
1656                 if (!singleton_->pimpl_->enchant_checker_)
1657                         singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1658                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1659 #else
1660                 singleton_->pimpl_->spell_checker_ = nullptr;
1661 #endif
1662         } else if (lyxrc.spellchecker == "hunspell") {
1663 #if defined(USE_HUNSPELL)
1664                 if (!singleton_->pimpl_->hunspell_checker_)
1665                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1666                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1667 #else
1668                 singleton_->pimpl_->spell_checker_ = nullptr;
1669 #endif
1670         } else {
1671                 singleton_->pimpl_->spell_checker_ = nullptr;
1672         }
1673         if (singleton_->pimpl_->spell_checker_) {
1674                 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1675                 singleton_->pimpl_->spell_checker_->advanceChangeNumber();
1676         }
1677 }
1678
1679 } // namespace lyx