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