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