]> git.lyx.org Git - lyx.git/blob - src/LyX.cpp
grammar
[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         // Write the index file of the converter cache
374         ConverterCache::get().writeIndex();
375
376         // close buffers first
377         pimpl_->buffer_list_.closeAll();
378
379         // register session changes and shutdown server and socket
380         if (use_gui) {
381                 if (pimpl_->session_)
382                         pimpl_->session_->writeFile();
383                 pimpl_->session_.reset();
384                 pimpl_->lyx_server_.reset();
385                 pimpl_->lyx_socket_.reset();
386         }
387
388         // do any other cleanup procedures now
389         if (package().temp_dir() != package().system_temp_dir()) {
390                 string const abs_tmpdir = package().temp_dir().absFilename();
391                 if (!contains(package().temp_dir().absFilename(), "lyx_tmpdir")) {
392                         docstring const msg =
393                                 bformat(_("%1$s does not appear like a LyX created temporary directory."),
394                                 from_utf8(abs_tmpdir));
395                         Alert::warning(_("Cannot remove temporary directory"), msg);
396                 } else {
397                         LYXERR(Debug::INFO, "Deleting tmp dir "
398                                 << package().temp_dir().absFilename());
399                         if (!package().temp_dir().destroyDirectory()) {
400                                 docstring const msg =
401                                         bformat(_("Unable to remove the temporary directory %1$s"),
402                                         from_utf8(package().temp_dir().absFilename()));
403                                 Alert::warning(_("Unable to remove temporary directory"), msg);
404                         }
405                 }
406         }
407
408         // Kill the application object before exiting. This avoids crashes
409         // when exiting on Linux.
410         if (pimpl_->application_)
411                 pimpl_->application_.reset();
412 }
413
414
415 void LyX::earlyExit(int status)
416 {
417         LASSERT(pimpl_->application_.get(), /**/);
418         // LyX::pimpl_::application_ is not initialised at this
419         // point so it's safe to just exit after some cleanup.
420         prepareExit();
421         exit(status);
422 }
423
424
425 int LyX::init(int & argc, char * argv[])
426 {
427         // check for any spurious extra arguments
428         // other than documents
429         for (int argi = 1; argi < argc ; ++argi) {
430                 if (argv[argi][0] == '-') {
431                         lyxerr << to_utf8(
432                                 bformat(_("Wrong command line option `%1$s'. Exiting."),
433                                 from_utf8(argv[argi]))) << endl;
434                         return EXIT_FAILURE;
435                 }
436         }
437
438         // Initialization of LyX (reads lyxrc and more)
439         LYXERR(Debug::INIT, "Initializing LyX::init...");
440         bool success = init();
441         LYXERR(Debug::INIT, "Initializing LyX::init...done");
442         if (!success)
443                 return EXIT_FAILURE;
444
445         // Remaining arguments are assumed to be files to load.
446         for (int argi = argc - 1; argi >= 1; --argi)
447                 pimpl_->files_to_load_.push_back(to_utf8(from_local8bit(argv[argi])));
448
449         if (first_start) {
450                 pimpl_->files_to_load_.push_back(
451                         i18nLibFileSearch("examples", "splash.lyx").absFilename());
452         }
453
454         return EXIT_SUCCESS;
455 }
456
457
458 bool LyX::loadFiles()
459 {
460         LASSERT(!use_gui, /**/);
461         bool success = true;
462         vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
463         vector<string>::const_iterator end = pimpl_->files_to_load_.end();
464
465         for (; it != end; ++it) {
466                 // get absolute path of file and add ".lyx" to
467                 // the filename if necessary
468                 FileName fname = fileSearch(string(), os::internal_path(*it), "lyx",
469                         may_not_exist);
470
471                 if (fname.empty())
472                         continue;
473
474                 Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFilename(), false);
475                 if (buf->loadLyXFile(fname)) {
476                         ErrorList const & el = buf->errorList("Parse");
477                         if (!el.empty())
478                                 for_each(el.begin(), el.end(),
479                                 boost::bind(&LyX::printError, this, _1));
480                 }
481                 else {
482                         pimpl_->buffer_list_.release(buf);
483                         success = false;
484                 }
485         }
486         return success;
487 }
488
489
490 void execBatchCommands()
491 {
492         LASSERT(singleton_, /**/);
493         singleton_->execCommands();
494 }
495
496
497 void LyX::execCommands()
498 {
499         // The advantage of doing this here is that the event loop
500         // is already started. So any need for interaction will be
501         // aknowledged.
502
503         // if reconfiguration is needed.
504         while (LayoutFileList::get().empty()) {
505                 switch (Alert::prompt(
506                         _("No textclass is found"),
507                         _("LyX cannot continue because no textclass is found. "
508                                 "You can either reconfigure normally, or reconfigure using "
509                                 "default textclasses, or quit LyX."),
510                         0, 2,
511                         _("&Reconfigure"),
512                         _("&Use Default"),
513                         _("&Exit LyX")))
514                 {
515                 case 0:
516                         // regular reconfigure
517                         pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
518                         break;
519                 case 1:
520                         // reconfigure --without-latex-config
521                         pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
522                                 " --without-latex-config"));
523                         break;
524                 default:
525                         pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
526                         return;
527                 }
528         }
529         
530         // create the first main window
531         pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
532
533         if (!pimpl_->files_to_load_.empty()) {
534                 // if some files were specified at command-line we assume that the
535                 // user wants to edit *these* files and not to restore the session.
536                 for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
537                         pimpl_->lyxfunc_.dispatch(
538                                 FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
539                 }
540                 // clear this list to save a few bytes of RAM
541                 pimpl_->files_to_load_.clear();
542         } else
543                 pimpl_->application_->restoreGuiSession();
544
545         // Execute batch commands if available
546         if (pimpl_->batch_commands.empty())
547                 return;
548
549         vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
550         vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
551         for (; bcit != bcend; bcit++) {
552                 LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
553                 pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(*bcit));
554         }
555 }
556
557
558 /*
559 Signals and Windows
560 ===================
561 The SIGHUP signal does not exist on Windows and does not need to be handled.
562
563 Windows handles SIGFPE and SIGSEGV signals as expected.
564
565 Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
566 cause a new thread to be spawned. This may well result in unexpected
567 behaviour by the single-threaded LyX.
568
569 SIGTERM signals will come only from another process actually sending
570 that signal using 'raise' in Windows' POSIX compatability layer. It will
571 not come from the general "terminate process" methods that everyone
572 actually uses (and which can't be trapped). Killing an app 'politely' on
573 Windows involves first sending a WM_CLOSE message, something that is
574 caught already by the Qt frontend.
575
576 For more information see:
577
578 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
579 ...signals are mostly useless on Windows for a variety of reasons that are
580 Windows specific...
581
582 'UNIX Application Migration Guide, Chapter 9'
583 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
584
585 'How To Terminate an Application "Cleanly" in Win32'
586 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
587 */
588 extern "C" {
589
590 static void error_handler(int err_sig)
591 {
592         // Throw away any signals other than the first one received.
593         static sig_atomic_t handling_error = false;
594         if (handling_error)
595                 return;
596         handling_error = true;
597
598         // We have received a signal indicating a fatal error, so
599         // try and save the data ASAP.
600         emergencyCleanup();
601
602         // These lyxerr calls may or may not work:
603
604         // Signals are asynchronous, so the main program may be in a very
605         // fragile state when a signal is processed and thus while a signal
606         // handler function executes.
607         // In general, therefore, we should avoid performing any
608         // I/O operations or calling most library and system functions from
609         // signal handlers.
610
611         // This shouldn't matter here, however, as we've already invoked
612         // emergencyCleanup.
613         switch (err_sig) {
614 #ifdef SIGHUP
615         case SIGHUP:
616                 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
617                 break;
618 #endif
619         case SIGFPE:
620                 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
621                 break;
622         case SIGSEGV:
623                 lyxerr << "\nlyx: SIGSEGV signal caught\n"
624                           "Sorry, you have found a bug in LyX. "
625                           "Please read the bug-reporting instructions "
626                           "in Help->Introduction and send us a bug report, "
627                           "if necessary. Thanks !\nBye." << endl;
628                 break;
629         case SIGINT:
630         case SIGTERM:
631                 // no comments
632                 break;
633         }
634
635         // Deinstall the signal handlers
636 #ifdef SIGHUP
637         signal(SIGHUP, SIG_DFL);
638 #endif
639         signal(SIGINT, SIG_DFL);
640         signal(SIGFPE, SIG_DFL);
641         signal(SIGSEGV, SIG_DFL);
642         signal(SIGTERM, SIG_DFL);
643
644 #ifdef SIGHUP
645         if (err_sig == SIGSEGV ||
646             (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
647 #else
648         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
649 #endif
650                 abort();
651         exit(0);
652 }
653
654 }
655
656
657 void LyX::printError(ErrorItem const & ei)
658 {
659         docstring tmp = _("LyX: ") + ei.error + char_type(':')
660                 + ei.description;
661         cerr << to_utf8(tmp) << endl;
662 }
663
664
665 bool LyX::init()
666 {
667 #ifdef SIGHUP
668         signal(SIGHUP, error_handler);
669 #endif
670         signal(SIGFPE, error_handler);
671         signal(SIGSEGV, error_handler);
672         signal(SIGINT, error_handler);
673         signal(SIGTERM, error_handler);
674         // SIGPIPE can be safely ignored.
675
676         lyxrc.tempdir_path = package().temp_dir().absFilename();
677         lyxrc.document_path = package().document_dir().absFilename();
678
679         if (lyxrc.example_path.empty()) {
680                 lyxrc.example_path = addPath(package().system_support().absFilename(),
681                                               "examples");
682         }
683         if (lyxrc.template_path.empty()) {
684                 lyxrc.template_path = addPath(package().system_support().absFilename(),
685                                               "templates");
686         }
687
688         //
689         // Read configuration files
690         //
691
692         // This one may have been distributed along with LyX.
693         if (!readRcFile("lyxrc.dist"))
694                 return false;
695
696         // Set the language defined by the distributor.
697         setRcGuiLanguage();
698
699         // Set the PATH correctly.
700 #if !defined (USE_POSIX_PACKAGING)
701         // Add the directory containing the LyX executable to the path
702         // so that LyX can find things like tex2lyx.
703         if (package().build_support().empty())
704                 prependEnvPath("PATH", package().binary_dir().absFilename());
705 #endif
706         if (!lyxrc.path_prefix.empty())
707                 prependEnvPath("PATH", lyxrc.path_prefix);
708
709         // Check that user LyX directory is ok.
710         if (queryUserLyXDir(package().explicit_user_support()))
711                 reconfigureUserLyXDir();
712
713         // no need for a splash when there is no GUI
714         if (!use_gui) {
715                 first_start = false;
716         }
717
718         // This one is generated in user_support directory by lib/configure.py.
719         if (!readRcFile("lyxrc.defaults"))
720                 return false;
721
722         // Query the OS to know what formats are viewed natively
723         formats.setAutoOpen();
724
725         // Read lyxrc.dist again to be able to override viewer auto-detection.
726         readRcFile("lyxrc.dist");
727
728         // Set again the language defined by the distributor.
729         setRcGuiLanguage();
730
731         system_lyxrc = lyxrc;
732         system_formats = formats;
733         pimpl_->system_converters_ = pimpl_->converters_;
734         pimpl_->system_movers_ = pimpl_->movers_;
735         system_lcolor = lcolor;
736
737         // This one is edited through the preferences dialog.
738         if (!readRcFile("preferences"))
739                 return false;
740
741         if (!readEncodingsFile("encodings", "unicodesymbols"))
742                 return false;
743         if (!readLanguagesFile("languages"))
744                 return false;
745
746         // Set the language defined by the user.
747         setRcGuiLanguage();
748
749         // Load the layouts
750         LYXERR(Debug::INIT, "Reading layouts...");
751         if (!LyXSetStyle())
752                 return false;
753         //...and the modules
754         moduleList.read();
755
756         // read keymap and ui files in batch mode as well
757         // because InsetInfo needs to know these to produce
758         // the correct output
759
760         // Set up command definitions
761         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
762
763         // Set up bindings
764         pimpl_->toplevel_keymap_.read("site");
765         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
766         // load user bind file user.bind
767         pimpl_->toplevel_keymap_.read("user");
768
769         pimpl_->lyxfunc_.initKeySequences(&pimpl_->toplevel_keymap_);
770
771         if (lyxerr.debugging(Debug::LYXRC))
772                 lyxrc.print();
773
774         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
775         if (!lyxrc.path_prefix.empty())
776                 prependEnvPath("PATH", lyxrc.path_prefix);
777
778         FileName const document_path(lyxrc.document_path);
779         if (document_path.exists() && document_path.isDirectory())
780                 package().document_dir() = document_path;
781
782         package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
783         if (package().temp_dir().empty()) {
784                 Alert::error(_("Could not create temporary directory"),
785                              bformat(_("Could not create a temporary directory in\n"
786                                                        "\"%1$s\"\n"
787                                                            "Make sure that this path exists and is writable and try again."),
788                                      from_utf8(lyxrc.tempdir_path)));
789                 // createLyXTmpDir() tries sufficiently hard to create a
790                 // usable temp dir, so the probability to come here is
791                 // close to zero. We therefore don't try to overcome this
792                 // problem with e.g. asking the user for a new path and
793                 // trying again but simply exit.
794                 return false;
795         }
796
797         LYXERR(Debug::INIT, "LyX tmp dir: `"
798                             << package().temp_dir().absFilename() << '\'');
799
800         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
801         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
802
803         // This must happen after package initialization and after lyxrc is
804         // read, therefore it can't be done by a static object.
805         ConverterCache::init();
806                 
807         return true;
808 }
809
810
811 void emergencyCleanup()
812 {
813         // what to do about tmpfiles is non-obvious. we would
814         // like to delete any we find, but our lyxdir might
815         // contain documents etc. which might be helpful on
816         // a crash
817
818         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
819         if (use_gui) {
820                 if (singleton_->pimpl_->lyx_server_)
821                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
822                 singleton_->pimpl_->lyx_server_.reset();
823                 singleton_->pimpl_->lyx_socket_.reset();
824         }
825 }
826
827
828 // return true if file does not exist or is older than configure.py.
829 static bool needsUpdate(string const & file)
830 {
831         // We cannot initialize configure_script directly because the package
832         // is not initialized yet when  static objects are constructed.
833         static FileName configure_script;
834         static bool firstrun = true;
835         if (firstrun) {
836                 configure_script =
837                         FileName(addName(package().system_support().absFilename(),
838                                 "configure.py"));
839                 firstrun = false;
840         }
841
842         FileName absfile = 
843                 FileName(addName(package().user_support().absFilename(), file));
844         return !absfile.exists()
845                 || configure_script.lastModified() > absfile.lastModified();
846 }
847
848
849 bool LyX::queryUserLyXDir(bool explicit_userdir)
850 {
851         // Does user directory exist?
852         FileName const sup = package().user_support();
853         if (sup.exists() && sup.isDirectory()) {
854                 first_start = false;
855
856                 return needsUpdate("lyxrc.defaults")
857                         || needsUpdate("lyxmodules.lst")
858                         || needsUpdate("textclass.lst")
859                         || needsUpdate("packages.lst");
860         }
861
862         first_start = !explicit_userdir;
863
864         // If the user specified explicitly a directory, ask whether
865         // to create it. If the user says "no", then exit.
866         if (explicit_userdir &&
867             Alert::prompt(
868                     _("Missing user LyX directory"),
869                     bformat(_("You have specified a non-existent user "
870                                            "LyX directory, %1$s.\n"
871                                            "It is needed to keep your own configuration."),
872                             from_utf8(package().user_support().absFilename())),
873                     1, 0,
874                     _("&Create directory"),
875                     _("&Exit LyX"))) {
876                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
877                 earlyExit(EXIT_FAILURE);
878         }
879
880         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
881                           from_utf8(sup.absFilename()))) << endl;
882
883         if (!sup.createDirectory(0755)) {
884                 // Failed, so let's exit.
885                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
886                        << endl;
887                 earlyExit(EXIT_FAILURE);
888         }
889
890         return true;
891 }
892
893
894 bool LyX::readRcFile(string const & name)
895 {
896         LYXERR(Debug::INIT, "About to read " << name << "... ");
897
898         FileName const lyxrc_path = libFileSearch(string(), name);
899         if (!lyxrc_path.empty()) {
900                 LYXERR(Debug::INIT, "Found in " << lyxrc_path);
901                 if (lyxrc.read(lyxrc_path) < 0) {
902                         showFileError(name);
903                         return false;
904                 }
905         } else {
906                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
907         }
908         return true;
909 }
910
911 // Read the languages file `name'
912 bool LyX::readLanguagesFile(string const & name)
913 {
914         LYXERR(Debug::INIT, "About to read " << name << "...");
915
916         FileName const lang_path = libFileSearch(string(), name);
917         if (lang_path.empty()) {
918                 showFileError(name);
919                 return false;
920         }
921         languages.read(lang_path);
922         return true;
923 }
924
925
926 // Read the encodings file `name'
927 bool LyX::readEncodingsFile(string const & enc_name,
928                             string const & symbols_name)
929 {
930         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
931                             << symbols_name << "...");
932
933         FileName const symbols_path = libFileSearch(string(), symbols_name);
934         if (symbols_path.empty()) {
935                 showFileError(symbols_name);
936                 return false;
937         }
938
939         FileName const enc_path = libFileSearch(string(), enc_name);
940         if (enc_path.empty()) {
941                 showFileError(enc_name);
942                 return false;
943         }
944         encodings.read(enc_path, symbols_path);
945         return true;
946 }
947
948
949 namespace {
950
951 /// return the the number of arguments consumed
952 typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
953
954 int parse_dbg(string const & arg, string const &, string &)
955 {
956         if (arg.empty()) {
957                 lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
958                 Debug::showTags(lyxerr);
959                 exit(0);
960         }
961         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
962
963         lyxerr.level(Debug::value(arg));
964         Debug::showLevel(lyxerr, lyxerr.level());
965         return 1;
966 }
967
968
969 int parse_help(string const &, string const &, string &)
970 {
971         lyxerr <<
972                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
973                   "Command line switches (case sensitive):\n"
974                   "\t-help              summarize LyX usage\n"
975                   "\t-userdir dir       set user directory to dir\n"
976                   "\t-sysdir dir        set system directory to dir\n"
977                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
978                   "\t-dbg feature[,feature]...\n"
979                   "                  select the features to debug.\n"
980                   "                  Type `lyx -dbg' to see the list of features\n"
981                   "\t-x [--execute] command\n"
982                   "                  where command is a lyx command.\n"
983                   "\t-e [--export] fmt\n"
984                   "                  where fmt is the export format of choice.\n"
985                   "                  Look on Tools->Preferences->File formats->Format\n"
986                   "                  to get an idea which parameters should be passed.\n"
987                   "                  Note that the order of -e and -x switches matters."
988                   "\t-i [--import] fmt file.xxx\n"
989                   "                  where fmt is the import format of choice\n"
990                   "                  and file.xxx is the file to be imported.\n"
991                   "\t--batch         execute commands and exit\n"
992                   "\t-version        summarize version and build info\n"
993                                "Check the LyX man page for more details.")) << endl;
994         exit(0);
995         return 0;
996 }
997
998
999 int parse_version(string const &, string const &, string &)
1000 {
1001         lyxerr << "LyX " << lyx_version
1002                << " (" << lyx_release_date << ")" << endl;
1003         lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
1004
1005         lyxerr << lyx_version_info << endl;
1006         exit(0);
1007         return 0;
1008 }
1009
1010
1011 int parse_sysdir(string const & arg, string const &, string &)
1012 {
1013         if (arg.empty()) {
1014                 Alert::error(_("No system directory"),
1015                         _("Missing directory for -sysdir switch"));
1016                 exit(1);
1017         }
1018         cl_system_support = arg;
1019         return 1;
1020 }
1021
1022
1023 int parse_userdir(string const & arg, string const &, string &)
1024 {
1025         if (arg.empty()) {
1026                 Alert::error(_("No user directory"),
1027                         _("Missing directory for -userdir switch"));
1028                 exit(1);
1029         }
1030         cl_user_support = arg;
1031         return 1;
1032 }
1033
1034
1035 int parse_execute(string const & arg, string const &, string & batch)
1036 {
1037         if (arg.empty()) {
1038                 Alert::error(_("Incomplete command"),
1039                         _("Missing command string after --execute switch"));
1040                 exit(1);
1041         }
1042         batch = arg;
1043         return 1;
1044 }
1045
1046
1047 int parse_export(string const & type, string const &, string & batch)
1048 {
1049         if (type.empty()) {
1050                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1051                                          "--export switch")) << endl;
1052                 exit(1);
1053         }
1054         batch = "buffer-export " + type;
1055         use_gui = false;
1056         return 1;
1057 }
1058
1059
1060 int parse_import(string const & type, string const & file, string & batch)
1061 {
1062         if (type.empty()) {
1063                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1064                                          "--import switch")) << endl;
1065                 exit(1);
1066         }
1067         if (file.empty()) {
1068                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1069                 exit(1);
1070         }
1071
1072         batch = "buffer-import " + type + ' ' + file;
1073         return 2;
1074 }
1075
1076
1077 int parse_geometry(string const & arg1, string const &, string &)
1078 {
1079         geometryArg = arg1;
1080         // don't remove "-geometry", it will be pruned out later in the
1081         // frontend if need be.
1082         return -1;
1083 }
1084
1085
1086 int parse_batch(string const &, string const &, string &) 
1087 {
1088         use_gui = false;
1089         return 0;
1090 }
1091
1092
1093 } // namespace anon
1094
1095
1096 void LyX::easyParse(int & argc, char * argv[])
1097 {
1098         map<string, cmd_helper> cmdmap;
1099
1100         cmdmap["-dbg"] = parse_dbg;
1101         cmdmap["-help"] = parse_help;
1102         cmdmap["--help"] = parse_help;
1103         cmdmap["-version"] = parse_version;
1104         cmdmap["--version"] = parse_version;
1105         cmdmap["-sysdir"] = parse_sysdir;
1106         cmdmap["-userdir"] = parse_userdir;
1107         cmdmap["-x"] = parse_execute;
1108         cmdmap["--execute"] = parse_execute;
1109         cmdmap["-e"] = parse_export;
1110         cmdmap["--export"] = parse_export;
1111         cmdmap["-i"] = parse_import;
1112         cmdmap["--import"] = parse_import;
1113         cmdmap["-geometry"] = parse_geometry;
1114         cmdmap["--batch"] = parse_batch;
1115
1116         for (int i = 1; i < argc; ++i) {
1117                 map<string, cmd_helper>::const_iterator it
1118                         = cmdmap.find(argv[i]);
1119
1120                 // don't complain if not found - may be parsed later
1121                 if (it == cmdmap.end())
1122                         continue;
1123
1124                 string const arg =
1125                         (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
1126                 string const arg2 =
1127                         (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
1128
1129                 string batch;
1130                 int const remove = 1 + it->second(arg, arg2, batch);
1131                 if (!batch.empty())
1132                         pimpl_->batch_commands.push_back(batch);
1133
1134                 // Now, remove used arguments by shifting
1135                 // the following ones remove places down.
1136                 if (remove > 0) {
1137                         argc -= remove;
1138                         for (int j = i; j < argc; ++j)
1139                                 argv[j] = argv[j + remove];
1140                         --i;
1141                 }
1142         }
1143 }
1144
1145
1146 FuncStatus getStatus(FuncRequest const & action)
1147 {
1148         LASSERT(singleton_, /**/);
1149         return singleton_->pimpl_->lyxfunc_.getStatus(action);
1150 }
1151
1152
1153 void dispatch(FuncRequest const & action)
1154 {
1155         LASSERT(singleton_, /**/);
1156         singleton_->pimpl_->lyxfunc_.dispatch(action);
1157 }
1158
1159
1160 BufferList & theBufferList()
1161 {
1162         LASSERT(singleton_, /**/);
1163         return singleton_->pimpl_->buffer_list_;
1164 }
1165
1166
1167 LyXFunc & theLyXFunc()
1168 {
1169         LASSERT(singleton_, /**/);
1170         return singleton_->pimpl_->lyxfunc_;
1171 }
1172
1173
1174 Server & theServer()
1175 {
1176         // FIXME: this should not be use_gui dependent
1177         LASSERT(use_gui, /**/);
1178         LASSERT(singleton_, /**/);
1179         return *singleton_->pimpl_->lyx_server_.get();
1180 }
1181
1182
1183 ServerSocket & theServerSocket()
1184 {
1185         // FIXME: this should not be use_gui dependent
1186         LASSERT(use_gui, /**/);
1187         LASSERT(singleton_, /**/);
1188         return *singleton_->pimpl_->lyx_socket_.get();
1189 }
1190
1191
1192 KeyMap & theTopLevelKeymap()
1193 {
1194         LASSERT(singleton_, /**/);
1195         return singleton_->pimpl_->toplevel_keymap_;
1196 }
1197
1198
1199 Converters & theConverters()
1200 {
1201         LASSERT(singleton_, /**/);
1202         return  singleton_->pimpl_->converters_;
1203 }
1204
1205
1206 Converters & theSystemConverters()
1207 {
1208         LASSERT(singleton_, /**/);
1209         return  singleton_->pimpl_->system_converters_;
1210 }
1211
1212
1213 Movers & theMovers()
1214 {
1215         LASSERT(singleton_, /**/);
1216         return singleton_->pimpl_->movers_;
1217 }
1218
1219
1220 Mover const & getMover(string  const & fmt)
1221 {
1222         LASSERT(singleton_, /**/);
1223         return singleton_->pimpl_->movers_(fmt);
1224 }
1225
1226
1227 void setMover(string const & fmt, string const & command)
1228 {
1229         LASSERT(singleton_, /**/);
1230         singleton_->pimpl_->movers_.set(fmt, command);
1231 }
1232
1233
1234 Movers & theSystemMovers()
1235 {
1236         LASSERT(singleton_, /**/);
1237         return singleton_->pimpl_->system_movers_;
1238 }
1239
1240
1241 Messages & getMessages(string const & language)
1242 {
1243         LASSERT(singleton_, /**/);
1244         return singleton_->messages(language);
1245 }
1246
1247
1248 Messages & getGuiMessages()
1249 {
1250         LASSERT(singleton_, /**/);
1251         return singleton_->pimpl_->messages_["GUI"];
1252 }
1253
1254
1255 graphics::Previews & thePreviews()
1256 {
1257         LASSERT(singleton_, /**/);
1258         return singleton_->pimpl_->preview_;
1259 }
1260
1261
1262 Session & theSession()
1263 {
1264         LASSERT(singleton_, /**/);
1265         return *singleton_->pimpl_->session_.get();
1266 }
1267
1268
1269 CmdDef & theTopLevelCmdDef()
1270 {
1271         LASSERT(singleton_, /**/);
1272         return singleton_->pimpl_->toplevel_cmddef_;
1273 }
1274
1275
1276 SpellChecker * theSpellChecker()
1277 {
1278         return singleton_->pimpl_->spell_checker_;
1279 }
1280
1281 } // namespace lyx