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