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