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