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