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