]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
Generate the magic label always. We'll need it other times, when we do
[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
75 using namespace std;
76 using namespace lyx::support;
77
78 namespace lyx {
79
80 namespace Alert = frontend::Alert;
81 namespace os = support::os;
82
83
84
85 // Are we using the GUI at all?  We default to true and this is changed
86 // to false when the export feature is used.
87
88 bool use_gui = true;
89
90 namespace {
91
92 // Filled with the command line arguments "foo" of "-sysdir foo" or
93 // "-userdir foo".
94 string cl_system_support;
95 string cl_user_support;
96
97 string geometryArg;
98
99 LyX * singleton_ = 0;
100
101 void showFileError(string const & error)
102 {
103         Alert::warning(_("Could not read configuration file"),
104                        bformat(_("Error while reading the configuration file\n%1$s.\n"
105                            "Please check your installation."), from_utf8(error)));
106 }
107
108
109 void reconfigureUserLyXDir()
110 {
111         string const configure_command = package().configure_command();
112
113         lyxerr << to_utf8(_("LyX: reconfiguring user directory")) << endl;
114         PathChanger p(package().user_support());
115         Systemcall one;
116         one.startscript(Systemcall::Wait, configure_command);
117         lyxerr << "LyX: " << to_utf8(_("Done!")) << endl;
118 }
119
120 } // namespace anon
121
122 /// The main application class private implementation.
123 struct LyX::Impl
124 {
125         Impl() : spell_checker_(0), aspell_checker_(0), hunspell_checker_(0)
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
133         ~Impl()
134         {
135                 delete aspell_checker_;
136                 delete hunspell_checker_;
137         }
138
139         /// our function handler
140         LyXFunc lyxfunc_;
141         ///
142         BufferList buffer_list_;
143         ///
144         KeyMap toplevel_keymap_;
145         ///
146         CmdDef toplevel_cmddef_;
147         ///
148         boost::scoped_ptr<Server> lyx_server_;
149         ///
150         boost::scoped_ptr<ServerSocket> lyx_socket_;
151         ///
152         boost::scoped_ptr<frontend::Application> application_;
153         /// lyx session, containing lastfiles, lastfilepos, and lastopened
154         boost::scoped_ptr<Session> session_;
155
156         /// Files to load at start.
157         vector<string> files_to_load_;
158
159         /// The messages translators.
160         map<string, Messages> messages_;
161
162         /// The file converters.
163         Converters converters_;
164
165         // The system converters copy after reading lyxrc.defaults.
166         Converters system_converters_;
167
168         ///
169         Movers movers_;
170         ///
171         Movers system_movers_;
172
173         /// has this user started lyx for the first time?
174         bool first_start;
175         /// the parsed command line batch command if any
176         vector<string> batch_commands;
177
178         ///
179         graphics::Previews preview_;
180         ///
181         SpellChecker * spell_checker_;
182         ///
183         SpellChecker * aspell_checker_;
184         ///
185         SpellChecker * hunspell_checker_;
186 };
187
188 ///
189 frontend::Application * theApp()
190 {
191         if (singleton_)
192                 return singleton_->pimpl_->application_.get();
193         else
194                 return 0;
195 }
196
197
198 LyX::~LyX()
199 {
200         singleton_ = 0;
201         delete pimpl_;
202 }
203
204
205 void lyx_exit(int exit_code)
206 {
207         if (exit_code)
208                 // Something wrong happened so better save everything, just in
209                 // case.
210                 emergencyCleanup();
211
212 #ifndef NDEBUG
213         // Properly crash in debug mode in order to get a useful backtrace.
214         abort();
215 #endif
216
217         // In release mode, try to exit gracefully.
218         if (theApp())
219                 theApp()->exit(exit_code);
220         else
221                 exit(exit_code);
222 }
223
224
225 LyX::LyX()
226         : first_start(false)
227 {
228         singleton_ = this;
229         pimpl_ = new Impl;
230 }
231
232
233 Messages & LyX::messages(string const & language)
234 {
235         map<string, Messages>::iterator it = pimpl_->messages_.find(language);
236
237         if (it != pimpl_->messages_.end())
238                 return it->second;
239
240         pair<map<string, Messages>::iterator, bool> result =
241                         pimpl_->messages_.insert(make_pair(language, Messages(language)));
242
243         LASSERT(result.second, /**/);
244         return result.first->second;
245 }
246
247
248 void setRcGuiLanguage()
249 {
250         LASSERT(singleton_, /**/);
251         if (lyxrc.gui_language == "auto")
252                 return;
253         Language const * language = languages.getLanguage(lyxrc.gui_language);
254         if (language) {
255                 LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << language->code());
256                 if (!setEnv("LANGUAGE", language->code()))
257                         LYXERR(Debug::LOCALE, "\t... failed!");
258         }
259         LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
260         if (!setEnv("LC_ALL", "en_US"))
261                 LYXERR(Debug::LOCALE, "\t... failed!");
262         Messages::init();
263         singleton_->pimpl_->messages_["GUI"] = Messages();
264 }
265
266
267 int LyX::exec(int & argc, char * argv[])
268 {
269         // Here we need to parse the command line. At least
270         // we need to parse for "-dbg" and "-help"
271         easyParse(argc, argv);
272
273         try {
274                 init_package(to_utf8(from_local8bit(argv[0])),
275                               cl_system_support, cl_user_support,
276                               top_build_dir_is_one_level_up);
277         } catch (ExceptionMessage const & message) {
278                 if (message.type_ == ErrorException) {
279                         Alert::error(message.title_, message.details_);
280                         lyx_exit(1);
281                 } else if (message.type_ == WarningException) {
282                         Alert::warning(message.title_, message.details_);
283                 }
284         }
285
286         // Reinit the messages machinery in case package() knows
287         // something interesting about the locale directory.
288         Messages::init();
289
290         if (!use_gui) {
291                 // FIXME: create a ConsoleApplication
292                 int exit_status = init(argc, argv);
293                 if (exit_status) {
294                         prepareExit();
295                         return exit_status;
296                 }
297
298                 // this is correct, since return values are inverted.
299                 exit_status = !loadFiles();
300
301                 if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
302                         prepareExit();
303                         return exit_status;
304                 }
305
306                 BufferList::iterator begin = pimpl_->buffer_list_.begin();
307
308                 bool final_success = false;
309                 for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
310                         Buffer * buf = *I;
311                         if (buf != buf->masterBuffer())
312                                 continue;
313                         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
314                         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
315                         DispatchResult dr;
316                         for (; bcit != bcend; bcit++) {
317                                 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
318                                 buf->dispatch(*bcit, dr);
319                                 final_success |= !dr.error();
320                         }
321                 }
322                 prepareExit();
323                 return !final_success;
324         }
325
326         // Let the frontend parse and remove all arguments that it knows
327         pimpl_->application_.reset(createApplication(argc, argv));
328
329         // Reestablish our defaults, as Qt overwrites them
330         // after createApplication()
331         locale_init();
332
333         // Parse and remove all known arguments in the LyX singleton
334         // Give an error for all remaining ones.
335         int exit_status = init(argc, argv);
336         if (exit_status) {
337                 // Kill the application object before exiting.
338                 pimpl_->application_.reset();
339                 use_gui = false;
340                 prepareExit();
341                 return exit_status;
342         }
343  
344         // FIXME
345         /* Create a CoreApplication class that will provide the main event loop
346         * and the socket callback registering. With Qt4, only QtCore
347         * library would be needed.
348         * When this is done, a server_mode could be created and the following two
349         * line would be moved out from here.
350         */
351         // Note: socket callback must be registered after init(argc, argv)
352         // such that package().temp_dir() is properly initialized.
353         pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
354         pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
355                         FileName(package().temp_dir().absFilename() + "/lyxsocket")));
356
357         // Start the real execution loop.
358         exit_status = pimpl_->application_->exec();
359
360         prepareExit();
361
362         return exit_status;
363 }
364
365
366 void LyX::prepareExit()
367 {
368         // Clear the clipboard and selection stack:
369         cap::clearCutStack();
370         cap::clearSelection();
371
372         // Write the index file of the converter cache
373         ConverterCache::get().writeIndex();
374
375         // close buffers first
376         pimpl_->buffer_list_.closeAll();
377
378         // register session changes and shutdown server and socket
379         if (use_gui) {
380                 if (pimpl_->session_)
381                         pimpl_->session_->writeFile();
382                 pimpl_->session_.reset();
383                 pimpl_->lyx_server_.reset();
384                 pimpl_->lyx_socket_.reset();
385         }
386
387         // do any other cleanup procedures now
388         if (package().temp_dir() != package().system_temp_dir()) {
389                 string const abs_tmpdir = package().temp_dir().absFilename();
390                 if (!contains(package().temp_dir().absFilename(), "lyx_tmpdir")) {
391                         docstring const msg =
392                                 bformat(_("%1$s does not appear like a LyX created temporary directory."),
393                                 from_utf8(abs_tmpdir));
394                         Alert::warning(_("Cannot remove temporary directory"), msg);
395                 } else {
396                         LYXERR(Debug::INFO, "Deleting tmp dir "
397                                 << package().temp_dir().absFilename());
398                         if (!package().temp_dir().destroyDirectory()) {
399                                 docstring const msg =
400                                         bformat(_("Unable to remove the temporary directory %1$s"),
401                                         from_utf8(package().temp_dir().absFilename()));
402                                 Alert::warning(_("Unable to remove temporary directory"), msg);
403                         }
404                 }
405         }
406
407         // Kill the application object before exiting. This avoids crashes
408         // when exiting on Linux.
409         if (pimpl_->application_)
410                 pimpl_->application_.reset();
411 }
412
413
414 void LyX::earlyExit(int status)
415 {
416         LASSERT(pimpl_->application_.get(), /**/);
417         // LyX::pimpl_::application_ is not initialised at this
418         // point so it's safe to just exit after some cleanup.
419         prepareExit();
420         exit(status);
421 }
422
423
424 int LyX::init(int & argc, char * argv[])
425 {
426         // check for any spurious extra arguments
427         // other than documents
428         for (int argi = 1; argi < argc ; ++argi) {
429                 if (argv[argi][0] == '-') {
430                         lyxerr << to_utf8(
431                                 bformat(_("Wrong command line option `%1$s'. Exiting."),
432                                 from_utf8(argv[argi]))) << endl;
433                         return EXIT_FAILURE;
434                 }
435         }
436
437         // Initialization of LyX (reads lyxrc and more)
438         LYXERR(Debug::INIT, "Initializing LyX::init...");
439         bool success = init();
440         LYXERR(Debug::INIT, "Initializing LyX::init...done");
441         if (!success)
442                 return EXIT_FAILURE;
443
444         // Remaining arguments are assumed to be files to load.
445         for (int argi = argc - 1; argi >= 1; --argi)
446                 pimpl_->files_to_load_.push_back(to_utf8(from_local8bit(argv[argi])));
447
448         if (first_start) {
449                 pimpl_->files_to_load_.push_back(
450                         i18nLibFileSearch("examples", "splash.lyx").absFilename());
451         }
452
453         return EXIT_SUCCESS;
454 }
455
456
457 bool LyX::loadFiles()
458 {
459         LASSERT(!use_gui, /**/);
460         bool success = true;
461         vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
462         vector<string>::const_iterator end = pimpl_->files_to_load_.end();
463
464         for (; it != end; ++it) {
465                 // get absolute path of file and add ".lyx" to
466                 // the filename if necessary
467                 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
468                         may_not_exist);
469
470                 if (fname.empty())
471                         continue;
472
473                 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFilename(), false);
474                 if (buf->loadLyXFile(fname)) {
475                         ErrorList const & el = buf->errorList("Parse");
476                         if (!el.empty())
477                                 for_each(el.begin(), el.end(),
478                                 boost::bind(&LyX::printError, this, _1));
479                 }
480                 else {
481                         pimpl_->buffer_list_.release(buf);
482                         success = false;
483                 }
484         }
485         return success;
486 }
487
488
489 void execBatchCommands()
490 {
491         LASSERT(singleton_, /**/);
492         singleton_->execCommands();
493 }
494
495
496 void LyX::execCommands()
497 {
498         // The advantage of doing this here is that the event loop
499         // is already started. So any need for interaction will be
500         // aknowledged.
501
502         // if reconfiguration is needed.
503         while (LayoutFileList::get().empty()) {
504                 switch (Alert::prompt(
505                         _("No textclass is found"),
506                         _("LyX cannot continue because no textclass is found. "
507                                 "You can either reconfigure normally, or reconfigure using "
508                                 "default textclasses, or quit LyX."),
509                         0, 2,
510                         _("&Reconfigure"),
511                         _("&Use Default"),
512                         _("&Exit LyX")))
513                 {
514                 case 0:
515                         // regular reconfigure
516                         pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
517                         break;
518                 case 1:
519                         // reconfigure --without-latex-config
520                         pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
521                                 " --without-latex-config"));
522                         break;
523                 default:
524                         pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
525                         return;
526                 }
527         }
528         
529         // create the first main window
530         pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
531
532         if (!pimpl_->files_to_load_.empty()) {
533                 // if some files were specified at command-line we assume that the
534                 // user wants to edit *these* files and not to restore the session.
535                 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
536                         pimpl_->lyxfunc_.dispatch(
537                                 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
538                 }
539                 // clear this list to save a few bytes of RAM
540                 pimpl_->files_to_load_.clear();
541         } else
542                 pimpl_->application_->restoreGuiSession();
543
544         // Execute batch commands if available
545         if (pimpl_->batch_commands.empty())
546                 return;
547
548         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
549         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
550         for (; bcit != bcend; bcit++) {
551                 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
552                 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(*bcit));
553         }
554 }
555
556
557 /*
558 Signals and Windows
559 ===================
560 The SIGHUP signal does not exist on Windows and does not need to be handled.
561
562 Windows handles SIGFPE and SIGSEGV signals as expected.
563
564 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
565 cause a new thread to be spawned. This may well result in unexpected
566 behaviour by the single-threaded LyX.
567
568 SIGTERM signals will come only from another process actually sending
569 that signal using 'raise' in Windows' POSIX compatability layer. It will
570 not come from the general "terminate process" methods that everyone
571 actually uses (and which can't be trapped). Killing an app 'politely' on
572 Windows involves first sending a WM_CLOSE message, something that is
573 caught already by the Qt frontend.
574
575 For more information see:
576
577 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
578 ...signals are mostly useless on Windows for a variety of reasons that are
579 Windows specific...
580
581 'UNIX Application Migration Guide, Chapter 9'
582 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
583
584 'How To Terminate an Application "Cleanly" in Win32'
585 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
586 */
587 extern "C" {
588
589 static void error_handler(int err_sig)
590 {
591         // Throw away any signals other than the first one received.
592         static sig_atomic_t handling_error = false;
593         if (handling_error)
594                 return;
595         handling_error = true;
596
597         // We have received a signal indicating a fatal error, so
598         // try and save the data ASAP.
599         emergencyCleanup();
600
601         // These lyxerr calls may or may not work:
602
603         // Signals are asynchronous, so the main program may be in a very
604         // fragile state when a signal is processed and thus while a signal
605         // handler function executes.
606         // In general, therefore, we should avoid performing any
607         // I/O operations or calling most library and system functions from
608         // signal handlers.
609
610         // This shouldn't matter here, however, as we've already invoked
611         // emergencyCleanup.
612         switch (err_sig) {
613 #ifdef SIGHUP
614         case SIGHUP:
615                 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
616                 break;
617 #endif
618         case SIGFPE:
619                 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
620                 break;
621         case SIGSEGV:
622                 lyxerr << "\nlyx: SIGSEGV signal caught\n"
623                           "Sorry, you have found a bug in LyX. "
624                           "Please read the bug-reporting instructions "
625                           "in Help->Introduction and send us a bug report, "
626                           "if necessary. Thanks !\nBye." << endl;
627                 break;
628         case SIGINT:
629         case SIGTERM:
630                 // no comments
631                 break;
632         }
633
634         // Deinstall the signal handlers
635 #ifdef SIGHUP
636         signal(SIGHUP, SIG_DFL);
637 #endif
638         signal(SIGINT, SIG_DFL);
639         signal(SIGFPE, SIG_DFL);
640         signal(SIGSEGV, SIG_DFL);
641         signal(SIGTERM, SIG_DFL);
642
643 #ifdef SIGHUP
644         if (err_sig == SIGSEGV ||
645             (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
646 #else
647         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
648 #endif
649                 abort();
650         exit(0);
651 }
652
653 }
654
655
656 void LyX::printError(ErrorItem const & ei)
657 {
658         docstring tmp = _("LyX: ") + ei.error + char_type(':')
659                 + ei.description;
660         cerr << to_utf8(tmp) << endl;
661 }
662
663
664 bool LyX::init()
665 {
666 #ifdef SIGHUP
667         signal(SIGHUP, error_handler);
668 #endif
669         signal(SIGFPE, error_handler);
670         signal(SIGSEGV, error_handler);
671         signal(SIGINT, error_handler);
672         signal(SIGTERM, error_handler);
673         // SIGPIPE can be safely ignored.
674
675         lyxrc.tempdir_path = package().temp_dir().absFilename();
676         lyxrc.document_path = package().document_dir().absFilename();
677
678         if (lyxrc.example_path.empty()) {
679                 lyxrc.example_path = addPath(package().system_support().absFilename(),
680                                               "examples");
681         }
682         if (lyxrc.template_path.empty()) {
683                 lyxrc.template_path = addPath(package().system_support().absFilename(),
684                                               "templates");
685         }
686
687         //
688         // Read configuration files
689         //
690
691         // This one may have been distributed along with LyX.
692         if (!readRcFile("lyxrc.dist"))
693                 return false;
694
695         // Set the language defined by the distributor.
696         setRcGuiLanguage();
697
698         // Set the PATH correctly.
699 #if !defined (USE_POSIX_PACKAGING)
700         // Add the directory containing the LyX executable to the path
701         // so that LyX can find things like tex2lyx.
702         if (package().build_support().empty())
703                 prependEnvPath("PATH", package().binary_dir().absFilename());
704 #endif
705         if (!lyxrc.path_prefix.empty())
706                 prependEnvPath("PATH", lyxrc.path_prefix);
707
708         // Check that user LyX directory is ok.
709         if (queryUserLyXDir(package().explicit_user_support()))
710                 reconfigureUserLyXDir();
711
712         // no need for a splash when there is no GUI
713         if (!use_gui) {
714                 first_start = false;
715         }
716
717         // This one is generated in user_support directory by lib/configure.py.
718         if (!readRcFile("lyxrc.defaults"))
719                 return false;
720
721         // Query the OS to know what formats are viewed natively
722         formats.setAutoOpen();
723
724         // Read lyxrc.dist again to be able to override viewer auto-detection.
725         readRcFile("lyxrc.dist");
726
727         // Set again the language defined by the distributor.
728         setRcGuiLanguage();
729
730         system_lyxrc = lyxrc;
731         system_formats = formats;
732         pimpl_->system_converters_ = pimpl_->converters_;
733         pimpl_->system_movers_ = pimpl_->movers_;
734         system_lcolor = lcolor;
735
736         // This one is edited through the preferences dialog.
737         if (!readRcFile("preferences"))
738                 return false;
739
740         if (!readEncodingsFile("encodings", "unicodesymbols"))
741                 return false;
742         if (!readLanguagesFile("languages"))
743                 return false;
744
745         // Set the language defined by the user.
746         setRcGuiLanguage();
747
748         // Load the layouts
749         LYXERR(Debug::INIT, "Reading layouts...");
750         if (!LyXSetStyle())
751                 return false;
752         //...and the modules
753         theModuleList.read();
754
755         // read keymap and ui files in batch mode as well
756         // because InsetInfo needs to know these to produce
757         // the correct output
758
759         // Set up command definitions
760         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
761
762         // Set up bindings
763         pimpl_->toplevel_keymap_.read("site");
764         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
765         // load user bind file user.bind
766         pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
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.\n"
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--batch         execute commands and exit\n"
989                   "\t-version        summarize version and build info\n"
990                                "Check the LyX man page for more details.")) << endl;
991         exit(0);
992         return 0;
993 }
994
995
996 int parse_version(string const &, string const &, string &)
997 {
998         lyxerr << "LyX " << lyx_version
999                << " (" << lyx_release_date << ")" << endl;
1000         lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1001
1002         lyxerr << lyx_version_info << endl;
1003         exit(0);
1004         return 0;
1005 }
1006
1007
1008 int parse_sysdir(string const & arg, string const &, string &)
1009 {
1010         if (arg.empty()) {
1011                 Alert::error(_("No system directory"),
1012                         _("Missing directory for -sysdir switch"));
1013                 exit(1);
1014         }
1015         cl_system_support = arg;
1016         return 1;
1017 }
1018
1019
1020 int parse_userdir(string const & arg, string const &, string &)
1021 {
1022         if (arg.empty()) {
1023                 Alert::error(_("No user directory"),
1024                         _("Missing directory for -userdir switch"));
1025                 exit(1);
1026         }
1027         cl_user_support = arg;
1028         return 1;
1029 }
1030
1031
1032 int parse_execute(string const & arg, string const &, string & batch)
1033 {
1034         if (arg.empty()) {
1035                 Alert::error(_("Incomplete command"),
1036                         _("Missing command string after --execute switch"));
1037                 exit(1);
1038         }
1039         batch = arg;
1040         return 1;
1041 }
1042
1043
1044 int parse_export(string const & type, string const &, string & batch)
1045 {
1046         if (type.empty()) {
1047                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1048                                          "--export switch")) << endl;
1049                 exit(1);
1050         }
1051         batch = "buffer-export " + type;
1052         use_gui = false;
1053         return 1;
1054 }
1055
1056
1057 int parse_import(string const & type, string const & file, string & batch)
1058 {
1059         if (type.empty()) {
1060                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1061                                          "--import switch")) << endl;
1062                 exit(1);
1063         }
1064         if (file.empty()) {
1065                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1066                 exit(1);
1067         }
1068
1069         batch = "buffer-import " + type + ' ' + file;
1070         return 2;
1071 }
1072
1073
1074 int parse_geometry(string const & arg1, string const &, string &)
1075 {
1076         geometryArg = arg1;
1077         // don't remove "-geometry", it will be pruned out later in the
1078         // frontend if need be.
1079         return -1;
1080 }
1081
1082
1083 int parse_batch(string const &, string const &, string &) 
1084 {
1085         use_gui = false;
1086         return 0;
1087 }
1088
1089
1090 } // namespace anon
1091
1092
1093 void LyX::easyParse(int & argc, char * argv[])
1094 {
1095         map<string, cmd_helper> cmdmap;
1096
1097         cmdmap["-dbg"] = parse_dbg;
1098         cmdmap["-help"] = parse_help;
1099         cmdmap["--help"] = parse_help;
1100         cmdmap["-version"] = parse_version;
1101         cmdmap["--version"] = parse_version;
1102         cmdmap["-sysdir"] = parse_sysdir;
1103         cmdmap["-userdir"] = parse_userdir;
1104         cmdmap["-x"] = parse_execute;
1105         cmdmap["--execute"] = parse_execute;
1106         cmdmap["-e"] = parse_export;
1107         cmdmap["--export"] = parse_export;
1108         cmdmap["-i"] = parse_import;
1109         cmdmap["--import"] = parse_import;
1110         cmdmap["-geometry"] = parse_geometry;
1111         cmdmap["--batch"] = parse_batch;
1112
1113         for (int i = 1; i < argc; ++i) {
1114                 map<string, cmd_helper>::const_iterator it
1115                         = cmdmap.find(argv[i]);
1116
1117                 // don't complain if not found - may be parsed later
1118                 if (it == cmdmap.end())
1119                         continue;
1120
1121                 string const arg =
1122                         (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1123                 string const arg2 =
1124                         (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1125
1126                 string batch;
1127                 int const remove = 1 + it->second(arg, arg2, batch);
1128                 if (!batch.empty())
1129                         pimpl_->batch_commands.push_back(batch);
1130
1131                 // Now, remove used arguments by shifting
1132                 // the following ones remove places down.
1133                 if (remove > 0) {
1134                         argc -= remove;
1135                         for (int j = i; j < argc; ++j)
1136                                 argv[j] = argv[j + remove];
1137                         --i;
1138                 }
1139         }
1140 }
1141
1142
1143 FuncStatus getStatus(FuncRequest const & action)
1144 {
1145         LASSERT(singleton_, /**/);
1146         return singleton_->pimpl_->lyxfunc_.getStatus(action);
1147 }
1148
1149
1150 void dispatch(FuncRequest const & action)
1151 {
1152         LASSERT(singleton_, /**/);
1153         singleton_->pimpl_->lyxfunc_.dispatch(action);
1154 }
1155
1156
1157 BufferList & theBufferList()
1158 {
1159         LASSERT(singleton_, /**/);
1160         return singleton_->pimpl_->buffer_list_;
1161 }
1162
1163
1164 LyXFunc & theLyXFunc()
1165 {
1166         LASSERT(singleton_, /**/);
1167         return singleton_->pimpl_->lyxfunc_;
1168 }
1169
1170
1171 Server & theServer()
1172 {
1173         // FIXME: this should not be use_gui dependent
1174         LASSERT(use_gui, /**/);
1175         LASSERT(singleton_, /**/);
1176         return *singleton_->pimpl_->lyx_server_.get();
1177 }
1178
1179
1180 ServerSocket & theServerSocket()
1181 {
1182         // FIXME: this should not be use_gui dependent
1183         LASSERT(use_gui, /**/);
1184         LASSERT(singleton_, /**/);
1185         return *singleton_->pimpl_->lyx_socket_.get();
1186 }
1187
1188
1189 KeyMap & theTopLevelKeymap()
1190 {
1191         LASSERT(singleton_, /**/);
1192         return singleton_->pimpl_->toplevel_keymap_;
1193 }
1194
1195
1196 Converters & theConverters()
1197 {
1198         LASSERT(singleton_, /**/);
1199         return  singleton_->pimpl_->converters_;
1200 }
1201
1202
1203 Converters & theSystemConverters()
1204 {
1205         LASSERT(singleton_, /**/);
1206         return  singleton_->pimpl_->system_converters_;
1207 }
1208
1209
1210 Movers & theMovers()
1211 {
1212         LASSERT(singleton_, /**/);
1213         return singleton_->pimpl_->movers_;
1214 }
1215
1216
1217 Mover const & getMover(string  const & fmt)
1218 {
1219         LASSERT(singleton_, /**/);
1220         return singleton_->pimpl_->movers_(fmt);
1221 }
1222
1223
1224 void setMover(string const & fmt, string const & command)
1225 {
1226         LASSERT(singleton_, /**/);
1227         singleton_->pimpl_->movers_.set(fmt, command);
1228 }
1229
1230
1231 Movers & theSystemMovers()
1232 {
1233         LASSERT(singleton_, /**/);
1234         return singleton_->pimpl_->system_movers_;
1235 }
1236
1237
1238 Messages & getMessages(string const & language)
1239 {
1240         LASSERT(singleton_, /**/);
1241         return singleton_->messages(language);
1242 }
1243
1244
1245 Messages & getGuiMessages()
1246 {
1247         LASSERT(singleton_, /**/);
1248         return singleton_->pimpl_->messages_["GUI"];
1249 }
1250
1251
1252 graphics::Previews & thePreviews()
1253 {
1254         LASSERT(singleton_, /**/);
1255         return singleton_->pimpl_->preview_;
1256 }
1257
1258
1259 Session & theSession()
1260 {
1261         LASSERT(singleton_, /**/);
1262         return *singleton_->pimpl_->session_.get();
1263 }
1264
1265
1266 CmdDef & theTopLevelCmdDef()
1267 {
1268         LASSERT(singleton_, /**/);
1269         return singleton_->pimpl_->toplevel_cmddef_;
1270 }
1271
1272
1273 SpellChecker * theSpellChecker()
1274 {
1275         if (!singleton_->pimpl_->spell_checker_)
1276                 setSpellChecker();
1277         return singleton_->pimpl_->spell_checker_;
1278 }
1279
1280
1281 void setSpellChecker()
1282 {
1283 #if defined(USE_ASPELL)
1284         if (lyxrc.spellchecker == "aspell") {
1285                 if (!singleton_->pimpl_->aspell_checker_)
1286                         singleton_->pimpl_->aspell_checker_ = new AspellChecker();
1287                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1288                 return;
1289         }
1290 #endif
1291 #if defined(USE_HUNSPELL)
1292         if (lyxrc.spellchecker == "hunspell") {
1293                 if (!singleton_->pimpl_->hunspell_checker_)
1294                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker();
1295                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1296                 return;
1297         }
1298 #endif
1299         singleton_->pimpl_->spell_checker_ = 0;
1300 }
1301
1302 } // namespace lyx