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