]> git.lyx.org Git - features.git/blob - src/LyX.cpp
b777e55f4e45c289029333d1c4499a5bd047cb59
[features.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         {
758                 string const lock_file = package().user_support().absFileName() + ".lyx_configure_lock";
759                 int fd = fileLock(lock_file.c_str());
760
761                 if (queryUserLyXDir(package().explicit_user_support())) {
762                         reconfigureUserLyXDir();
763                 }
764                 fileUnlock(fd, lock_file.c_str());
765         }
766
767         if (!use_gui) {
768                 // No need for a splash when there is no GUI
769                 first_start = false;
770                 // Default is to overwrite the main file during export, unless
771                 // the -f switch was specified or LYX_FORCE_OVERWRITE was set
772                 if (force_overwrite == UNSPECIFIED) {
773                         string const what = getEnv("LYX_FORCE_OVERWRITE");
774                         if (what == "all")
775                                 force_overwrite = ALL_FILES;
776                         else if (what == "none")
777                                 force_overwrite = NO_FILES;
778                         else
779                                 force_overwrite = MAIN_FILE;
780                 }
781         }
782
783         // This one is generated in user_support directory by lib/configure.py.
784         if (!readRcFile("lyxrc.defaults"))
785                 return false;
786
787         // Query the OS to know what formats are viewed natively
788         formats.setAutoOpen();
789
790         // Read lyxrc.dist again to be able to override viewer auto-detection.
791         readRcFile("lyxrc.dist");
792
793         system_lyxrc = lyxrc;
794         system_formats = formats;
795         pimpl_->system_converters_ = pimpl_->converters_;
796         pimpl_->system_movers_ = pimpl_->movers_;
797         system_lcolor = lcolor;
798
799         // This one is edited through the preferences dialog.
800         if (!readRcFile("preferences", true))
801                 return false;
802
803         // The language may have been set to someting useful through prefs
804         setLocale();
805
806         if (!readEncodingsFile("encodings", "unicodesymbols"))
807                 return false;
808         if (!readLanguagesFile("languages"))
809                 return false;
810
811         LYXERR(Debug::INIT, "Reading layouts...");
812         // Load the layouts
813         LayoutFileList::get().read();
814         //...and the modules
815         theModuleList.read();
816
817         // read keymap and ui files in batch mode as well
818         // because InsetInfo needs to know these to produce
819         // the correct output
820
821         // Set up command definitions
822         pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
823
824         // FIXME
825         // Set up bindings
826         pimpl_->toplevel_keymap_.read("site");
827         pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
828         // load user bind file user.bind
829         pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
830
831         if (lyxerr.debugging(Debug::LYXRC))
832                 lyxrc.print();
833
834         os::windows_style_tex_paths(lyxrc.windows_style_tex_paths);
835         if (!lyxrc.path_prefix.empty())
836                 prependEnvPath("PATH", replaceEnvironmentPath(lyxrc.path_prefix));
837
838         FileName const document_path(lyxrc.document_path);
839         if (document_path.exists() && document_path.isDirectory())
840                 package().document_dir() = document_path;
841
842         package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
843         if (package().temp_dir().empty()) {
844                 Alert::error(_("Could not create temporary directory"),
845                              bformat(_("Could not create a temporary directory in\n"
846                                                        "\"%1$s\"\n"
847                                                            "Make sure that this path exists and is writable and try again."),
848                                      from_utf8(lyxrc.tempdir_path)));
849                 // createLyXTmpDir() tries sufficiently hard to create a
850                 // usable temp dir, so the probability to come here is
851                 // close to zero. We therefore don't try to overcome this
852                 // problem with e.g. asking the user for a new path and
853                 // trying again but simply exit.
854                 return false;
855         }
856
857         LYXERR(Debug::INIT, "LyX tmp dir: `"
858                             << package().temp_dir().absFileName() << '\'');
859
860         LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
861         pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
862
863         // This must happen after package initialization and after lyxrc is
864         // read, therefore it can't be done by a static object.
865         ConverterCache::init();
866
867         return true;
868 }
869
870
871 void emergencyCleanup()
872 {
873         // what to do about tmpfiles is non-obvious. we would
874         // like to delete any we find, but our lyxdir might
875         // contain documents etc. which might be helpful on
876         // a crash
877
878         singleton_->pimpl_->buffer_list_.emergencyWriteAll();
879         if (use_gui) {
880                 if (singleton_->pimpl_->lyx_server_)
881                         singleton_->pimpl_->lyx_server_->emergencyCleanup();
882                 singleton_->pimpl_->lyx_server_.reset();
883                 singleton_->pimpl_->lyx_socket_.reset();
884         }
885 }
886
887
888 // return true if file does not exist or is older than configure.py.
889 static bool needsUpdate(string const & file)
890 {
891         // We cannot initialize configure_script directly because the package
892         // is not initialized yet when static objects are constructed.
893         static FileName configure_script;
894         static bool firstrun = true;
895         if (firstrun) {
896                 configure_script =
897                         FileName(addName(package().system_support().absFileName(),
898                                 "configure.py"));
899                 firstrun = false;
900         }
901
902         FileName absfile =
903                 FileName(addName(package().user_support().absFileName(), file));
904         return !absfile.exists()
905                 || configure_script.lastModified() > absfile.lastModified();
906 }
907
908
909 bool LyX::queryUserLyXDir(bool explicit_userdir)
910 {
911         // Does user directory exist?
912         FileName const sup = package().user_support();
913         if (sup.exists() && sup.isDirectory()) {
914                 first_start = false;
915
916                 return needsUpdate("lyxrc.defaults")
917                         || needsUpdate("lyxmodules.lst")
918                         || needsUpdate("textclass.lst")
919                         || needsUpdate("packages.lst");
920         }
921
922         first_start = !explicit_userdir;
923
924         // If the user specified explicitly a directory, ask whether
925         // to create it. If the user says "no", then exit.
926         if (explicit_userdir &&
927             Alert::prompt(
928                     _("Missing user LyX directory"),
929                     bformat(_("You have specified a non-existent user "
930                                            "LyX directory, %1$s.\n"
931                                            "It is needed to keep your own configuration."),
932                             from_utf8(package().user_support().absFileName())),
933                     1, 0,
934                     _("&Create directory"),
935                     _("&Exit LyX"))) {
936                 lyxerr << to_utf8(_("No user LyX directory. Exiting.")) << endl;
937                 earlyExit(EXIT_FAILURE);
938         }
939
940         lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
941                           from_utf8(sup.absFileName()))) << endl;
942
943         if (!sup.createDirectory(0755)) {
944                 // Failed, so let's exit.
945                 lyxerr << to_utf8(_("Failed to create directory. Exiting."))
946                        << endl;
947                 earlyExit(EXIT_FAILURE);
948         }
949
950         return true;
951 }
952
953
954 bool LyX::readRcFile(string const & name, bool check_format)
955 {
956         LYXERR(Debug::INIT, "About to read " << name << "... ");
957
958         FileName const lyxrc_path = libFileSearch(string(), name);
959         if (lyxrc_path.empty()) {
960                 LYXERR(Debug::INIT, "Not found." << lyxrc_path);
961                 // FIXME
962                 // This was the previous logic, but can it be right??
963                 return true;
964         }
965         LYXERR(Debug::INIT, "Found in " << lyxrc_path);
966         bool const success = lyxrc.read(lyxrc_path, check_format);
967         if (!success)
968                 showFileError(name);
969         return success;
970 }
971
972 // Read the languages file `name'
973 bool LyX::readLanguagesFile(string const & name)
974 {
975         LYXERR(Debug::INIT, "About to read " << name << "...");
976
977         FileName const lang_path = libFileSearch(string(), name);
978         if (lang_path.empty()) {
979                 showFileError(name);
980                 return false;
981         }
982         languages.read(lang_path);
983         return true;
984 }
985
986
987 // Read the encodings file `name'
988 bool LyX::readEncodingsFile(string const & enc_name,
989                             string const & symbols_name)
990 {
991         LYXERR(Debug::INIT, "About to read " << enc_name << " and "
992                             << symbols_name << "...");
993
994         FileName const symbols_path = libFileSearch(string(), symbols_name);
995         if (symbols_path.empty()) {
996                 showFileError(symbols_name);
997                 return false;
998         }
999
1000         FileName const enc_path = libFileSearch(string(), enc_name);
1001         if (enc_path.empty()) {
1002                 showFileError(enc_name);
1003                 return false;
1004         }
1005         encodings.read(enc_path, symbols_path);
1006         return true;
1007 }
1008
1009
1010 namespace {
1011
1012 /// return the the number of arguments consumed
1013 typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
1014
1015 int parse_dbg(string const & arg, string const &, string &)
1016 {
1017         if (arg.empty()) {
1018                 cout << to_utf8(_("List of supported debug flags:")) << endl;
1019                 Debug::showTags(cout);
1020                 exit(0);
1021         }
1022         lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
1023
1024         lyxerr.setLevel(Debug::value(arg));
1025         Debug::showLevel(lyxerr, lyxerr.level());
1026         return 1;
1027 }
1028
1029
1030 int parse_help(string const &, string const &, string &)
1031 {
1032         cout <<
1033                 to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
1034                   "Command line switches (case sensitive):\n"
1035                   "\t-help              summarize LyX usage\n"
1036                   "\t-userdir dir       set user directory to dir\n"
1037                   "\t-sysdir dir        set system directory to dir\n"
1038                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
1039                   "\t-dbg feature[,feature]...\n"
1040                   "                  select the features to debug.\n"
1041                   "                  Type `lyx -dbg' to see the list of features\n"
1042                   "\t-x [--execute] command\n"
1043                   "                  where command is a lyx command.\n"
1044                   "\t-e [--export] fmt\n"
1045                   "                  where fmt is the export format of choice. Look in\n"
1046                   "                  Tools->Preferences->File Handling->File Formats->Short Name\n"
1047                   "                  to see which parameter (which differs from the format name\n"
1048                   "                  in the File->Export menu) should be passed.\n"
1049                   "                  Note that the order of -e and -x switches matters.\n"
1050                   "\t-E [--export-to] fmt filename\n"
1051                   "                  where fmt is the export format of choice (see --export),\n"
1052                   "                  and filename is the destination filename.\n"
1053                   "\t-i [--import] fmt file.xxx\n"
1054                   "                  where fmt is the import format of choice\n"
1055                   "                  and file.xxx is the file to be imported.\n"
1056                   "\t-f [--force-overwrite] what\n"
1057                   "                  where what is either `all', `main' or `none',\n"
1058                   "                  specifying whether all files, main file only, or no files,\n"
1059                   "                  respectively, are to be overwritten during a batch export.\n"
1060                   "                  Anything else is equivalent to `all', but is not consumed.\n"
1061                   "\t-n [--no-remote]\n"
1062                   "                  open documents in a new instance\n"
1063                   "\t-r [--remote]\n"
1064                   "                  open documents in an already running instance\n"
1065                   "                  (a working lyxpipe is needed)\n"
1066                   "\t-batch    execute commands without launching GUI and exit.\n"
1067                   "\t-version  summarize version and build info\n"
1068                                "Check the LyX man page for more details.")) << endl;
1069         exit(0);
1070         return 0;
1071 }
1072
1073
1074 int parse_version(string const &, string const &, string &)
1075 {
1076         cout << "LyX " << lyx_version
1077                << " (" << lyx_release_date << ")" << endl;
1078         cout << to_utf8(bformat(_("Built on %1$s[[date]], %2$s[[time]]"),
1079                 from_ascii(__DATE__), from_ascii(__TIME__))) << endl;
1080
1081         cout << lyx_version_info << endl;
1082         exit(0);
1083         return 0;
1084 }
1085
1086
1087 int parse_sysdir(string const & arg, string const &, string &)
1088 {
1089         if (arg.empty()) {
1090                 Alert::error(_("No system directory"),
1091                         _("Missing directory for -sysdir switch"));
1092                 exit(1);
1093         }
1094         cl_system_support = arg;
1095         return 1;
1096 }
1097
1098
1099 int parse_userdir(string const & arg, string const &, string &)
1100 {
1101         if (arg.empty()) {
1102                 Alert::error(_("No user directory"),
1103                         _("Missing directory for -userdir switch"));
1104                 exit(1);
1105         }
1106         cl_user_support = arg;
1107         return 1;
1108 }
1109
1110
1111 int parse_execute(string const & arg, string const &, string & batch)
1112 {
1113         if (arg.empty()) {
1114                 Alert::error(_("Incomplete command"),
1115                         _("Missing command string after --execute switch"));
1116                 exit(1);
1117         }
1118         batch = arg;
1119         return 1;
1120 }
1121
1122
1123 int parse_export_to(string const & type, string const & output_file, string & batch)
1124 {
1125         if (type.empty()) {
1126                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1127                                          "--export-to switch")) << endl;
1128                 exit(1);
1129         }
1130         if (output_file.empty()) {
1131                 lyxerr << to_utf8(_("Missing destination filename after "
1132                                          "--export-to switch")) << endl;
1133                 exit(1);
1134         }
1135         batch = "buffer-export " + type + " " + output_file;
1136         use_gui = false;
1137         return 2;
1138 }
1139
1140
1141 int parse_export(string const & type, string const &, string & batch)
1142 {
1143         if (type.empty()) {
1144                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1145                                          "--export switch")) << endl;
1146                 exit(1);
1147         }
1148         batch = "buffer-export " + type;
1149         use_gui = false;
1150         return 1;
1151 }
1152
1153
1154 int parse_import(string const & type, string const & file, string & batch)
1155 {
1156         if (type.empty()) {
1157                 lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
1158                                          "--import switch")) << endl;
1159                 exit(1);
1160         }
1161         if (file.empty()) {
1162                 lyxerr << to_utf8(_("Missing filename for --import")) << endl;
1163                 exit(1);
1164         }
1165         batch = "buffer-import " + type + ' ' + file;
1166         return 2;
1167 }
1168
1169
1170 int parse_geometry(string const & arg1, string const &, string &)
1171 {
1172         geometryArg = arg1;
1173         // don't remove "-geometry", it will be pruned out later in the
1174         // frontend if need be.
1175         return -1;
1176 }
1177
1178
1179 int parse_batch(string const &, string const &, string &)
1180 {
1181         use_gui = false;
1182         return 0;
1183 }
1184
1185
1186 int parse_noremote(string const &, string const &, string &)
1187 {
1188         run_mode = NEW_INSTANCE;
1189         return 0;
1190 }
1191
1192
1193 int parse_remote(string const &, string const &, string &)
1194 {
1195         run_mode = USE_REMOTE;
1196         return 0;
1197 }
1198
1199
1200 int parse_force(string const & arg, string const &, string &)
1201 {
1202         if (arg == "all") {
1203                 force_overwrite = ALL_FILES;
1204                 return 1;
1205         } else if (arg == "main") {
1206                 force_overwrite = MAIN_FILE;
1207                 return 1;
1208         } else if (arg == "none") {
1209                 force_overwrite = NO_FILES;
1210                 return 1;
1211         }
1212         force_overwrite = ALL_FILES;
1213         return 0;
1214 }
1215
1216
1217 } // namespace anon
1218
1219
1220 void LyX::easyParse(int & argc, char * argv[])
1221 {
1222         map<string, cmd_helper> cmdmap;
1223
1224         cmdmap["-dbg"] = parse_dbg;
1225         cmdmap["-help"] = parse_help;
1226         cmdmap["--help"] = parse_help;
1227         cmdmap["-version"] = parse_version;
1228         cmdmap["--version"] = parse_version;
1229         cmdmap["-sysdir"] = parse_sysdir;
1230         cmdmap["-userdir"] = parse_userdir;
1231         cmdmap["-x"] = parse_execute;
1232         cmdmap["--execute"] = parse_execute;
1233         cmdmap["-e"] = parse_export;
1234         cmdmap["--export"] = parse_export;
1235         cmdmap["-E"] = parse_export_to;
1236         cmdmap["--export-to"] = parse_export_to;
1237         cmdmap["-i"] = parse_import;
1238         cmdmap["--import"] = parse_import;
1239         cmdmap["-geometry"] = parse_geometry;
1240         cmdmap["-batch"] = parse_batch;
1241         cmdmap["-f"] = parse_force;
1242         cmdmap["--force-overwrite"] = parse_force;
1243         cmdmap["-n"] = parse_noremote;
1244         cmdmap["--no-remote"] = parse_noremote;
1245         cmdmap["-r"] = parse_remote;
1246         cmdmap["--remote"] = parse_remote;
1247
1248         for (int i = 1; i < argc; ++i) {
1249                 map<string, cmd_helper>::const_iterator it
1250                         = cmdmap.find(argv[i]);
1251
1252                 // don't complain if not found - may be parsed later
1253                 if (it == cmdmap.end())
1254                         continue;
1255
1256                 string const arg =
1257                         (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
1258                 string const arg2 =
1259                         (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
1260
1261                 string batch;
1262                 int const remove = 1 + it->second(arg, arg2, batch);
1263                 if (!batch.empty())
1264                         pimpl_->batch_commands.push_back(batch);
1265
1266                 // Now, remove used arguments by shifting
1267                 // the following ones remove places down.
1268                 if (remove > 0) {
1269                         os::remove_internal_args(i, remove);
1270                         argc -= remove;
1271                         for (int j = i; j < argc; ++j)
1272                                 argv[j] = argv[j + remove];
1273                         --i;
1274                 }
1275         }
1276 }
1277
1278
1279 FuncStatus getStatus(FuncRequest const & action)
1280 {
1281         LAPPERR(theApp());
1282         return theApp()->getStatus(action);
1283 }
1284
1285
1286 void dispatch(FuncRequest const & action)
1287 {
1288         LAPPERR(theApp());
1289         return theApp()->dispatch(action);
1290 }
1291
1292
1293 void dispatch(FuncRequest const & action, DispatchResult & dr)
1294 {
1295         LAPPERR(theApp());
1296         return theApp()->dispatch(action, dr);
1297 }
1298
1299
1300 vector<string> & theFilesToLoad()
1301 {
1302         LAPPERR(singleton_);
1303         return singleton_->pimpl_->files_to_load_;
1304 }
1305
1306
1307 BufferList & theBufferList()
1308 {
1309         LAPPERR(singleton_);
1310         return singleton_->pimpl_->buffer_list_;
1311 }
1312
1313
1314 Server & theServer()
1315 {
1316         // FIXME: this should not be use_gui dependent
1317         LWARNIF(use_gui);
1318         LAPPERR(singleton_);
1319         return *singleton_->pimpl_->lyx_server_.get();
1320 }
1321
1322
1323 ServerSocket & theServerSocket()
1324 {
1325         // FIXME: this should not be use_gui dependent
1326         LWARNIF(use_gui);
1327         LAPPERR(singleton_);
1328         return *singleton_->pimpl_->lyx_socket_.get();
1329 }
1330
1331
1332 KeyMap & theTopLevelKeymap()
1333 {
1334         LAPPERR(singleton_);
1335         return singleton_->pimpl_->toplevel_keymap_;
1336 }
1337
1338
1339 Converters & theConverters()
1340 {
1341         LAPPERR(singleton_);
1342         return  singleton_->pimpl_->converters_;
1343 }
1344
1345
1346 Converters & theSystemConverters()
1347 {
1348         LAPPERR(singleton_);
1349         return  singleton_->pimpl_->system_converters_;
1350 }
1351
1352
1353 Movers & theMovers()
1354 {
1355         LAPPERR(singleton_);
1356         return singleton_->pimpl_->movers_;
1357 }
1358
1359
1360 Mover const & getMover(string  const & fmt)
1361 {
1362         LAPPERR(singleton_);
1363         return singleton_->pimpl_->movers_(fmt);
1364 }
1365
1366
1367 void setMover(string const & fmt, string const & command)
1368 {
1369         LAPPERR(singleton_);
1370         singleton_->pimpl_->movers_.set(fmt, command);
1371 }
1372
1373
1374 Movers & theSystemMovers()
1375 {
1376         LAPPERR(singleton_);
1377         return singleton_->pimpl_->system_movers_;
1378 }
1379
1380
1381 Messages const & getMessages(string const & language)
1382 {
1383         LAPPERR(singleton_);
1384         return singleton_->messages(language);
1385 }
1386
1387
1388 Messages const & getGuiMessages()
1389 {
1390         LAPPERR(singleton_);
1391         return singleton_->messages(Messages::guiLanguage());
1392 }
1393
1394
1395 Session & theSession()
1396 {
1397         LAPPERR(singleton_);
1398         return *singleton_->pimpl_->session_.get();
1399 }
1400
1401
1402 LaTeXFonts & theLaTeXFonts()
1403 {
1404         LAPPERR(singleton_);
1405         if (!singleton_->pimpl_->latexfonts_)
1406                 singleton_->pimpl_->latexfonts_ = new LaTeXFonts;
1407         return *singleton_->pimpl_->latexfonts_;
1408 }
1409
1410
1411 CmdDef & theTopLevelCmdDef()
1412 {
1413         LAPPERR(singleton_);
1414         return singleton_->pimpl_->toplevel_cmddef_;
1415 }
1416
1417
1418 SpellChecker * theSpellChecker()
1419 {
1420         if (!singleton_->pimpl_->spell_checker_)
1421                 setSpellChecker();
1422         return singleton_->pimpl_->spell_checker_;
1423 }
1424
1425
1426 void setSpellChecker()
1427 {
1428         SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
1429                 singleton_->pimpl_->spell_checker_->changeNumber() : 0;
1430
1431         if (lyxrc.spellchecker == "native") {
1432 #if defined(USE_MACOSX_PACKAGING)
1433                 if (!singleton_->pimpl_->apple_spell_checker_)
1434                         singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker;
1435                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
1436 #else
1437                 singleton_->pimpl_->spell_checker_ = 0;
1438 #endif
1439         } else if (lyxrc.spellchecker == "aspell") {
1440 #if defined(USE_ASPELL)
1441                 if (!singleton_->pimpl_->aspell_checker_)
1442                         singleton_->pimpl_->aspell_checker_ = new AspellChecker;
1443                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
1444 #else
1445                 singleton_->pimpl_->spell_checker_ = 0;
1446 #endif
1447         } else if (lyxrc.spellchecker == "enchant") {
1448 #if defined(USE_ENCHANT)
1449                 if (!singleton_->pimpl_->enchant_checker_)
1450                         singleton_->pimpl_->enchant_checker_ = new EnchantChecker;
1451                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
1452 #else
1453                 singleton_->pimpl_->spell_checker_ = 0;
1454 #endif
1455         } else if (lyxrc.spellchecker == "hunspell") {
1456 #if defined(USE_HUNSPELL)
1457                 if (!singleton_->pimpl_->hunspell_checker_)
1458                         singleton_->pimpl_->hunspell_checker_ = new HunspellChecker;
1459                 singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
1460 #else
1461                 singleton_->pimpl_->spell_checker_ = 0;
1462 #endif
1463         } else {
1464                 singleton_->pimpl_->spell_checker_ = 0;
1465         }
1466         if (singleton_->pimpl_->spell_checker_) {
1467                 singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
1468                 singleton_->pimpl_->spell_checker_->advanceChangeNumber();
1469         }
1470 }
1471
1472 } // namespace lyx