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