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