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