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