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