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