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