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