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