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