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