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