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