]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
A little more lv cleanup.
[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         docstring msg;
615         switch (err_sig) {
616 #ifdef SIGHUP
617         case SIGHUP:
618                 msg = _("SIGHUP signal caught!\nBye.");
619                 break;
620 #endif
621         case SIGFPE:
622                 msg = _("SIGFPE signal caught!\nBye.");
623                 break;
624         case SIGSEGV:
625                 msg = _("SIGSEGV signal caught!\n"
626                           "Sorry, you have found a bug in LyX, "
627                           "hope you have not lost any data.\n"
628                           "Please read the bug-reporting instructions "
629                           "in 'Help->Introduction' and send us a bug report, "
630                           "if necessary. Thanks !\nBye.");
631                 break;
632         case SIGINT:
633         case SIGTERM:
634                 // no comments
635                 break;
636         }
637
638         if (!msg.empty()) {
639                 lyxerr << "\nlyx: " << msg << endl;
640                 // try to make a GUI message
641                 Alert::error(_("LyX crashed!"), msg);
642         }
643
644         // Deinstall the signal handlers
645 #ifdef SIGHUP
646         signal(SIGHUP, SIG_DFL);
647 #endif
648         signal(SIGINT, SIG_DFL);
649         signal(SIGFPE, SIG_DFL);
650         signal(SIGSEGV, SIG_DFL);
651         signal(SIGTERM, SIG_DFL);
652
653 #ifdef SIGHUP
654         if (err_sig == SIGSEGV ||
655                 (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
656 #else
657         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
658 #endif 
659 #ifdef _MSC_VER
660                 // with abort() it crashes again.
661                 exit(err_sig);
662 #else
663                 abort();
664 #endif
665         }
666
667         exit(0);
668 }
669
670 }
671
672
673 void LyX::printError(ErrorItem const & ei)
674 {
675         docstring tmp = _("LyX: ") + ei.error + char_type(':')
676                 + ei.description;
677         cerr << to_utf8(tmp) << endl;
678 }
679
680
681 bool LyX::init()
682 {
683 #ifdef SIGHUP
684         signal(SIGHUP, error_handler);
685 #endif
686         signal(SIGFPE, error_handler);
687         signal(SIGSEGV, error_handler);
688         signal(SIGINT, error_handler);
689         signal(SIGTERM, error_handler);
690         // SIGPIPE can be safely ignored.
691
692         lyxrc.tempdir_path = package().temp_dir().absFilename();
693         lyxrc.document_path = package().document_dir().absFilename();
694
695         if (lyxrc.example_path.empty()) {
696                 lyxrc.example_path = addPath(package().system_support().absFilename(),
697                                               "examples");
698         }
699         if (lyxrc.template_path.empty()) {
700                 lyxrc.template_path = addPath(package().system_support().absFilename(),
701                                               "templates");
702         }
703
704         //
705         // Read configuration files
706         //
707
708         // This one may have been distributed along with LyX.
709         if (!readRcFile("lyxrc.dist"))
710                 return false;
711
712         // Set the language defined by the distributor.
713         setRcGuiLanguage();
714
715         // Set the PATH correctly.
716 #if !defined (USE_POSIX_PACKAGING)
717         // Add the directory containing the LyX executable to the path
718         // so that LyX can find things like tex2lyx.
719         if (package().build_support().empty())
720                 prependEnvPath("PATH", package().binary_dir().absFilename());
721 #endif
722         if (!lyxrc.path_prefix.empty())
723                 prependEnvPath("PATH", lyxrc.path_prefix);
724
725         // Check that user LyX directory is ok.
726         if (queryUserLyXDir(package().explicit_user_support()))
727                 reconfigureUserLyXDir();
728
729         // no need for a splash when there is no GUI
730         if (!use_gui) {
731                 first_start = false;
732         }
733
734         // This one is generated in user_support directory by lib/configure.py.
735         if (!readRcFile("lyxrc.defaults"))
736                 return false;
737
738         // Query the OS to know what formats are viewed natively
739         formats.setAutoOpen();
740
741         // Read lyxrc.dist again to be able to override viewer auto-detection.
742         readRcFile("lyxrc.dist");
743
744         // Set again the language defined by the distributor.
745         setRcGuiLanguage();
746
747         system_lyxrc = lyxrc;
748         system_formats = formats;
749         pimpl_->system_converters_ = pimpl_->converters_;
750         pimpl_->system_movers_ = pimpl_->movers_;
751         system_lcolor = lcolor;
752
753         // This one is edited through the preferences dialog.
754         if (!readRcFile("preferences"))
755                 return false;
756
757         if (!readEncodingsFile("encodings", "unicodesymbols"))
758                 return false;
759         if (!readLanguagesFile("languages"))
760                 return false;
761
762         // Set the language defined by the user.
763         setRcGuiLanguage();
764
765         // Load the layouts
766         LYXERR(Debug::INIT, "Reading layouts...");
767         if (!LyXSetStyle())
768                 return false;
769         //...and the modules
770         theModuleList.read();
771
772         // read keymap and ui files in batch mode as well
773         // because InsetInfo needs to know these to produce
774         // the correct output
775
776         // Set up command definitions
777         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
778
779         // Set up bindings
780         pimpl_->toplevel_keymap_.read("site");
781         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
782         // load user bind file user.bind
783         pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
784
785         if (lyxerr.debugging(Debug::LYXRC))
786                 lyxrc.print();
787
788         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
789         if (!lyxrc.path_prefix.empty())
790                 prependEnvPath("PATH", lyxrc.path_prefix);
791
792         FileName const document_path(lyxrc.document_path);
793         if (document_path.exists() && document_path.isDirectory())
794                 package().document_dir() = document_path;
795
796         package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
797         if (package().temp_dir().empty()) {
798                 Alert::error(_("Could not create temporary directory"),
799                              bformat(_("Could not create a temporary directory in\n"
800                                                        "\"%1$s\"\n"
801                                                            "Make sure that this path exists and is writable and try again."),
802                                      from_utf8(lyxrc.tempdir_path)));
803                 // createLyXTmpDir() tries sufficiently hard to create a
804                 // usable temp dir, so the probability to come here is
805                 // close to zero. We therefore don't try to overcome this
806                 // problem with e.g. asking the user for a new path and
807                 // trying again but simply exit.
808                 return false;
809         }
810
811         LYXERR(Debug::INIT, "LyX tmp dir: `"
812                             << package().temp_dir().absFilename() << '\'');
813
814         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
815         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
816
817         // This must happen after package initialization and after lyxrc is
818         // read, therefore it can't be done by a static object.
819         ConverterCache::init();
820                 
821         return true;
822 }
823
824
825 void emergencyCleanup()
826 {
827         // what to do about tmpfiles is non-obvious. we would
828         // like to delete any we find, but our lyxdir might
829         // contain documents etc. which might be helpful on
830         // a crash
831
832         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
833         if (use_gui) {
834                 if (singleton_->pimpl_->lyx_server_)
835                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
836                 singleton_->pimpl_->lyx_server_.reset();
837                 singleton_->pimpl_->lyx_socket_.reset();
838         }
839 }
840
841
842 // return true if file does not exist or is older than configure.py.
843 static bool needsUpdate(string const & file)
844 {
845         // We cannot initialize configure_script directly because the package
846         // is not initialized yet when  static objects are constructed.
847         static FileName configure_script;
848         static bool firstrun = true;
849         if (firstrun) {
850                 configure_script =
851                         FileName(addName(package().system_support().absFilename(),
852                                 "configure.py"));
853                 firstrun = false;
854         }
855
856         FileName absfile = 
857                 FileName(addName(package().user_support().absFilename(), file));
858         return !absfile.exists()
859                 || configure_script.lastModified() > absfile.lastModified();
860 }
861
862
863 bool LyX::queryUserLyXDir(bool explicit_userdir)
864 {
865         // Does user directory exist?
866         FileName const sup = package().user_support();
867         if (sup.exists() && sup.isDirectory()) {
868                 first_start = false;
869
870                 return needsUpdate("lyxrc.defaults")
871                         || needsUpdate("lyxmodules.lst")
872                         || needsUpdate("textclass.lst")
873                         || needsUpdate("packages.lst");
874         }
875
876         first_start = !explicit_userdir;
877
878         // If the user specified explicitly a directory, ask whether
879         // to create it. If the user says "no", then exit.
880         if (explicit_userdir &&
881             Alert::prompt(
882                     _("Missing user LyX directory"),
883                     bformat(_("You have specified a non-existent user "
884                                            "LyX directory, %1$s.\n"
885                                            "It is needed to keep your own configuration."),
886                             from_utf8(package().user_support().absFilename())),
887                     1, 0,
888                     _("&Create directory"),
889                     _("&Exit LyX"))) {
890                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
891                 earlyExit(EXIT_FAILURE);
892         }
893
894         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
895                           from_utf8(sup.absFilename()))) << endl;
896
897         if (!sup.createDirectory(0755)) {
898                 // Failed, so let's exit.
899                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
900                        << endl;
901                 earlyExit(EXIT_FAILURE);
902         }
903
904         return true;
905 }
906
907
908 bool LyX::readRcFile(string const & name)
909 {
910         LYXERR(Debug::INIT, "About to read " << name << "... ");
911
912         FileName const lyxrc_path = libFileSearch(string(), name);
913         if (!lyxrc_path.empty()) {
914                 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
915                 if (lyxrc.read(lyxrc_path) < 0) {
916                         showFileError(name);
917                         return false;
918                 }
919         } else {
920                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
921         }
922         return true;
923 }
924
925 // Read the languages file `name'
926 bool LyX::readLanguagesFile(string const & name)
927 {
928         LYXERR(Debug::INIT, "About to read " << name << "...");
929
930         FileName const lang_path = libFileSearch(string(), name);
931         if (lang_path.empty()) {
932                 showFileError(name);
933                 return false;
934         }
935         languages.read(lang_path);
936         return true;
937 }
938
939
940 // Read the encodings file `name'
941 bool LyX::readEncodingsFile(string const & enc_name,
942                             string const & symbols_name)
943 {
944         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
945                             << symbols_name << "...");
946
947         FileName const symbols_path = libFileSearch(string(), symbols_name);
948         if (symbols_path.empty()) {
949                 showFileError(symbols_name);
950                 return false;
951         }
952
953         FileName const enc_path = libFileSearch(string(), enc_name);
954         if (enc_path.empty()) {
955                 showFileError(enc_name);
956                 return false;
957         }
958         encodings.read(enc_path, symbols_path);
959         return true;
960 }
961
962
963 namespace {
964
965 /// return the the number of arguments consumed
966 typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
967
968 int parse_dbg(string const & arg, string const &, string &)
969 {
970         if (arg.empty()) {
971                 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
972                 Debug::showTags(lyxerr);
973                 exit(0);
974         }
975         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
976
977         lyxerr.setLevel(Debug::value(arg));
978         Debug::showLevel(lyxerr, lyxerr.level());
979         return 1;
980 }
981
982
983 int parse_help(string const &, string const &, string &)
984 {
985         lyxerr <<
986                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
987                   "Command line switches (case sensitive):\n"
988                   "\t-help              summarize LyX usage\n"
989                   "\t-userdir dir       set user directory to dir\n"
990                   "\t-sysdir dir        set system directory to dir\n"
991                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
992                   "\t-dbg feature[,feature]...\n"
993                   "                  select the features to debug.\n"
994                   "                  Type `lyx -dbg' to see the list of features\n"
995                   "\t-x [--execute] command\n"
996                   "                  where command is a lyx command.\n"
997                   "\t-e [--export] fmt\n"
998                   "                  where fmt is the export format of choice.\n"
999                   "                  Look on Tools->Preferences->File formats->Format\n"
1000                   "                  to get an idea which parameters should be passed.\n"
1001                   "                  Note that the order of -e and -x switches matters.\n"
1002                   "\t-i [--import] fmt file.xxx\n"
1003                   "                  where fmt is the import format of choice\n"
1004                   "                  and file.xxx is the file to be imported.\n"
1005                   "\t-batch          execute commands without launching GUI and exit.\n"
1006                   "\t-version        summarize version and build info\n"
1007                                "Check the LyX man page for more details.")) << endl;
1008         exit(0);
1009         return 0;
1010 }
1011
1012
1013 int parse_version(string const &, string const &, string &)
1014 {
1015         lyxerr << "LyX " << lyx_version
1016                << " (" << lyx_release_date << ")" << endl;
1017         lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1018
1019         lyxerr << lyx_version_info << endl;
1020         exit(0);
1021         return 0;
1022 }
1023
1024
1025 int parse_sysdir(string const & arg, string const &, string &)
1026 {
1027         if (arg.empty()) {
1028                 Alert::error(_("No system directory"),
1029                         _("Missing directory for -sysdir switch"));
1030                 exit(1);
1031         }
1032         cl_system_support = arg;
1033         return 1;
1034 }
1035
1036
1037 int parse_userdir(string const & arg, string const &, string &)
1038 {
1039         if (arg.empty()) {
1040                 Alert::error(_("No user directory"),
1041                         _("Missing directory for -userdir switch"));
1042                 exit(1);
1043         }
1044         cl_user_support = arg;
1045         return 1;
1046 }
1047
1048
1049 int parse_execute(string const & arg, string const &, string & batch)
1050 {
1051         if (arg.empty()) {
1052                 Alert::error(_("Incomplete command"),
1053                         _("Missing command string after --execute switch"));
1054                 exit(1);
1055         }
1056         batch = arg;
1057         return 1;
1058 }
1059
1060
1061 int parse_export(string const & type, string const &, string & batch)
1062 {
1063         if (type.empty()) {
1064                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1065                                          "--export switch")) << endl;
1066                 exit(1);
1067         }
1068         batch = "buffer-export " + type;
1069         use_gui = false;
1070         return 1;
1071 }
1072
1073
1074 int parse_import(string const & type, string const & file, string & batch)
1075 {
1076         if (type.empty()) {
1077                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1078                                          "--import switch")) << endl;
1079                 exit(1);
1080         }
1081         if (file.empty()) {
1082                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1083                 exit(1);
1084         }
1085
1086         batch = "buffer-import " + type + ' ' + file;
1087         return 2;
1088 }
1089
1090
1091 int parse_geometry(string const & arg1, string const &, string &)
1092 {
1093         geometryArg = arg1;
1094         // don't remove "-geometry", it will be pruned out later in the
1095         // frontend if need be.
1096         return -1;
1097 }
1098
1099
1100 int parse_batch(string const &, string const &, string &) 
1101 {
1102         use_gui = false;
1103         return 0;
1104 }
1105
1106
1107 } // namespace anon
1108
1109
1110 void LyX::easyParse(int & argc, char * argv[])
1111 {
1112         map<string, cmd_helper> cmdmap;
1113
1114         cmdmap["-dbg"] = parse_dbg;
1115         cmdmap["-help"] = parse_help;
1116         cmdmap["--help"] = parse_help;
1117         cmdmap["-version"] = parse_version;
1118         cmdmap["--version"] = parse_version;
1119         cmdmap["-sysdir"] = parse_sysdir;
1120         cmdmap["-userdir"] = parse_userdir;
1121         cmdmap["-x"] = parse_execute;
1122         cmdmap["--execute"] = parse_execute;
1123         cmdmap["-e"] = parse_export;
1124         cmdmap["--export"] = parse_export;
1125         cmdmap["-i"] = parse_import;
1126         cmdmap["--import"] = parse_import;
1127         cmdmap["-geometry"] = parse_geometry;
1128         cmdmap["-batch"] = parse_batch;
1129
1130         for (int i = 1; i < argc; ++i) {
1131                 map<string, cmd_helper>::const_iterator it
1132                         = cmdmap.find(argv[i]);
1133
1134                 // don't complain if not found - may be parsed later
1135                 if (it == cmdmap.end())
1136                         continue;
1137
1138                 string const arg =
1139                         (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1140                 string const arg2 =
1141                         (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1142
1143                 string batch;
1144                 int const remove = 1 + it->second(arg, arg2, batch);
1145                 if (!batch.empty())
1146                         pimpl_->batch_commands.push_back(batch);
1147
1148                 // Now, remove used arguments by shifting
1149                 // the following ones remove places down.
1150                 if (remove > 0) {
1151                         os::remove_internal_args(i, remove);
1152                         argc -= remove;
1153                         for (int j = i; j < argc; ++j)
1154                                 argv[j] = argv[j + remove];
1155                         --i;
1156                 }
1157         }
1158 }
1159
1160
1161 FuncStatus getStatus(FuncRequest const & action)
1162 {
1163         LASSERT(theApp(), /**/);
1164         return theApp()->getStatus(action);
1165 }
1166
1167
1168 void dispatch(FuncRequest const & action)
1169 {
1170         LASSERT(theApp(), /**/);
1171         return theApp()->dispatch(action);
1172 }
1173
1174
1175 void dispatch(FuncRequest const & action, DispatchResult & dr)
1176 {
1177         LASSERT(theApp(), /**/);
1178         return theApp()->dispatch(action, dr);
1179 }
1180
1181
1182 BufferList & theBufferList()
1183 {
1184         LASSERT(singleton_, /**/);
1185         return singleton_->pimpl_->buffer_list_;
1186 }
1187
1188
1189 Server & theServer()
1190 {
1191         // FIXME: this should not be use_gui dependent
1192         LASSERT(use_gui, /**/);
1193         LASSERT(singleton_, /**/);
1194         return *singleton_->pimpl_->lyx_server_.get();
1195 }
1196
1197
1198 ServerSocket & theServerSocket()
1199 {
1200         // FIXME: this should not be use_gui dependent
1201         LASSERT(use_gui, /**/);
1202         LASSERT(singleton_, /**/);
1203         return *singleton_->pimpl_->lyx_socket_.get();
1204 }
1205
1206
1207 KeyMap & theTopLevelKeymap()
1208 {
1209         LASSERT(singleton_, /**/);
1210         return singleton_->pimpl_->toplevel_keymap_;
1211 }
1212
1213
1214 Converters & theConverters()
1215 {
1216         LASSERT(singleton_, /**/);
1217         return  singleton_->pimpl_->converters_;
1218 }
1219
1220
1221 Converters & theSystemConverters()
1222 {
1223         LASSERT(singleton_, /**/);
1224         return  singleton_->pimpl_->system_converters_;
1225 }
1226
1227
1228 Movers & theMovers()
1229 {
1230         LASSERT(singleton_, /**/);
1231         return singleton_->pimpl_->movers_;
1232 }
1233
1234
1235 Mover const & getMover(string  const & fmt)
1236 {
1237         LASSERT(singleton_, /**/);
1238         return singleton_->pimpl_->movers_(fmt);
1239 }
1240
1241
1242 void setMover(string const & fmt, string const & command)
1243 {
1244         LASSERT(singleton_, /**/);
1245         singleton_->pimpl_->movers_.set(fmt, command);
1246 }
1247
1248
1249 Movers & theSystemMovers()
1250 {
1251         LASSERT(singleton_, /**/);
1252         return singleton_->pimpl_->system_movers_;
1253 }
1254
1255
1256 Messages const & getMessages(string const & language)
1257 {
1258         LASSERT(singleton_, /**/);
1259         return singleton_->messages(language);
1260 }
1261
1262
1263 Messages const & getGuiMessages()
1264 {
1265         LASSERT(singleton_, /**/);
1266         return singleton_->pimpl_->messages_["GUI"];
1267 }
1268
1269
1270 graphics::Previews & thePreviews()
1271 {
1272         LASSERT(singleton_, /**/);
1273         return singleton_->pimpl_->preview_;
1274 }
1275
1276
1277 Session & theSession()
1278 {
1279         LASSERT(singleton_, /**/);
1280         return *singleton_->pimpl_->session_.get();
1281 }
1282
1283
1284 CmdDef & theTopLevelCmdDef()
1285 {
1286         LASSERT(singleton_, /**/);
1287         return singleton_->pimpl_->toplevel_cmddef_;
1288 }
1289
1290
1291 SpellChecker * theSpellChecker()
1292 {
1293         if (!singleton_->pimpl_->spell_checker_)
1294                 setSpellChecker();
1295         return singleton_->pimpl_->spell_checker_;
1296 }
1297
1298
1299 void setSpellChecker()
1300 {
1301 #if defined(USE_ASPELL)
1302         if (lyxrc.spellchecker == "aspell") {
1303                 if (!singleton_->pimpl_->aspell_checker_)
1304                         singleton_->pimpl_->aspell_checker_ = new AspellChecker();
1305                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1306                 return;
1307         }
1308 #endif
1309 #if defined(USE_ENCHANT)
1310         if (lyxrc.spellchecker == "enchant") {
1311                 if (!singleton_->pimpl_->enchant_checker_)
1312                         singleton_->pimpl_->enchant_checker_ = new EnchantChecker();
1313                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1314                 return;
1315         }
1316 #endif
1317 #if defined(USE_HUNSPELL)
1318         if (lyxrc.spellchecker == "hunspell") {
1319                 if (!singleton_->pimpl_->hunspell_checker_)
1320                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker();
1321                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1322                 return;
1323         }
1324 #endif
1325         singleton_->pimpl_->spell_checker_ = 0;
1326 }
1327
1328 } // namespace lyx