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