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