]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
Prevent par break caused by \egroup (#12821)
[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 installing python with your software manager "
620                           "or from the python.org website."),
621                         0, 1,
622                         _("&Continue"),
623                         _("&Exit LyX")))
624                 {
625                 case 1:
626                         lyx::dispatch(FuncRequest(LFUN_LYX_QUIT, ""));
627                         return;
628                 default:
629                         break;
630                 }
631         }
632
633         // create the first main window
634         lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
635
636         if (!pimpl_->files_to_load_.empty()) {
637                 // if some files were specified at command-line we assume that the
638                 // user wants to edit *these* files and not to restore the session.
639                 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
640                         lyx::dispatch(
641                                 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
642                 }
643                 // clear this list to save a few bytes of RAM
644                 pimpl_->files_to_load_.clear();
645         } else
646                 pimpl_->application_->restoreGuiSession();
647
648         // Execute batch commands if available
649         if (pimpl_->batch_commands.empty())
650                 return;
651
652         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
653         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
654         for (; bcit != bcend; ++bcit) {
655                 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
656                 lyx::dispatch(lyxaction.lookupFunc(*bcit));
657         }
658 }
659
660
661 /*
662 Signals and Windows
663 ===================
664 The SIGHUP signal does not exist on Windows and does not need to be handled.
665
666 Windows handles SIGFPE and SIGSEGV signals as expected.
667
668 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
669 cause a new thread to be spawned. This may well result in unexpected
670 behaviour by the single-threaded LyX.
671
672 SIGTERM signals will come only from another process actually sending
673 that signal using 'raise' in Windows' POSIX compatability layer. It will
674 not come from the general "terminate process" methods that everyone
675 actually uses (and which can't be trapped). Killing an app 'politely' on
676 Windows involves first sending a WM_CLOSE message, something that is
677 caught already by the Qt frontend.
678
679 For more information see:
680
681 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
682 ...signals are mostly useless on Windows for a variety of reasons that are
683 Windows specific...
684
685 'UNIX Application Migration Guide, Chapter 9'
686 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
687
688 'How To Terminate an Application "Cleanly" in Win32'
689 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
690 */
691 extern "C" {
692
693 static void error_handler(int err_sig)
694 {
695         // Throw away any signals other than the first one received.
696         static sig_atomic_t handling_error = false;
697         if (handling_error)
698                 return;
699         handling_error = true;
700
701         // We have received a signal indicating a fatal error, so
702         // try and save the data ASAP.
703         emergencyCleanup();
704
705         // These lyxerr calls may or may not work:
706
707         // Signals are asynchronous, so the main program may be in a very
708         // fragile state when a signal is processed and thus while a signal
709         // handler function executes.
710         // In general, therefore, we should avoid performing any
711         // I/O operations or calling most library and system functions from
712         // signal handlers.
713
714         // This shouldn't matter here, however, as we've already invoked
715         // emergencyCleanup.
716         docstring msg;
717         switch (err_sig) {
718 #ifdef SIGHUP
719         case SIGHUP:
720                 msg = _("SIGHUP signal caught!\nBye.");
721                 break;
722 #endif
723         case SIGFPE:
724                 msg = _("SIGFPE signal caught!\nBye.");
725                 break;
726         case SIGSEGV:
727                 msg = _("SIGSEGV signal caught!\n"
728                           "Sorry, you have found a bug in LyX, "
729                           "hope you have not lost any data.\n"
730                           "Please read the bug-reporting instructions "
731                           "in 'Help->Introduction' and send us a bug report, "
732                           "if necessary. Thanks!\nBye.");
733                 break;
734         case SIGINT:
735         case SIGTERM:
736                 // no comments
737                 break;
738         }
739
740         if (!msg.empty()) {
741                 lyxerr << "\nlyx: " << msg << endl;
742                 // try to make a GUI message
743                 Alert::error(_("LyX crashed!"), msg, true);
744         }
745
746         // Deinstall the signal handlers
747 #ifdef SIGHUP
748         signal(SIGHUP, SIG_DFL);
749 #endif
750         signal(SIGINT, SIG_DFL);
751         signal(SIGFPE, SIG_DFL);
752         signal(SIGSEGV, SIG_DFL);
753         signal(SIGTERM, SIG_DFL);
754
755 #ifdef SIGHUP
756         if (err_sig == SIGSEGV ||
757                 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
758 #else
759         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
760 #endif
761 #ifdef _MSC_VER
762                 // with abort() it crashes again.
763                 exit(err_sig);
764 #else
765                 abort();
766 #endif
767         }
768
769         exit(0);
770 }
771
772 }
773
774
775 void LyX::printError(ErrorItem const & ei)
776 {
777         docstring tmp = _("LyX: ") + ei.error + char_type(':')
778                 + ei.description;
779         cerr << to_utf8(tmp) << endl;
780 }
781
782 #if defined (USE_MACOSX_PACKAGING)
783 namespace {
784         // Unexposed--extract an environment variable name from its NAME=VALUE
785         // representation
786         std::string varname(const char* line)
787         {
788                 size_t nameLen = strcspn(line, "=");
789                 if (nameLen == strlen(line)) {
790                         return std::string();
791                 } else {
792                         return std::string(line, nameLen);
793                 }
794         }
795 } // namespace
796
797 void cleanDuplicateEnvVars()
798 {
799         std::set<std::string> seen;
800         std::set<std::string> dupes;
801
802         // Create a list of the environment variables that appear more than once
803         for (char **read = *_NSGetEnviron(); *read; read++) {
804                 std::string name = varname(*read);
805                 if (name.size() == 0) {
806                         continue;
807                 }
808                 if (seen.find(name) != seen.end()) {
809                         dupes.insert(name);
810                 } else {
811                         seen.insert(name);
812                 }
813         }
814
815         // Loop over the list of duplicated variables
816         std::set<std::string>::iterator dupe = dupes.begin();
817         std::set<std::string>::iterator const dend = dupes.end();
818         for (; dupe != dend; ++dupe) {
819                 const char *name = (*dupe).c_str();
820                 char *val = getenv(name);
821                 if (val != NULL) {
822                         LYXERR(Debug::INIT, "Duplicate environment variable: " << name);
823                         // unsetenv removes *all* instances of the variable from the environment
824                         unsetenv(name);
825
826                         // replace with the value from getenv (in practice appears to be the
827                         // first value in the list)
828                         setenv(name, val, 0);
829                 }
830         }
831 }
832 #endif
833
834
835 static void initTemplatePath()
836 {
837         FileName const package_template_path =
838                 FileName(addName(package().system_support().absFileName(), "templates"));
839
840         if (lyxrc.template_path.empty()) {
841                 lyxrc.template_path = package_template_path.absFileName();
842         }
843 #if defined (USE_MACOSX_PACKAGING)
844         FileName const user_template_path =
845                 FileName(addName(package().user_support().absFileName(), "templates"));
846
847         if (package_template_path != FileName(lyxrc.template_path) &&
848                 user_template_path != FileName(lyxrc.template_path))
849         {
850                 return;
851         }
852         FileName const user_template_link =
853                 FileName(addName(user_template_path.absFileName(),"SystemTemplates"));
854         if (user_template_link.isSymLink() && !equivalent(user_template_link, package_template_path)) {
855                 user_template_link.removeFile();
856         }
857         if (!user_template_link.exists()) {
858                 if (!package_template_path.link(user_template_link)) {
859                         FileName const user_support = package().user_support();
860                         if (user_support.exists() && user_support.isDirectory()) {
861                                 LYXERR(Debug::INIT, "Cannot create symlink " + user_template_link.absFileName());
862                                 lyxrc.template_path = package_template_path.absFileName();
863                         }
864                         return;
865                 }
866                 LYXERR(Debug::INIT, "Symlink \"" << user_template_link.absFileName() << "\" created.");
867         }
868         lyxrc.template_path = user_template_path.absFileName();
869 #endif
870 }
871
872
873 bool LyX::init()
874 {
875 #ifdef SIGHUP
876         signal(SIGHUP, error_handler);
877 #endif
878         signal(SIGFPE, error_handler);
879         signal(SIGSEGV, error_handler);
880         signal(SIGINT, error_handler);
881         signal(SIGTERM, error_handler);
882         // SIGPIPE can be safely ignored.
883
884 #if defined (USE_MACOSX_PACKAGING)
885         cleanDuplicateEnvVars();
886 #endif
887
888         lyxrc.tempdir_path = package().temp_dir().absFileName();
889         lyxrc.document_path = package().document_dir().absFileName();
890
891         if (lyxrc.example_path.empty()) {
892                 lyxrc.example_path = addPath(package().system_support().absFileName(),
893                                               "examples");
894         }
895         initTemplatePath();
896
897         // init LyXDir environment variable
898         string const lyx_dir = package().lyx_dir().absFileName();
899         LYXERR(Debug::INIT, "Setting LyXDir... to \"" << lyx_dir << "\"");
900         if (!setEnv("LyXDir", lyx_dir))
901                 LYXERR(Debug::INIT, "\t... failed!");
902
903         if (package().explicit_user_support() && getEnv(LYX_USERDIR_VER).empty()) {
904                 // -userdir was given on the command line.
905                 // Make it available to child processes, otherwise tex2lyx
906                 // would not find all layout files, and other converters might
907                 // use it as well.
908                 string const user_dir = package().user_support().absFileName();
909                 LYXERR(Debug::INIT, "Setting " LYX_USERDIR_VER "... to \""
910                                     << user_dir << '"');
911                 if (!setEnv(LYX_USERDIR_VER, user_dir))
912                         LYXERR(Debug::INIT, "\t... failed!");
913         }
914
915         //
916         // Read configuration files
917         //
918
919         // This one may have been distributed along with LyX.
920         if (!readRcFile("lyxrc.dist"))
921                 return false;
922
923         // Set the PATH correctly.
924 #if !defined (USE_POSIX_PACKAGING) && !defined (USE_HAIKU_PACKAGING)
925         // Add the directory containing the LyX executable to the path
926         // so that LyX can find things like tex2lyx.
927         if (package().build_support().empty())
928                 prependEnvPath("PATH", package().binary_dir().absFileName());
929 #endif
930         {
931                 // Add the directory containing the dt2dv and dv2dt executables to the path
932                 FileName dtldir;
933                 if (!package().build_support().empty()) {
934                         // dtl executables should be in the same dir ar tex2lyx
935                         dtldir = package().binary_dir();
936                 }
937                 else {
938                         dtldir = FileName(addName(package().system_support().absFileName(), "extratools"));
939                 }
940 #if defined(_WIN32)
941                 string dtlexe = "dt2dv.exe";
942 #else
943                 string dtlexe = "dt2dv";
944 #endif
945                 FileName const dt2dv = FileName(addName(dtldir.absFileName(), dtlexe));
946                 if (dt2dv.exists())
947                         prependEnvPath("PATH", dtldir.absFileName());
948         }
949         if (!lyxrc.path_prefix.empty())
950                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
951
952         // Check that user LyX directory is ok.
953         {
954                 string const lock_file = package().getConfigureLockName();
955                 int fd = fileLock(lock_file.c_str());
956
957                 if (queryUserLyXDir(package().explicit_user_support())) {
958                         package().reconfigureUserLyXDir("");
959                         // Now the user directory is present on first start.
960                         initTemplatePath();
961                 }
962                 fileUnlock(fd, lock_file.c_str());
963         }
964
965         if (!use_gui) {
966                 // No need for a splash when there is no GUI
967                 first_start = false;
968                 // Default is to overwrite the main file during export, unless
969                 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
970                 if (force_overwrite == UNSPECIFIED) {
971                         string const what = getEnv("LYX_FORCE_OVERWRITE");
972                         if (what == "all")
973                                 force_overwrite = ALL_FILES;
974                         else if (what == "none")
975                                 force_overwrite = NO_FILES;
976                         else
977                                 force_overwrite = MAIN_FILE;
978                 }
979         }
980
981         // This one is generated in user_support directory by lib/configure.py.
982         if (!readRcFile("lyxrc.defaults"))
983                 return false;
984
985         // Query the OS to know what formats are viewed natively
986         theFormats().setAutoOpen();
987
988         // Read lyxrc.dist again to be able to override viewer auto-detection.
989         readRcFile("lyxrc.dist");
990
991         system_lyxrc = lyxrc;
992         theSystemFormats() = theFormats();
993         pimpl_->system_converters_ = pimpl_->converters_;
994         pimpl_->system_movers_ = pimpl_->movers_;
995         system_lcolor = lcolor;
996
997         // This one is edited through the preferences dialog.
998         if (!readRcFile("preferences", true))
999                 return false;
1000
1001         // The language may have been set to someting useful through prefs
1002         setLocale();
1003
1004         if (!readEncodingsFile("encodings", "unicodesymbols"))
1005                 return false;
1006         if (!readLanguagesFile("languages"))
1007                 return false;
1008
1009         LYXERR(Debug::INIT, "Reading layouts...");
1010         // Load the layouts
1011         LayoutFileList::get().read();
1012         //... the modules
1013         theModuleList.read();
1014         //... and the cite engines
1015         theCiteEnginesList.read();
1016
1017         // read keymap and ui files in batch mode as well
1018         // because InsetInfo needs to know these to produce
1019         // the correct output
1020
1021         // Set up command definitions
1022         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
1023
1024         // FIXME
1025         // Set up bindings
1026         pimpl_->toplevel_keymap_.read("site");
1027         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
1028         // load user bind file user.bind
1029         pimpl_->toplevel_keymap_.read("user", nullptr, KeyMap::MissingOK);
1030
1031         if (lyxerr.debugging(Debug::LYXRC))
1032                 lyxrc.print();
1033
1034         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
1035         // Prepend path prefix a second time to take the user preferences into a account
1036         if (!lyxrc.path_prefix.empty())
1037                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
1038
1039         FileName const document_path(lyxrc.document_path);
1040         if (document_path.exists() && document_path.isDirectory())
1041                 package().document_dir() = document_path;
1042
1043         package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
1044         if (package().temp_dir().empty()) {
1045                 Alert::error(_("Could not create temporary directory"),
1046                              bformat(_("Could not create a temporary directory in\n"
1047                                                        "\"%1$s\"\n"
1048                                                            "Make sure that this path exists and is writable and try again."),
1049                                      from_utf8(lyxrc.tempdir_path)));
1050                 // createLyXTmpDir() tries sufficiently hard to create a
1051                 // usable temp dir, so the probability to come here is
1052                 // close to zero. We therefore don't try to overcome this
1053                 // problem with e.g. asking the user for a new path and
1054                 // trying again but simply exit.
1055                 return false;
1056         }
1057
1058         LYXERR(Debug::INIT, "LyX tmp dir: `"
1059                             << package().temp_dir().absFileName() << '\'');
1060
1061         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
1062         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
1063
1064         // This must happen after package initialization and after lyxrc is
1065         // read, therefore it can't be done by a static object.
1066         ConverterCache::init();
1067
1068         return true;
1069 }
1070
1071
1072 void emergencyCleanup()
1073 {
1074         // what to do about tmpfiles is non-obvious. we would
1075         // like to delete any we find, but our lyxdir might
1076         // contain documents etc. which might be helpful on
1077         // a crash
1078
1079         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
1080         if (use_gui) {
1081                 if (singleton_->pimpl_->lyx_server_)
1082                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
1083                 singleton_->pimpl_->lyx_server_.reset();
1084                 singleton_->pimpl_->lyx_socket_.reset();
1085         }
1086 }
1087
1088
1089 bool LyX::queryUserLyXDir(bool explicit_userdir)
1090 {
1091         // Does user directory exist?
1092         FileName const sup = package().user_support();
1093         if (sup.exists() && sup.isDirectory()) {
1094                 first_start = false;
1095
1096                 return configFileNeedsUpdate("lyxrc.defaults")
1097                         || configFileNeedsUpdate("lyxmodules.lst")
1098                         || configFileNeedsUpdate("textclass.lst")
1099                         || configFileNeedsUpdate("packages.lst")
1100                         || configFileNeedsUpdate("lyxciteengines.lst")
1101                         || configFileNeedsUpdate("xtemplates.lst");
1102         }
1103
1104         first_start = !explicit_userdir;
1105
1106         // If the user specified explicitly a directory, ask whether
1107         // to create it. If the user says "no", then exit.
1108         if (explicit_userdir &&
1109             Alert::prompt(
1110                     _("Missing user LyX directory"),
1111                     bformat(_("You have specified a non-existent user "
1112                                            "LyX directory, %1$s.\n"
1113                                            "It is needed to keep your own configuration."),
1114                             from_utf8(package().user_support().absFileName())),
1115                     1, 0,
1116                     _("&Create directory"),
1117                     _("&Exit LyX"))) {
1118                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
1119                 earlyExit(EXIT_FAILURE);
1120         }
1121
1122         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
1123                           from_utf8(sup.absFileName()))) << endl;
1124
1125         if (!sup.createDirectory(0755)) {
1126                 // Failed, so let's exit.
1127                 lyxerr << to_utf8(_("Failed to create directory. Perhaps wrong -userdir command-line option?\nExiting."))
1128                        << endl;
1129                 earlyExit(EXIT_FAILURE);
1130         }
1131
1132         return true;
1133 }
1134
1135
1136 bool LyX::readRcFile(string const & name, bool check_format)
1137 {
1138         LYXERR(Debug::INIT, "About to read " << name << "... ");
1139
1140         FileName const lyxrc_path = libFileSearch(string(), name);
1141         if (lyxrc_path.empty()) {
1142                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
1143                 // FIXME
1144                 // This was the previous logic, but can it be right??
1145                 return true;
1146         }
1147         LYXERR(Debug::INIT, "Found in " << lyxrc_path);
1148         bool const success = lyxrc.read(lyxrc_path, check_format);
1149         if (!success)
1150                 showFileError(name);
1151         return success;
1152 }
1153
1154 // Read the languages file `name'
1155 bool LyX::readLanguagesFile(string const & name)
1156 {
1157         LYXERR(Debug::INIT, "About to read " << name << "...");
1158
1159         FileName const lang_path = libFileSearch(string(), name);
1160         if (lang_path.empty()) {
1161                 showFileError(name);
1162                 return false;
1163         }
1164         languages.read(lang_path);
1165         return true;
1166 }
1167
1168
1169 // Read the encodings file `name'
1170 bool LyX::readEncodingsFile(string const & enc_name,
1171                             string const & symbols_name)
1172 {
1173         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
1174                             << symbols_name << "...");
1175
1176         FileName const symbols_path = libFileSearch(string(), symbols_name);
1177         if (symbols_path.empty()) {
1178                 showFileError(symbols_name);
1179                 return false;
1180         }
1181
1182         FileName const enc_path = libFileSearch(string(), enc_name);
1183         if (enc_path.empty()) {
1184                 showFileError(enc_name);
1185                 return false;
1186         }
1187         encodings.read(enc_path, symbols_path);
1188         return true;
1189 }
1190
1191
1192 namespace {
1193
1194 /// return the the number of arguments consumed
1195 typedef function<int(string const &, string const &, string &)> cmd_helper;
1196
1197 int parse_dbg(string const & arg, string const &, string &)
1198 {
1199         if (arg.empty()) {
1200                 cout << to_utf8(_("List of supported debug flags:")) << endl;
1201                 Debug::showTags(cout);
1202                 exit(0);
1203         }
1204         string bad = Debug::badValue(arg);
1205         if (bad.empty()) {
1206                 lyxerr.setLevel(Debug::value(arg));
1207                 Debug::showLevel(lyxerr, lyxerr.level());
1208         } else {
1209                 cout << to_utf8(bformat(_("Bad debug value `%1$s'. Exiting."),
1210                                         from_utf8(bad))) << endl;
1211                 exit(1);
1212         }
1213         return 1;
1214 }
1215
1216
1217 int parse_help(string const &, string const &, string &)
1218 {
1219         cout <<
1220                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1221                   "Command line switches (case sensitive):\n"
1222                   "\t-help              summarize LyX usage\n"
1223                   "\t-userdir dir       set user directory to dir\n"
1224                   "\t-sysdir dir        set system directory to dir\n"
1225                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1226                   "\t-dbg feature[,feature]...\n"
1227                   "                  select the features to debug.\n"
1228                   "                  Type `lyx -dbg' to see the list of features\n"
1229                   "\t-x [--execute] command\n"
1230                   "                  where command is a lyx command.\n"
1231                   "\t-e [--export] fmt\n"
1232                   "                  where fmt is the export format of choice. Look in\n"
1233                   "                  Tools->Preferences->File Handling->File Formats->Short Name\n"
1234                   "                  to see which parameter (which differs from the format name\n"
1235                   "                  in the File->Export menu) should be passed. To export to\n"
1236                   "                  the document's default output format, use 'default'.\n"
1237                   "                  Note that the order of -e and -x switches matters.\n"
1238                   "\t-E [--export-to] fmt filename\n"
1239                   "                  where fmt is the export format of choice (see --export),\n"
1240                   "                  and filename is the destination filename.\n"
1241                   "\t-i [--import] fmt file.xxx\n"
1242                   "                  where fmt is the import format of choice\n"
1243                   "                  and file.xxx is the file to be imported.\n"
1244                   "\t-f [--force-overwrite] what\n"
1245                   "                  where what is either `all', `main' or `none',\n"
1246                   "                  specifying whether all files, main file only, or no files,\n"
1247                   "                  respectively, are to be overwritten during a batch export.\n"
1248                   "                  Anything else is equivalent to `all', but is not consumed.\n"
1249                   "\t--ignore-error-message which\n"
1250                   "                  allows you to ignore specific LaTeX error messages.\n"
1251                   "                  Do not use for final documents! Currently supported values:\n"
1252                   "                  * missing_glyphs: Fontspec `missing glyphs' error.\n"
1253                   "\t-n [--no-remote]\n"
1254                   "                  open documents in a new instance\n"
1255                   "\t-r [--remote]\n"
1256                   "                  open documents in an already running instance\n"
1257                   "                  (a working lyxpipe is needed)\n"
1258                   "\t-v [--verbose]\n"
1259                   "                  report on terminal about spawned commands.\n"
1260                   "\t-batch    execute commands without launching GUI and exit.\n"
1261                   "\t-version  summarize version and build info\n"
1262                                "Check the LyX man page for more details.")) << endl;
1263         exit(0);
1264         return 0;
1265 }
1266
1267
1268 int parse_version(string const &, string const &, string &)
1269 {
1270         cout << "LyX " << lyx_version
1271                << " (" << lyx_release_date << ")" << endl;
1272         if (string(lyx_git_commit_hash) != "none")
1273                 cout << to_utf8(_("  Git commit hash "))
1274                      << string(lyx_git_commit_hash).substr(0,8) << endl;
1275         cout << lyx_version_info << endl;
1276         exit(0);
1277         return 0;
1278 }
1279
1280
1281 int parse_sysdir(string const & arg, string const &, string &)
1282 {
1283         if (arg.empty()) {
1284                 Alert::error(_("No system directory"),
1285                         _("Missing directory for -sysdir switch"));
1286                 exit(1);
1287         }
1288         cl_system_support = arg;
1289         return 1;
1290 }
1291
1292
1293 int parse_userdir(string const & arg, string const &, string &)
1294 {
1295         if (arg.empty()) {
1296                 Alert::error(_("No user directory"),
1297                         _("Missing directory for -userdir switch"));
1298                 exit(1);
1299         }
1300         cl_user_support = arg;
1301         return 1;
1302 }
1303
1304
1305 int parse_execute(string const & arg, string const &, string & batch)
1306 {
1307         if (arg.empty()) {
1308                 Alert::error(_("Incomplete command"),
1309                         _("Missing command string after --execute switch"));
1310                 exit(1);
1311         }
1312         batch = arg;
1313         return 1;
1314 }
1315
1316
1317 int parse_export_to(string const & type, string const & output_file, string & batch)
1318 {
1319         if (type.empty()) {
1320                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1321                                          "--export-to switch")) << endl;
1322                 exit(1);
1323         }
1324         if (output_file.empty()) {
1325                 lyxerr << to_utf8(_("Missing destination filename after "
1326                                          "--export-to switch")) << endl;
1327                 exit(1);
1328         }
1329         batch = "buffer-export " + type + " " + output_file;
1330         use_gui = false;
1331         return 2;
1332 }
1333
1334
1335 int parse_export(string const & type, string const &, string & batch)
1336 {
1337         if (type.empty()) {
1338                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1339                                          "--export switch")) << endl;
1340                 exit(1);
1341         }
1342         batch = "buffer-export " + type;
1343         use_gui = false;
1344         return 1;
1345 }
1346
1347
1348 int parse_import(string const & type, string const & file, string & batch)
1349 {
1350         if (type.empty()) {
1351                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1352                                          "--import switch")) << endl;
1353                 exit(1);
1354         }
1355         if (file.empty()) {
1356                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1357                 exit(1);
1358         }
1359         batch = "buffer-import " + type + ' ' + file;
1360         return 2;
1361 }
1362
1363
1364 int parse_geometry(string const & arg1, string const &, string &)
1365 {
1366         geometryArg = arg1;
1367         // don't remove "-geometry", it will be pruned out later in the
1368         // frontend if need be.
1369         return -1;
1370 }
1371
1372
1373 int parse_batch(string const &, string const &, string &)
1374 {
1375         use_gui = false;
1376         return 0;
1377 }
1378
1379
1380 int parse_noremote(string const &, string const &, string &)
1381 {
1382         run_mode = NEW_INSTANCE;
1383         return 0;
1384 }
1385
1386
1387 int parse_remote(string const &, string const &, string &)
1388 {
1389         run_mode = USE_REMOTE;
1390         return 0;
1391 }
1392
1393
1394 int parse_verbose(string const &, string const &, string &)
1395 {
1396         verbose = true;
1397         return 0;
1398 }
1399
1400
1401 int parse_ignore_error_message(string const & arg1, string const &, string &)
1402 {
1403         if (arg1 == "missing_glyphs") {
1404                 ignore_missing_glyphs = true;
1405                 return 1;
1406         }
1407         return 0;
1408 }
1409
1410
1411 int parse_force(string const & arg, string const &, string &)
1412 {
1413         if (arg == "all") {
1414                 force_overwrite = ALL_FILES;
1415                 return 1;
1416         } else if (arg == "main") {
1417                 force_overwrite = MAIN_FILE;
1418                 return 1;
1419         } else if (arg == "none") {
1420                 force_overwrite = NO_FILES;
1421                 return 1;
1422         }
1423         force_overwrite = ALL_FILES;
1424         return 0;
1425 }
1426
1427
1428 } // namespace
1429
1430
1431 void LyX::easyParse(int & argc, char * argv[])
1432 {
1433         map<string, cmd_helper> cmdmap;
1434
1435         cmdmap["-dbg"] = parse_dbg;
1436         cmdmap["-help"] = parse_help;
1437         cmdmap["--help"] = parse_help;
1438         cmdmap["-version"] = parse_version;
1439         cmdmap["--version"] = parse_version;
1440         cmdmap["-sysdir"] = parse_sysdir;
1441         cmdmap["-userdir"] = parse_userdir;
1442         cmdmap["-x"] = parse_execute;
1443         cmdmap["--execute"] = parse_execute;
1444         cmdmap["-e"] = parse_export;
1445         cmdmap["--export"] = parse_export;
1446         cmdmap["-E"] = parse_export_to;
1447         cmdmap["--export-to"] = parse_export_to;
1448         cmdmap["-i"] = parse_import;
1449         cmdmap["--import"] = parse_import;
1450         cmdmap["-geometry"] = parse_geometry;
1451         cmdmap["-batch"] = parse_batch;
1452         cmdmap["-f"] = parse_force;
1453         cmdmap["--force-overwrite"] = parse_force;
1454         cmdmap["-n"] = parse_noremote;
1455         cmdmap["--no-remote"] = parse_noremote;
1456         cmdmap["-r"] = parse_remote;
1457         cmdmap["--remote"] = parse_remote;
1458         cmdmap["-v"] = parse_verbose;
1459         cmdmap["--verbose"] = parse_verbose;
1460         cmdmap["--ignore-error-message"] = parse_ignore_error_message;
1461
1462         for (int i = 1; i < argc; ++i) {
1463                 map<string, cmd_helper>::const_iterator it
1464                         = cmdmap.find(argv[i]);
1465
1466                 // don't complain if not found - may be parsed later
1467                 if (it == cmdmap.end())
1468                         continue;
1469
1470                 string const arg =
1471                         (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1472                 string const arg2 =
1473                         (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1474
1475                 string batch;
1476                 int const remove = 1 + it->second(arg, arg2, batch);
1477                 if (!batch.empty())
1478                         pimpl_->batch_commands.push_back(batch);
1479
1480                 // Now, remove used arguments by shifting
1481                 // the following ones remove places down.
1482                 if (remove > 0) {
1483                         os::remove_internal_args(i, remove);
1484                         argc -= remove;
1485                         for (int j = i; j < argc; ++j)
1486                                 argv[j] = argv[j + remove];
1487                         --i;
1488                 }
1489         }
1490 }
1491
1492
1493 FuncStatus getStatus(FuncRequest const & action)
1494 {
1495         LAPPERR(theApp());
1496         return theApp()->getStatus(action);
1497 }
1498
1499
1500 DispatchResult const & dispatch(FuncRequest const & action)
1501 {
1502         LAPPERR(theApp());
1503         return theApp()->dispatch(action);
1504 }
1505
1506
1507 void dispatch(FuncRequest const & action, DispatchResult & dr)
1508 {
1509         LAPPERR(theApp());
1510         theApp()->dispatch(action, dr);
1511 }
1512
1513
1514 vector<string> & theFilesToLoad()
1515 {
1516         LAPPERR(singleton_);
1517         return singleton_->pimpl_->files_to_load_;
1518 }
1519
1520
1521 BufferList & theBufferList()
1522 {
1523         LAPPERR(singleton_);
1524         return singleton_->pimpl_->buffer_list_;
1525 }
1526
1527
1528 Server & theServer()
1529 {
1530         // FIXME: this should not be use_gui dependent
1531         LWARNIF(use_gui);
1532         LAPPERR(singleton_);
1533         return *singleton_->pimpl_->lyx_server_;
1534 }
1535
1536
1537 ServerSocket & theServerSocket()
1538 {
1539         // FIXME: this should not be use_gui dependent
1540         LWARNIF(use_gui);
1541         LAPPERR(singleton_);
1542         return *singleton_->pimpl_->lyx_socket_;
1543 }
1544
1545
1546 KeyMap & theTopLevelKeymap()
1547 {
1548         LAPPERR(singleton_);
1549         return singleton_->pimpl_->toplevel_keymap_;
1550 }
1551
1552
1553 Formats & theFormats()
1554 {
1555         LAPPERR(singleton_);
1556         return singleton_->pimpl_->formats_;
1557 }
1558
1559
1560 Formats & theSystemFormats()
1561 {
1562         LAPPERR(singleton_);
1563         return singleton_->pimpl_->system_formats_;
1564 }
1565
1566
1567 Converters & theConverters()
1568 {
1569         LAPPERR(singleton_);
1570         return singleton_->pimpl_->converters_;
1571 }
1572
1573
1574 Converters & theSystemConverters()
1575 {
1576         LAPPERR(singleton_);
1577         return singleton_->pimpl_->system_converters_;
1578 }
1579
1580
1581 Movers & theMovers()
1582 {
1583         LAPPERR(singleton_);
1584         return singleton_->pimpl_->movers_;
1585 }
1586
1587
1588 Mover const & getMover(string  const & fmt)
1589 {
1590         LAPPERR(singleton_);
1591         return singleton_->pimpl_->movers_(fmt);
1592 }
1593
1594
1595 void setMover(string const & fmt, string const & command)
1596 {
1597         LAPPERR(singleton_);
1598         singleton_->pimpl_->movers_.set(fmt, command);
1599 }
1600
1601
1602 Movers & theSystemMovers()
1603 {
1604         LAPPERR(singleton_);
1605         return singleton_->pimpl_->system_movers_;
1606 }
1607
1608
1609 Messages const & getMessages(string const & language)
1610 {
1611         LAPPERR(singleton_);
1612         return singleton_->messages(language);
1613 }
1614
1615
1616 Messages const & getGuiMessages()
1617 {
1618         LAPPERR(singleton_);
1619         return singleton_->messages(Messages::guiLanguage());
1620 }
1621
1622
1623 Session & theSession()
1624 {
1625         LAPPERR(singleton_);
1626         return *singleton_->pimpl_->session_.get();
1627 }
1628
1629
1630 LaTeXFonts & theLaTeXFonts()
1631 {
1632         LAPPERR(singleton_);
1633         if (!singleton_->pimpl_->latexfonts_)
1634                 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1635         return *singleton_->pimpl_->latexfonts_;
1636 }
1637
1638
1639 CmdDef & theTopLevelCmdDef()
1640 {
1641         LAPPERR(singleton_);
1642         return singleton_->pimpl_->toplevel_cmddef_;
1643 }
1644
1645
1646 SpellChecker * theSpellChecker()
1647 {
1648         if (!singleton_->pimpl_->spell_checker_)
1649                 setSpellChecker();
1650         return singleton_->pimpl_->spell_checker_;
1651 }
1652
1653
1654 void setSpellChecker()
1655 {
1656         SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1657                 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1658
1659         if (lyxrc.spellchecker == "native") {
1660 #if defined(USE_MACOSX_PACKAGING)
1661                 if (!singleton_->pimpl_->apple_spell_checker_)
1662                         singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1663                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1664 #else
1665                 singleton_->pimpl_->spell_checker_ = nullptr;
1666 #endif
1667         } else if (lyxrc.spellchecker == "aspell") {
1668 #if defined(USE_ASPELL)
1669                 if (!singleton_->pimpl_->aspell_checker_)
1670                         singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1671                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1672 #else
1673                 singleton_->pimpl_->spell_checker_ = nullptr;
1674 #endif
1675         } else if (lyxrc.spellchecker == "enchant") {
1676 #if defined(USE_ENCHANT)
1677                 if (!singleton_->pimpl_->enchant_checker_)
1678                         singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1679                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1680 #else
1681                 singleton_->pimpl_->spell_checker_ = nullptr;
1682 #endif
1683         } else if (lyxrc.spellchecker == "hunspell") {
1684 #if defined(USE_HUNSPELL)
1685                 if (!singleton_->pimpl_->hunspell_checker_)
1686                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1687                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1688 #else
1689                 singleton_->pimpl_->spell_checker_ = nullptr;
1690 #endif
1691         } else {
1692                 singleton_->pimpl_->spell_checker_ = nullptr;
1693         }
1694         if (singleton_->pimpl_->spell_checker_) {
1695                 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1696                 singleton_->pimpl_->spell_checker_->advanceChangeNumber();
1697         }
1698 }
1699
1700 } // namespace lyx