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