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