]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
Pure HTML output for math macros.
[lyx.git] / src / LyX.cpp
1 /**
2  * \file LyX.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16 #include <version.h>
17
18 #include "LyX.h"
19
20 #include "AspellChecker.h"
21 #include "Buffer.h"
22 #include "BufferList.h"
23 #include "CmdDef.h"
24 #include "ColorSet.h"
25 #include "ConverterCache.h"
26 #include "Converter.h"
27 #include "CutAndPaste.h"
28 #include "EnchantChecker.h"
29 #include "Encoding.h"
30 #include "ErrorList.h"
31 #include "Format.h"
32 #include "FuncStatus.h"
33 #include "HunspellChecker.h"
34 #include "KeyMap.h"
35 #include "Language.h"
36 #include "LayoutFile.h"
37 #include "Lexer.h"
38 #include "LyX.h"
39 #include "LyXAction.h"
40 #include "LyXRC.h"
41 #include "ModuleList.h"
42 #include "Mover.h"
43 #include "Server.h"
44 #include "ServerSocket.h"
45 #include "Session.h"
46
47 #include "frontends/alert.h"
48 #include "frontends/Application.h"
49
50 #include "graphics/Previews.h"
51
52 #include "support/lassert.h"
53 #include "support/debug.h"
54 #include "support/environment.h"
55 #include "support/ExceptionMessage.h"
56 #include "support/filetools.h"
57 #include "support/gettext.h"
58 #include "support/lstrings.h"
59 #include "support/Messages.h"
60 #include "support/os.h"
61 #include "support/Package.h"
62 #include "support/Path.h"
63 #include "support/Systemcall.h"
64
65 #include <boost/bind.hpp>
66 #include <boost/scoped_ptr.hpp>
67
68 #include <algorithm>
69 #include <iostream>
70 #include <csignal>
71 #include <map>
72 #include <stdlib.h>
73 #include <string>
74 #include <vector>
75
76 using namespace std;
77 using namespace lyx::support;
78
79 namespace lyx {
80
81 namespace Alert = frontend::Alert;
82 namespace os = support::os;
83
84
85
86 // Are we using the GUI at all?  We default to true and this is changed
87 // to false when the export feature is used.
88
89 bool use_gui = true;
90
91 namespace {
92
93 // Filled with the command line arguments "foo" of "-sysdir foo" or
94 // "-userdir foo".
95 string cl_system_support;
96 string cl_user_support;
97
98 string geometryArg;
99
100 LyX * singleton_ = 0;
101
102 void showFileError(string const & error)
103 {
104         Alert::warning(_("Could not read configuration file"),
105                        bformat(_("Error while reading the configuration file\n%1$s.\n"
106                            "Please check your installation."), from_utf8(error)));
107 }
108
109
110 void reconfigureUserLyXDir()
111 {
112         string const configure_command = package().configure_command();
113
114         lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
115         PathChanger p(package().user_support());
116         Systemcall one;
117         one.startscript(Systemcall::Wait, configure_command);
118         lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
119 }
120
121 } // namespace anon
122
123 /// The main application class private implementation.
124 struct LyX::Impl
125 {
126         Impl() : spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
127         {
128                 // Set the default User Interface language as soon as possible.
129                 // The language used will be derived from the environment
130                 // variables.
131                 messages_["GUI"] = Messages();
132         }
133
134         ~Impl()
135         {
136                 delete aspell_checker_;
137                 delete enchant_checker_;
138                 delete hunspell_checker_;
139         }
140
141         ///
142         BufferList buffer_list_;
143         ///
144         KeyMap toplevel_keymap_;
145         ///
146         CmdDef toplevel_cmddef_;
147         ///
148         boost::scoped_ptr<Server> lyx_server_;
149         ///
150         boost::scoped_ptr<ServerSocket> lyx_socket_;
151         ///
152         boost::scoped_ptr<frontend::Application> application_;
153         /// lyx session, containing lastfiles, lastfilepos, and lastopened
154         boost::scoped_ptr<Session> session_;
155
156         /// Files to load at start.
157         vector<string> files_to_load_;
158
159         /// The messages translators.
160         map<string, Messages> messages_;
161
162         /// The file converters.
163         Converters converters_;
164
165         // The system converters copy after reading lyxrc.defaults.
166         Converters system_converters_;
167
168         ///
169         Movers movers_;
170         ///
171         Movers system_movers_;
172
173         /// has this user started lyx for the first time?
174         bool first_start;
175         /// the parsed command line batch command if any
176         vector<string> batch_commands;
177
178         ///
179         graphics::Previews preview_;
180         ///
181         SpellChecker * spell_checker_;
182         ///
183         SpellChecker * aspell_checker_;
184         ///
185         SpellChecker * enchant_checker_;
186         ///
187         SpellChecker * hunspell_checker_;
188 };
189
190 ///
191 frontend::Application * theApp()
192 {
193         if (singleton_)
194                 return singleton_->pimpl_->application_.get();
195         else
196                 return 0;
197 }
198
199
200 LyX::~LyX()
201 {
202         singleton_ = 0;
203         delete pimpl_;
204 }
205
206
207 void lyx_exit(int exit_code)
208 {
209         if (exit_code)
210                 // Something wrong happened so better save everything, just in
211                 // case.
212                 emergencyCleanup();
213
214 #ifndef NDEBUG
215         // Properly crash in debug mode in order to get a useful backtrace.
216         abort();
217 #endif
218
219         // In release mode, try to exit gracefully.
220         if (theApp())
221                 theApp()->exit(exit_code);
222         else
223                 exit(exit_code);
224 }
225
226
227 LyX::LyX()
228         : first_start(false)
229 {
230         singleton_ = this;
231         pimpl_ = new Impl;
232 }
233
234
235 Messages & LyX::messages(string const & language)
236 {
237         map<string, Messages>::iterator it = pimpl_->messages_.find(language);
238
239         if (it != pimpl_->messages_.end())
240                 return it->second;
241
242         pair<map<string, Messages>::iterator, bool> result =
243                         pimpl_->messages_.insert(make_pair(language, Messages(language)));
244
245         LASSERT(result.second, /**/);
246         return result.first->second;
247 }
248
249
250 void setRcGuiLanguage()
251 {
252         LASSERT(singleton_, /**/);
253         if (lyxrc.gui_language == "auto")
254                 return;
255         Language const * language = languages.getLanguage(lyxrc.gui_language);
256         if (language) {
257                 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << language->code());
258                 if (!setEnv("LANGUAGE", language->code()))
259                         LYXERR(Debug::LOCALE, "\t... failed!");
260         }
261         LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
262         if (!setEnv("LC_ALL", "en_US"))
263                 LYXERR(Debug::LOCALE, "\t... failed!");
264         Messages::init();
265         singleton_->pimpl_->messages_["GUI"] = Messages();
266 }
267
268
269 int LyX::exec(int & argc, char * argv[])
270 {
271         // Here we need to parse the command line. At least
272         // we need to parse for "-dbg" and "-help"
273         easyParse(argc, argv);
274
275         try {
276                 init_package(os::utf8_argv(0),
277                               cl_system_support, cl_user_support,
278                               top_build_dir_is_one_level_up);
279         } catch (ExceptionMessage const & message) {
280                 if (message.type_ == ErrorException) {
281                         Alert::error(message.title_, message.details_);
282                         lyx_exit(1);
283                 } else if (message.type_ == WarningException) {
284                         Alert::warning(message.title_, message.details_);
285                 }
286         }
287
288         // Reinit the messages machinery in case package() knows
289         // something interesting about the locale directory.
290         Messages::init();
291
292         if (!use_gui) {
293                 // FIXME: create a ConsoleApplication
294                 int exit_status = init(argc, argv);
295                 if (exit_status) {
296                         prepareExit();
297                         return exit_status;
298                 }
299
300                 // this is correct, since return values are inverted.
301                 exit_status = !loadFiles();
302
303                 if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
304                         prepareExit();
305                         return exit_status;
306                 }
307
308                 BufferList::iterator begin = pimpl_->buffer_list_.begin();
309
310                 bool final_success = false;
311                 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
312                         Buffer * buf = *I;
313                         if (buf != buf->masterBuffer())
314                                 continue;
315                         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
316                         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
317                         DispatchResult dr;
318                         for (; bcit != bcend; bcit++) {
319                                 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
320                                 buf->dispatch(*bcit, dr);
321                                 final_success |= !dr.error();
322                         }
323                 }
324                 prepareExit();
325                 return !final_success;
326         }
327
328         // Let the frontend parse and remove all arguments that it knows
329         pimpl_->application_.reset(createApplication(argc, argv));
330
331         // Reestablish our defaults, as Qt overwrites them
332         // after createApplication()
333         locale_init();
334
335         // Parse and remove all known arguments in the LyX singleton
336         // Give an error for all remaining ones.
337         int exit_status = init(argc, argv);
338         if (exit_status) {
339                 // Kill the application object before exiting.
340                 pimpl_->application_.reset();
341                 use_gui = false;
342                 prepareExit();
343                 return exit_status;
344         }
345  
346         // FIXME
347         /* Create a CoreApplication class that will provide the main event loop
348         * and the socket callback registering. With Qt4, only QtCore
349         * library would be needed.
350         * When this is done, a server_mode could be created and the following two
351         * line would be moved out from here.
352         */
353         // Note: socket callback must be registered after init(argc, argv)
354         // such that package().temp_dir() is properly initialized.
355         pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
356         pimpl_->lyx_socket_.reset(new ServerSocket(
357                         FileName(package().temp_dir().absFilename() + "/lyxsocket")));
358
359         // Start the real execution loop.
360         exit_status = pimpl_->application_->exec();
361
362         prepareExit();
363
364         return exit_status;
365 }
366
367
368 void LyX::prepareExit()
369 {
370         // Clear the clipboard and selection stack:
371         cap::clearCutStack();
372         cap::clearSelection();
373
374         // Write the index file of the converter cache
375         ConverterCache::get().writeIndex();
376
377         // close buffers first
378         pimpl_->buffer_list_.closeAll();
379
380         // register session changes and shutdown server and socket
381         if (use_gui) {
382                 if (pimpl_->session_)
383                         pimpl_->session_->writeFile();
384                 pimpl_->session_.reset();
385                 pimpl_->lyx_server_.reset();
386                 pimpl_->lyx_socket_.reset();
387         }
388
389         // do any other cleanup procedures now
390         if (package().temp_dir() != package().system_temp_dir()) {
391                 string const abs_tmpdir = package().temp_dir().absFilename();
392                 if (!contains(package().temp_dir().absFilename(), "lyx_tmpdir")) {
393                         docstring const msg =
394                                 bformat(_("%1$s does not appear like a LyX created temporary directory."),
395                                 from_utf8(abs_tmpdir));
396                         Alert::warning(_("Cannot remove temporary directory"), msg);
397                 } else {
398                         LYXERR(Debug::INFO, "Deleting tmp dir "
399                                 << package().temp_dir().absFilename());
400                         if (!package().temp_dir().destroyDirectory()) {
401                                 docstring const msg =
402                                         bformat(_("Unable to remove the temporary directory %1$s"),
403                                         from_utf8(package().temp_dir().absFilename()));
404                                 Alert::warning(_("Unable to remove temporary directory"), msg);
405                         }
406                 }
407         }
408
409         // Kill the application object before exiting. This avoids crashes
410         // when exiting on Linux.
411         if (pimpl_->application_)
412                 pimpl_->application_.reset();
413 }
414
415
416 void LyX::earlyExit(int status)
417 {
418         LASSERT(pimpl_->application_.get(), /**/);
419         // LyX::pimpl_::application_ is not initialised at this
420         // point so it's safe to just exit after some cleanup.
421         prepareExit();
422         exit(status);
423 }
424
425
426 int LyX::init(int & argc, char * argv[])
427 {
428         // check for any spurious extra arguments
429         // other than documents
430         for (int argi = 1; argi < argc ; ++argi) {
431                 if (argv[argi][0] == '-') {
432                         lyxerr << to_utf8(
433                                 bformat(_("Wrong command line option `%1$s'. Exiting."),
434                                 from_utf8(os::utf8_argv(argi)))) << endl;
435                         return EXIT_FAILURE;
436                 }
437         }
438
439         // Initialization of LyX (reads lyxrc and more)
440         LYXERR(Debug::INIT, "Initializing LyX::init...");
441         bool success = init();
442         LYXERR(Debug::INIT, "Initializing LyX::init...done");
443         if (!success)
444                 return EXIT_FAILURE;
445
446         // Remaining arguments are assumed to be files to load.
447         for (int argi = argc - 1; argi >= 1; --argi)
448                 pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
449
450         if (first_start) {
451                 pimpl_->files_to_load_.push_back(
452                         i18nLibFileSearch("examples", "splash.lyx").absFilename());
453         }
454
455         return EXIT_SUCCESS;
456 }
457
458
459 bool LyX::loadFiles()
460 {
461         LASSERT(!use_gui, /**/);
462         bool success = true;
463         vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
464         vector<string>::const_iterator end = pimpl_->files_to_load_.end();
465
466         for (; it != end; ++it) {
467                 // get absolute path of file and add ".lyx" to
468                 // the filename if necessary
469                 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
470                         may_not_exist);
471
472                 if (fname.empty())
473                         continue;
474
475                 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFilename(), false);
476                 if (buf->loadLyXFile(fname)) {
477                         ErrorList const & el = buf->errorList("Parse");
478                         if (!el.empty())
479                                 for_each(el.begin(), el.end(),
480                                 boost::bind(&LyX::printError, this, _1));
481                 }
482                 else {
483                         pimpl_->buffer_list_.release(buf);
484                         success = false;
485                 }
486         }
487         return success;
488 }
489
490
491 void execBatchCommands()
492 {
493         LASSERT(singleton_, /**/);
494         singleton_->execCommands();
495 }
496
497
498 void LyX::execCommands()
499 {
500         // The advantage of doing this here is that the event loop
501         // is already started. So any need for interaction will be
502         // aknowledged.
503
504         // if reconfiguration is needed.
505         while (LayoutFileList::get().empty()) {
506                 switch (Alert::prompt(
507                         _("No textclass is found"),
508                         _("LyX cannot continue because no textclass is found. "
509                                 "You can either reconfigure normally, or reconfigure using "
510                                 "default textclasses, or quit LyX."),
511                         0, 2,
512                         _("&Reconfigure"),
513                         _("&Use Default"),
514                         _("&Exit LyX")))
515                 {
516                 case 0:
517                         // regular reconfigure
518                         lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
519                         break;
520                 case 1:
521                         // reconfigure --without-latex-config
522                         lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
523                                 " --without-latex-config"));
524                         break;
525                 default:
526                         lyx::dispatch(FuncRequest(LFUN_LYX_QUIT));
527                         return;
528                 }
529         }
530         
531         // create the first main window
532         lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
533
534         if (!pimpl_->files_to_load_.empty()) {
535                 // if some files were specified at command-line we assume that the
536                 // user wants to edit *these* files and not to restore the session.
537                 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
538                         lyx::dispatch(
539                                 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
540                 }
541                 // clear this list to save a few bytes of RAM
542                 pimpl_->files_to_load_.clear();
543         } else
544                 pimpl_->application_->restoreGuiSession();
545
546         // Execute batch commands if available
547         if (pimpl_->batch_commands.empty())
548                 return;
549
550         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
551         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
552         for (; bcit != bcend; bcit++) {
553                 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
554                 lyx::dispatch(lyxaction.lookupFunc(*bcit));
555         }
556 }
557
558
559 /*
560 Signals and Windows
561 ===================
562 The SIGHUP signal does not exist on Windows and does not need to be handled.
563
564 Windows handles SIGFPE and SIGSEGV signals as expected.
565
566 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
567 cause a new thread to be spawned. This may well result in unexpected
568 behaviour by the single-threaded LyX.
569
570 SIGTERM signals will come only from another process actually sending
571 that signal using 'raise' in Windows' POSIX compatability layer. It will
572 not come from the general "terminate process" methods that everyone
573 actually uses (and which can't be trapped). Killing an app 'politely' on
574 Windows involves first sending a WM_CLOSE message, something that is
575 caught already by the Qt frontend.
576
577 For more information see:
578
579 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
580 ...signals are mostly useless on Windows for a variety of reasons that are
581 Windows specific...
582
583 'UNIX Application Migration Guide, Chapter 9'
584 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
585
586 'How To Terminate an Application "Cleanly" in Win32'
587 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
588 */
589 extern "C" {
590
591 static void error_handler(int err_sig)
592 {
593         // Throw away any signals other than the first one received.
594         static sig_atomic_t handling_error = false;
595         if (handling_error)
596                 return;
597         handling_error = true;
598
599         // We have received a signal indicating a fatal error, so
600         // try and save the data ASAP.
601         emergencyCleanup();
602
603         // These lyxerr calls may or may not work:
604
605         // Signals are asynchronous, so the main program may be in a very
606         // fragile state when a signal is processed and thus while a signal
607         // handler function executes.
608         // In general, therefore, we should avoid performing any
609         // I/O operations or calling most library and system functions from
610         // signal handlers.
611
612         // This shouldn't matter here, however, as we've already invoked
613         // emergencyCleanup.
614         switch (err_sig) {
615 #ifdef SIGHUP
616         case SIGHUP:
617                 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
618                 break;
619 #endif
620         case SIGFPE:
621                 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
622                 break;
623         case SIGSEGV:
624                 lyxerr << "\nlyx: SIGSEGV signal caught\n"
625                           "Sorry, you have found a bug in LyX. "
626                           "Please read the bug-reporting instructions "
627                           "in Help->Introduction and send us a bug report, "
628                           "if necessary. Thanks !\nBye." << endl;
629                 break;
630         case SIGINT:
631         case SIGTERM:
632                 // no comments
633                 break;
634         }
635
636         // Deinstall the signal handlers
637 #ifdef SIGHUP
638         signal(SIGHUP, SIG_DFL);
639 #endif
640         signal(SIGINT, SIG_DFL);
641         signal(SIGFPE, SIG_DFL);
642         signal(SIGSEGV, SIG_DFL);
643         signal(SIGTERM, SIG_DFL);
644
645 #ifdef SIGHUP
646         if (err_sig == SIGSEGV ||
647             (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
648 #else
649         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
650 #endif
651                 abort();
652         exit(0);
653 }
654
655 }
656
657
658 void LyX::printError(ErrorItem const & ei)
659 {
660         docstring tmp = _("LyX: ") + ei.error + char_type(':')
661                 + ei.description;
662         cerr << to_utf8(tmp) << endl;
663 }
664
665
666 bool LyX::init()
667 {
668 #ifdef SIGHUP
669         signal(SIGHUP, error_handler);
670 #endif
671         signal(SIGFPE, error_handler);
672         signal(SIGSEGV, error_handler);
673         signal(SIGINT, error_handler);
674         signal(SIGTERM, error_handler);
675         // SIGPIPE can be safely ignored.
676
677         lyxrc.tempdir_path = package().temp_dir().absFilename();
678         lyxrc.document_path = package().document_dir().absFilename();
679
680         if (lyxrc.example_path.empty()) {
681                 lyxrc.example_path = addPath(package().system_support().absFilename(),
682                                               "examples");
683         }
684         if (lyxrc.template_path.empty()) {
685                 lyxrc.template_path = addPath(package().system_support().absFilename(),
686                                               "templates");
687         }
688
689         //
690         // Read configuration files
691         //
692
693         // This one may have been distributed along with LyX.
694         if (!readRcFile("lyxrc.dist"))
695                 return false;
696
697         // Set the language defined by the distributor.
698         setRcGuiLanguage();
699
700         // Set the PATH correctly.
701 #if !defined (USE_POSIX_PACKAGING)
702         // Add the directory containing the LyX executable to the path
703         // so that LyX can find things like tex2lyx.
704         if (package().build_support().empty())
705                 prependEnvPath("PATH", package().binary_dir().absFilename());
706 #endif
707         if (!lyxrc.path_prefix.empty())
708                 prependEnvPath("PATH", lyxrc.path_prefix);
709
710         // Check that user LyX directory is ok.
711         if (queryUserLyXDir(package().explicit_user_support()))
712                 reconfigureUserLyXDir();
713
714         // no need for a splash when there is no GUI
715         if (!use_gui) {
716                 first_start = false;
717         }
718
719         // This one is generated in user_support directory by lib/configure.py.
720         if (!readRcFile("lyxrc.defaults"))
721                 return false;
722
723         // Query the OS to know what formats are viewed natively
724         formats.setAutoOpen();
725
726         // Read lyxrc.dist again to be able to override viewer auto-detection.
727         readRcFile("lyxrc.dist");
728
729         // Set again the language defined by the distributor.
730         setRcGuiLanguage();
731
732         system_lyxrc = lyxrc;
733         system_formats = formats;
734         pimpl_->system_converters_ = pimpl_->converters_;
735         pimpl_->system_movers_ = pimpl_->movers_;
736         system_lcolor = lcolor;
737
738         // This one is edited through the preferences dialog.
739         if (!readRcFile("preferences"))
740                 return false;
741
742         if (!readEncodingsFile("encodings", "unicodesymbols"))
743                 return false;
744         if (!readLanguagesFile("languages"))
745                 return false;
746
747         // Set the language defined by the user.
748         setRcGuiLanguage();
749
750         // Load the layouts
751         LYXERR(Debug::INIT, "Reading layouts...");
752         if (!LyXSetStyle())
753                 return false;
754         //...and the modules
755         theModuleList.read();
756
757         // read keymap and ui files in batch mode as well
758         // because InsetInfo needs to know these to produce
759         // the correct output
760
761         // Set up command definitions
762         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
763
764         // Set up bindings
765         pimpl_->toplevel_keymap_.read("site");
766         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
767         // load user bind file user.bind
768         pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
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.setLevel(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) ? os::utf8_argv(i + 1) : string();
1125                 string const arg2 =
1126                         (i + 2 < argc) ? os::utf8_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                         os::remove_internal_args(i, remove);
1137                         argc -= remove;
1138                         for (int j = i; j < argc; ++j)
1139                                 argv[j] = argv[j + remove];
1140                         --i;
1141                 }
1142         }
1143 }
1144
1145
1146 FuncStatus getStatus(FuncRequest const & action)
1147 {
1148         LASSERT(theApp(), /**/);
1149         return theApp()->getStatus(action);
1150 }
1151
1152
1153 void dispatch(FuncRequest const & action)
1154 {
1155         LASSERT(theApp(), /**/);
1156         return theApp()->dispatch(action);
1157 }
1158
1159
1160 void dispatch(FuncRequest const & action, DispatchResult & dr)
1161 {
1162         LASSERT(theApp(), /**/);
1163         return theApp()->dispatch(action, dr);
1164 }
1165
1166
1167 BufferList & theBufferList()
1168 {
1169         LASSERT(singleton_, /**/);
1170         return singleton_->pimpl_->buffer_list_;
1171 }
1172
1173
1174 Server & theServer()
1175 {
1176         // FIXME: this should not be use_gui dependent
1177         LASSERT(use_gui, /**/);
1178         LASSERT(singleton_, /**/);
1179         return *singleton_->pimpl_->lyx_server_.get();
1180 }
1181
1182
1183 ServerSocket & theServerSocket()
1184 {
1185         // FIXME: this should not be use_gui dependent
1186         LASSERT(use_gui, /**/);
1187         LASSERT(singleton_, /**/);
1188         return *singleton_->pimpl_->lyx_socket_.get();
1189 }
1190
1191
1192 KeyMap & theTopLevelKeymap()
1193 {
1194         LASSERT(singleton_, /**/);
1195         return singleton_->pimpl_->toplevel_keymap_;
1196 }
1197
1198
1199 Converters & theConverters()
1200 {
1201         LASSERT(singleton_, /**/);
1202         return  singleton_->pimpl_->converters_;
1203 }
1204
1205
1206 Converters & theSystemConverters()
1207 {
1208         LASSERT(singleton_, /**/);
1209         return  singleton_->pimpl_->system_converters_;
1210 }
1211
1212
1213 Movers & theMovers()
1214 {
1215         LASSERT(singleton_, /**/);
1216         return singleton_->pimpl_->movers_;
1217 }
1218
1219
1220 Mover const & getMover(string  const & fmt)
1221 {
1222         LASSERT(singleton_, /**/);
1223         return singleton_->pimpl_->movers_(fmt);
1224 }
1225
1226
1227 void setMover(string const & fmt, string const & command)
1228 {
1229         LASSERT(singleton_, /**/);
1230         singleton_->pimpl_->movers_.set(fmt, command);
1231 }
1232
1233
1234 Movers & theSystemMovers()
1235 {
1236         LASSERT(singleton_, /**/);
1237         return singleton_->pimpl_->system_movers_;
1238 }
1239
1240
1241 Messages const & getMessages(string const & language)
1242 {
1243         LASSERT(singleton_, /**/);
1244         return singleton_->messages(language);
1245 }
1246
1247
1248 Messages const & getGuiMessages()
1249 {
1250         LASSERT(singleton_, /**/);
1251         return singleton_->pimpl_->messages_["GUI"];
1252 }
1253
1254
1255 graphics::Previews & thePreviews()
1256 {
1257         LASSERT(singleton_, /**/);
1258         return singleton_->pimpl_->preview_;
1259 }
1260
1261
1262 Session & theSession()
1263 {
1264         LASSERT(singleton_, /**/);
1265         return *singleton_->pimpl_->session_.get();
1266 }
1267
1268
1269 CmdDef & theTopLevelCmdDef()
1270 {
1271         LASSERT(singleton_, /**/);
1272         return singleton_->pimpl_->toplevel_cmddef_;
1273 }
1274
1275
1276 SpellChecker * theSpellChecker()
1277 {
1278         if (!singleton_->pimpl_->spell_checker_)
1279                 setSpellChecker();
1280         return singleton_->pimpl_->spell_checker_;
1281 }
1282
1283
1284 void setSpellChecker()
1285 {
1286 #if defined(USE_ASPELL)
1287         if (lyxrc.spellchecker == "aspell") {
1288                 if (!singleton_->pimpl_->aspell_checker_)
1289                         singleton_->pimpl_->aspell_checker_ = new AspellChecker();
1290                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1291                 return;
1292         }
1293 #endif
1294 #if defined(USE_ENCHANT)
1295         if (lyxrc.spellchecker == "enchant") {
1296                 if (!singleton_->pimpl_->enchant_checker_)
1297                         singleton_->pimpl_->enchant_checker_ = new EnchantChecker();
1298                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1299                 return;
1300         }
1301 #endif
1302 #if defined(USE_HUNSPELL)
1303         if (lyxrc.spellchecker == "hunspell") {
1304                 if (!singleton_->pimpl_->hunspell_checker_)
1305                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker();
1306                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1307                 return;
1308         }
1309 #endif
1310         singleton_->pimpl_->spell_checker_ = 0;
1311 }
1312
1313 } // namespace lyx