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