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