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