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