]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
1.) some white spaces removed
[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                         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
315                         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
316                         DispatchResult dr;
317                         for (; bcit != bcend; bcit++) {
318                                 LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << *bcit);
319                                 buf->dispatch(*bcit, dr);
320                                 final_success |= !dr.error();
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--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         return singleton_->pimpl_->spell_checker_;
1276 }
1277
1278 } // namespace lyx