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