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