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