]> git.lyx.org Git - lyx.git/blob - src/lyx_main.C
Fix bug 2485 and crash on middle mouse paste on math
[lyx.git] / src / lyx_main.C
1 /**
2  * \file lyx_main.C
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_main.h"
19
20 #include "buffer.h"
21 #include "buffer_funcs.h"
22 #include "bufferlist.h"
23 #include "converter.h"
24 #include "debug.h"
25 #include "encoding.h"
26 #include "errorlist.h"
27 #include "format.h"
28 #include "gettext.h"
29 #include "kbmap.h"
30 #include "language.h"
31 #include "session.h"
32 #include "LColor.h"
33 #include "lyx_cb.h"
34 #include "lyxfunc.h"
35 #include "lyxlex.h"
36 #include "lyxrc.h"
37 #include "lyxtextclasslist.h"
38 #include "lyxserver.h"
39 #include "MenuBackend.h"
40 #include "mover.h"
41 #include "ToolbarBackend.h"
42
43 #include "mathed/math_inset.h"
44
45 #include "frontends/Alert.h"
46 #include "frontends/lyx_gui.h"
47 #include "frontends/LyXView.h"
48
49 #include "support/environment.h"
50 #include "support/filetools.h"
51 #include "support/lyxlib.h"
52 #include "support/os.h"
53 #include "support/package.h"
54 #include "support/path.h"
55
56 #include <boost/bind.hpp>
57 #include <boost/filesystem/operations.hpp>
58
59 #include <iostream>
60 #include <csignal>
61
62 using lyx::support::addName;
63 using lyx::support::addPath;
64 using lyx::support::bformat;
65 using lyx::support::createDirectory;
66 using lyx::support::createLyXTmpDir;
67 using lyx::support::fileSearch;
68 using lyx::support::getEnv;
69 using lyx::support::i18nLibFileSearch;
70 using lyx::support::libFileSearch;
71 using lyx::support::package;
72 using lyx::support::Path;
73 using lyx::support::prependEnvPath;
74 using lyx::support::quoteName;
75 using lyx::support::rtrim;
76
77 namespace os = lyx::support::os;
78 namespace fs = boost::filesystem;
79
80 using std::endl;
81 using std::string;
82 using std::vector;
83
84 #ifndef CXX_GLOBAL_CSTD
85 using std::exit;
86 using std::signal;
87 using std::system;
88 #endif
89
90
91 extern LyXServer * lyxserver;
92
93 // This is the global bufferlist object
94 BufferList bufferlist;
95
96 // convenient to have it here.
97 boost::scoped_ptr<kb_keymap> toplevel_keymap;
98
99 namespace {
100
101 // Filled with the command line arguments "foo" of "-sysdir foo" or
102 // "-userdir foo".
103 string cl_system_support;
104 string cl_user_support;
105
106
107 void showFileError(string const & error)
108 {
109         Alert::warning(_("Could not read configuration file"),
110                    bformat(_("Error while reading the configuration file\n%1$s.\n"
111                      "Please check your installation."), error));
112         exit(EXIT_FAILURE);
113 }
114
115
116 void reconfigureUserLyXDir()
117 {
118         string const configure_script =
119                 addName(package().system_support(), "configure.py");
120         string const configure_command =
121                 "python " + quoteName(configure_script);
122
123         lyxerr << _("LyX: reconfiguring user directory") << endl;
124         Path p(package().user_support());
125         ::system(configure_command.c_str());
126         lyxerr << "LyX: " << _("Done!") << endl;
127 }
128
129 } // namespace anon
130
131
132 boost::scoped_ptr<LyX> LyX::singleton_;
133
134 void LyX::exec(int & argc, char * argv[])
135 {
136         BOOST_ASSERT(!singleton_.get());
137         // We must return from this before launching the gui so that
138         // other parts of the code can access singleton_ through
139         // LyX::ref and LyX::cref.
140         singleton_.reset(new LyX);
141         // Start the real execution loop.
142         singleton_->priv_exec(argc, argv);
143 }
144
145
146 LyX & LyX::ref()
147 {
148         BOOST_ASSERT(singleton_.get());
149         return *singleton_.get();
150 }
151
152
153 LyX const & LyX::cref()
154 {
155         BOOST_ASSERT(singleton_.get());
156         return *singleton_.get();
157 }
158
159
160 LyX::LyX()
161         : first_start(false)
162 {}
163
164
165 lyx::Session & LyX::session()
166 {
167         BOOST_ASSERT(session_.get());
168         return *session_.get();
169 }
170
171
172 lyx::Session const & LyX::session() const
173 {
174         BOOST_ASSERT(session_.get());
175         return *session_.get();
176 }
177
178
179 void LyX::addLyXView(boost::shared_ptr<LyXView> const & lyxview)
180 {
181         views_.push_back(lyxview);
182 }
183
184
185 Buffer const * const LyX::updateInset(InsetBase const * inset) const
186 {
187         if (!inset)
188                 return 0;
189
190         Buffer const * buffer_ptr = 0;
191         ViewList::const_iterator it = views_.begin();
192         ViewList::const_iterator const end = views_.end();
193         for (; it != end; ++it) {
194                 Buffer const * ptr = (*it)->updateInset(inset);
195                 if (ptr)
196                         buffer_ptr = ptr;
197         }
198         return buffer_ptr;
199 }
200
201
202 void LyX::priv_exec(int & argc, char * argv[])
203 {
204         // Here we need to parse the command line. At least
205         // we need to parse for "-dbg" and "-help"
206         bool const want_gui = easyParse(argc, argv);
207
208         lyx::support::init_package(argv[0], cl_system_support, cl_user_support,
209                                    lyx::support::top_build_dir_is_one_level_up);
210
211         if (want_gui)
212                 lyx_gui::parse_init(argc, argv);
213
214         // check for any spurious extra arguments
215         // other than documents
216         for (int argi = 1; argi < argc ; ++argi) {
217                 if (argv[argi][0] == '-') {
218                         lyxerr << bformat(_("Wrong command line option `%1$s'. Exiting."),
219                                 argv[argi]) << endl;
220                         exit(1);
221                 }
222         }
223
224         // Initialization of LyX (reads lyxrc and more)
225         lyxerr[Debug::INIT] << "Initializing LyX::init..." << endl;
226         init(want_gui);
227         lyxerr[Debug::INIT] << "Initializing LyX::init...done" << endl;
228
229         if (want_gui)
230                 lyx_gui::parse_lyxrc();
231
232         vector<string> files;
233
234         for (int argi = argc - 1; argi >= 1; --argi)
235                 files.push_back(os::internal_path(argv[argi]));
236
237         if (first_start)
238                 files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
239
240         // if a file is specified, I assume that user wants to edit *that* file
241         if (files.empty() && lyxrc.load_session) {
242                 vector<string> const & lastopened = session_->lastOpenedFiles();
243                 files.insert(files.end(), lastopened.begin(), lastopened.end()  );
244                 // clear this list to save a few bytes of RAM
245                 session_->clearLastOpenedFiles();
246         }
247
248         // Execute batch commands if available
249         if (!batch_command.empty()) {
250
251                 lyxerr[Debug::INIT] << "About to handle -x '"
252                        << batch_command << '\'' << endl;
253
254                 Buffer * last_loaded = 0;
255
256                 vector<string>::const_iterator it = files.begin();
257                 vector<string>::const_iterator end = files.end();
258
259                 for (; it != end; ++it) {
260                         // get absolute path of file and add ".lyx" to
261                         // the filename if necessary
262                         string s = fileSearch(string(), *it, "lyx");
263                         if (s.empty()) {
264                                 last_loaded = newFile(*it, string(), true);
265                         } else {
266                                 Buffer * buf = bufferlist.newBuffer(s, false);
267                                 buf->error.connect(boost::bind(&LyX::printError, this, _1));
268                                 if (loadLyXFile(buf, s))
269                                         last_loaded = buf;
270                                 else
271                                         bufferlist.release(buf);
272                         }
273                 }
274
275                 // try to dispatch to last loaded buffer first
276                 if (last_loaded) {
277                         bool success = false;
278                         if (last_loaded->dispatch(batch_command, &success)) {
279                                 quitLyX(false);
280                                 exit(!success);
281                         }
282                 }
283                 files.clear(); // the files are already loaded
284         }
285
286         if (want_gui)
287                 lyx_gui::start(batch_command, files);
288         else {
289                 // Something went wrong above
290                 quitLyX(false);
291                 exit(EXIT_FAILURE);
292         }
293 }
294
295
296 /*
297 Signals and Windows
298 ===================
299 The SIGHUP signal does not exist on Windows and does not need to be handled.
300
301 Windows handles SIGFPE and SIGSEGV signals as expected.
302
303 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
304 cause a new thread to be spawned. This may well result in unexpected
305 behaviour by the single-threaded LyX.
306
307 SIGTERM signals will come only from another process actually sending
308 that signal using 'raise' in Windows' POSIX compatability layer. It will
309 not come from the general "terminate process" methods that everyone
310 actually uses (and which can't be trapped). Killing an app 'politely' on
311 Windows involves first sending a WM_CLOSE message, something that is
312 caught already by the Qt frontend.
313
314 For more information see:
315
316 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
317 ...signals are mostly useless on Windows for a variety of reasons that are
318 Windows specific...
319
320 'UNIX Application Migration Guide, Chapter 9'
321 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
322
323 'How To Terminate an Application "Cleanly" in Win32'
324 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
325 */
326 extern "C" {
327
328 static void error_handler(int err_sig)
329 {
330         // Throw away any signals other than the first one received.
331         static sig_atomic_t handling_error = false;
332         if (handling_error)
333                 return;
334         handling_error = true;
335
336         // We have received a signal indicating a fatal error, so
337         // try and save the data ASAP.
338         LyX::cref().emergencyCleanup();
339
340         // These lyxerr calls may or may not work:
341
342         // Signals are asynchronous, so the main program may be in a very
343         // fragile state when a signal is processed and thus while a signal
344         // handler function executes.
345         // In general, therefore, we should avoid performing any
346         // I/O operations or calling most library and system functions from
347         // signal handlers.
348
349         // This shouldn't matter here, however, as we've already invoked
350         // emergencyCleanup.
351         switch (err_sig) {
352 #ifdef SIGHUP
353         case SIGHUP:
354                 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
355                 break;
356 #endif
357         case SIGFPE:
358                 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
359                 break;
360         case SIGSEGV:
361                 lyxerr << "\nlyx: SIGSEGV signal caught\n"
362                           "Sorry, you have found a bug in LyX. "
363                           "Please read the bug-reporting instructions "
364                           "in Help->Introduction and send us a bug report, "
365                           "if necessary. Thanks !\nBye." << endl;
366                 break;
367         case SIGINT:
368         case SIGTERM:
369                 // no comments
370                 break;
371         }
372
373         // Deinstall the signal handlers
374 #ifdef SIGHUP
375         signal(SIGHUP, SIG_DFL);
376 #endif
377         signal(SIGINT, SIG_DFL);
378         signal(SIGFPE, SIG_DFL);
379         signal(SIGSEGV, SIG_DFL);
380         signal(SIGTERM, SIG_DFL);
381
382 #ifdef SIGHUP
383         if (err_sig == SIGSEGV ||
384             (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
385 #else
386         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
387 #endif
388                 lyx::support::abort();
389         exit(0);
390 }
391
392 }
393
394
395 void LyX::printError(ErrorItem const & ei)
396 {
397         std::cerr << _("LyX: ") << ei.error
398                   << ':' << ei.description << std::endl;
399
400 }
401
402
403 void LyX::init(bool gui)
404 {
405 #ifdef SIGHUP
406         signal(SIGHUP, error_handler);
407 #endif
408         signal(SIGFPE, error_handler);
409         signal(SIGSEGV, error_handler);
410         signal(SIGINT, error_handler);
411         signal(SIGTERM, error_handler);
412         // SIGPIPE can be safely ignored.
413
414         // Check that user LyX directory is ok. We don't do that if
415         // running in batch mode.
416         bool reconfigure = false;
417         if (gui) {
418                 reconfigure =
419                         queryUserLyXDir(package().explicit_user_support());
420         } else {
421                 first_start = false;
422         }
423
424         // Disable gui when easyparse says so
425         lyx_gui::use_gui = gui;
426
427         lyxrc.tempdir_path = package().temp_dir();
428         lyxrc.document_path = package().document_dir();
429
430         if (lyxrc.template_path.empty()) {
431                 lyxrc.template_path = addPath(package().system_support(),
432                                               "templates");
433         }
434
435         if (lyxrc.roman_font_name.empty())
436                 lyxrc.roman_font_name = lyx_gui::roman_font_name();
437         if (lyxrc.sans_font_name.empty())
438                 lyxrc.sans_font_name = lyx_gui::sans_font_name();
439         if (lyxrc.typewriter_font_name.empty())
440                 lyxrc.typewriter_font_name = lyx_gui::typewriter_font_name();
441
442         //
443         // Read configuration files
444         //
445
446         readRcFile("lyxrc.defaults");
447         system_lyxrc = lyxrc;
448         system_formats = formats;
449         system_converters = converters;
450         system_movers = movers;
451         system_lcolor = lcolor;
452
453         string prefsfile = "preferences";
454         // back compatibility to lyxs < 1.1.6
455         if (libFileSearch(string(), prefsfile).empty())
456                 prefsfile = "lyxrc";
457         if (!libFileSearch(string(), prefsfile).empty())
458                 readRcFile(prefsfile);
459
460         readEncodingsFile("encodings");
461         readLanguagesFile("languages");
462
463         // Load the layouts
464         lyxerr[Debug::INIT] << "Reading layouts..." << endl;
465         LyXSetStyle();
466
467         if (gui) {
468                 // Set up bindings
469                 toplevel_keymap.reset(new kb_keymap);
470                 defaultKeyBindings(toplevel_keymap.get());
471                 toplevel_keymap->read(lyxrc.bind_file);
472
473                 // Read menus
474                 readUIFile(lyxrc.ui_file);
475         }
476
477         if (lyxerr.debugging(Debug::LYXRC))
478                 lyxrc.print();
479
480         os::cygwin_path_fix(lyxrc.cygwin_path_fix);
481         if (!lyxrc.path_prefix.empty())
482                 prependEnvPath("PATH", lyxrc.path_prefix);
483
484 #if !defined (USE_POSIX_PACKAGING)
485         // Add the directory containing the LyX executable to the path
486         // so that LyX can find things like tex2lyx.
487         if (package().build_support().empty())
488                 prependEnvPath("PATH", package().binary_dir());
489 #endif
490
491         // Having reset the PATH we're now in a position to run configure
492         // if necessary.
493         if (reconfigure)
494                 reconfigureUserLyXDir();
495
496         if (fs::exists(lyxrc.document_path) &&
497             fs::is_directory(lyxrc.document_path))
498                 package().document_dir() = lyxrc.document_path;
499
500         package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
501         if (package().temp_dir().empty()) {
502                 Alert::error(_("Could not create temporary directory"),
503                              bformat(_("Could not create a temporary directory in\n"
504                                        "%1$s. Make sure that this\n"
505                                        "path exists and is writable and try again."),
506                                      lyxrc.tempdir_path));
507                 // createLyXTmpDir() tries sufficiently hard to create a
508                 // usable temp dir, so the probability to come here is
509                 // close to zero. We therefore don't try to overcome this
510                 // problem with e.g. asking the user for a new path and
511                 // trying again but simply exit.
512                 exit(EXIT_FAILURE);
513         }
514
515         if (lyxerr.debugging(Debug::INIT)) {
516                 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
517         }
518
519         lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
520         session_.reset(new lyx::Session(lyxrc.num_lastfiles));
521 }
522
523
524 void LyX::defaultKeyBindings(kb_keymap  * kbmap)
525 {
526         kbmap->bind("Right", FuncRequest(LFUN_RIGHT));
527         kbmap->bind("Left", FuncRequest(LFUN_LEFT));
528         kbmap->bind("Up", FuncRequest(LFUN_UP));
529         kbmap->bind("Down", FuncRequest(LFUN_DOWN));
530
531         kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
532         kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
533         kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
534         kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
535
536         kbmap->bind("Home", FuncRequest(LFUN_HOME));
537         kbmap->bind("End", FuncRequest(LFUN_END));
538         kbmap->bind("Prior", FuncRequest(LFUN_PRIOR));
539         kbmap->bind("Next", FuncRequest(LFUN_NEXT));
540
541         kbmap->bind("Return", FuncRequest(LFUN_BREAKPARAGRAPH));
542         //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
543
544         kbmap->bind("Delete", FuncRequest(LFUN_DELETE));
545         kbmap->bind("BackSpace", FuncRequest(LFUN_BACKSPACE));
546
547         // kbmap->bindings to enable the use of the numeric keypad
548         // e.g. Num Lock set
549         //kbmap->bind("KP_0", FuncRequest(LFUN_SELFINSERT));
550         //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELFINSERT));
551         kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAKPARAGRAPH));
552         //kbmap->bind("KP_1", FuncRequest(LFUN_SELFINSERT));
553         //kbmap->bind("KP_2", FuncRequest(LFUN_SELFINSERT));
554         //kbmap->bind("KP_3", FuncRequest(LFUN_SELFINSERT));
555         //kbmap->bind("KP_4", FuncRequest(LFUN_SELFINSERT));
556         //kbmap->bind("KP_5", FuncRequest(LFUN_SELFINSERT));
557         //kbmap->bind("KP_6", FuncRequest(LFUN_SELFINSERT));
558         //kbmap->bind("KP_Add", FuncRequest(LFUN_SELFINSERT));
559         //kbmap->bind("KP_7", FuncRequest(LFUN_SELFINSERT));
560         //kbmap->bind("KP_8", FuncRequest(LFUN_SELFINSERT));
561         //kbmap->bind("KP_9", FuncRequest(LFUN_SELFINSERT));
562         //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELFINSERT));
563         //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELFINSERT));
564         //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELFINSERT));
565         kbmap->bind("KP_Right", FuncRequest(LFUN_RIGHT));
566         kbmap->bind("KP_Left", FuncRequest(LFUN_LEFT));
567         kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
568         kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
569         kbmap->bind("KP_Home", FuncRequest(LFUN_HOME));
570         kbmap->bind("KP_End", FuncRequest(LFUN_END));
571         kbmap->bind("KP_Prior", FuncRequest(LFUN_PRIOR));
572         kbmap->bind("KP_Next", FuncRequest(LFUN_NEXT));
573 }
574
575
576 void LyX::emergencyCleanup() const
577 {
578         // what to do about tmpfiles is non-obvious. we would
579         // like to delete any we find, but our lyxdir might
580         // contain documents etc. which might be helpful on
581         // a crash
582
583         bufferlist.emergencyWriteAll();
584         if (lyxserver)
585                 lyxserver->emergencyCleanup();
586 }
587
588
589 void LyX::deadKeyBindings(kb_keymap * kbmap)
590 {
591         // bindKeyings for transparent handling of deadkeys
592         // The keysyms are gotten from XFree86 X11R6
593         kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACUTE));
594         kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_BREVE));
595         kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_CARON));
596         kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_CEDILLA));
597         kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_CIRCLE));
598         kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_CIRCUMFLEX));
599         kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_DOT));
600         kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_GRAVE));
601         kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_HUNG_UMLAUT));
602         kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_MACRON));
603         // nothing with this name
604         // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_SPECIAL_CARON);
605         kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_TILDE));
606         kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_UMLAUT));
607         // nothing with this name either...
608         //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_UNDERBAR));
609         kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_UNDERDOT));
610         kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_TIE));
611         kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_OGONEK));
612 }
613
614
615 bool LyX::queryUserLyXDir(bool explicit_userdir)
616 {
617         bool reconfigure = false;
618
619         // Does user directory exist?
620         if (fs::exists(package().user_support()) &&
621             fs::is_directory(package().user_support())) {
622                 first_start = false;
623                 string const configure_script =
624                         addName(package().system_support(), "configure.py");
625                 string const userDefaults =
626                         addName(package().user_support(), "lyxrc.defaults");
627                 if (fs::exists(configure_script) &&
628                     fs::exists(userDefaults) &&
629                     fs::last_write_time(configure_script)
630                     > fs::last_write_time(userDefaults)) {
631                         reconfigure = true;
632                 }
633                 return reconfigure;
634         }
635
636         first_start = !explicit_userdir;
637
638         // If the user specified explicitly a directory, ask whether
639         // to create it. If the user says "no", then exit.
640         if (explicit_userdir &&
641             Alert::prompt(
642                     _("Missing user LyX directory"),
643                     bformat(_("You have specified a non-existent user "
644                               "LyX directory, %1$s.\n"
645                               "It is needed to keep your own configuration."),
646                             package().user_support()),
647                     1, 0,
648                     _("&Create directory"),
649                     _("&Exit LyX"))) {
650                 lyxerr << _("No user LyX directory. Exiting.") << endl;
651                 exit(1);
652         }
653
654         lyxerr << bformat(_("LyX: Creating directory %1$s"),
655                           package().user_support())
656                << endl;
657         reconfigure = true;
658
659         if (!createDirectory(package().user_support(), 0755)) {
660                 // Failed, so let's exit.
661                 lyxerr << _("Failed to create directory. Exiting.")
662                        << endl;
663                 exit(1);
664         }
665
666         return reconfigure;
667 }
668
669
670 void LyX::readRcFile(string const & name)
671 {
672         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
673
674         string const lyxrc_path = libFileSearch(string(), name);
675         if (!lyxrc_path.empty()) {
676
677                 lyxerr[Debug::INIT] << "Found " << name
678                                     << " in " << lyxrc_path << endl;
679
680                 if (lyxrc.read(lyxrc_path) >= 0)
681                         return;
682         }
683
684         showFileError(name);
685 }
686
687
688 // Read the ui file `name'
689 void LyX::readUIFile(string const & name)
690 {
691         enum Uitags {
692                 ui_menuset = 1,
693                 ui_toolbar,
694                 ui_toolbars,
695                 ui_include,
696                 ui_last
697         };
698
699         struct keyword_item uitags[ui_last - 1] = {
700                 { "include", ui_include },
701                 { "menuset", ui_menuset },
702                 { "toolbar", ui_toolbar },
703                 { "toolbars", ui_toolbars }
704         };
705
706         // Ensure that a file is read only once (prevents include loops)
707         static std::list<string> uifiles;
708         std::list<string>::const_iterator it  = uifiles.begin();
709         std::list<string>::const_iterator end = uifiles.end();
710         it = std::find(it, end, name);
711         if (it != end) {
712                 lyxerr[Debug::INIT] << "UI file '" << name
713                                     << "' has been read already. "
714                                     << "Is this an include loop?"
715                                     << endl;
716                 return;
717         }
718
719         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
720
721         string const ui_path = libFileSearch("ui", name, "ui");
722
723         if (ui_path.empty()) {
724                 lyxerr[Debug::INIT] << "Could not find " << name << endl;
725                 showFileError(name);
726                 return;
727         }
728         uifiles.push_back(name);
729
730         lyxerr[Debug::INIT] << "Found " << name
731                             << " in " << ui_path << endl;
732         LyXLex lex(uitags, ui_last - 1);
733         lex.setFile(ui_path);
734         if (!lex.isOK()) {
735                 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
736                        << endl;
737         }
738
739         if (lyxerr.debugging(Debug::PARSER))
740                 lex.printTable(lyxerr);
741
742         while (lex.isOK()) {
743                 switch (lex.lex()) {
744                 case ui_include: {
745                         lex.next(true);
746                         string const file = lex.getString();
747                         readUIFile(file);
748                         break;
749                 }
750                 case ui_menuset:
751                         menubackend.read(lex);
752                         break;
753
754                 case ui_toolbar:
755                         toolbarbackend.read(lex);
756                         break;
757
758                 case ui_toolbars:
759                         toolbarbackend.readToolbars(lex);
760                         break;
761
762                 default:
763                         if (!rtrim(lex.getString()).empty())
764                                 lex.printError("LyX::ReadUIFile: "
765                                                "Unknown menu tag: `$$Token'");
766                         break;
767                 }
768         }
769 }
770
771
772 // Read the languages file `name'
773 void LyX::readLanguagesFile(string const & name)
774 {
775         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
776
777         string const lang_path = libFileSearch(string(), name);
778         if (lang_path.empty()) {
779                 showFileError(name);
780                 return;
781         }
782         languages.read(lang_path);
783 }
784
785
786 // Read the encodings file `name'
787 void LyX::readEncodingsFile(string const & name)
788 {
789         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
790
791         string const enc_path = libFileSearch(string(), name);
792         if (enc_path.empty()) {
793                 showFileError(name);
794                 return;
795         }
796         encodings.read(enc_path);
797 }
798
799
800 namespace {
801
802 bool is_gui = true;
803 string batch;
804
805 /// return the the number of arguments consumed
806 typedef boost::function<int(string const &, string const &)> cmd_helper;
807
808 int parse_dbg(string const & arg, string const &)
809 {
810         if (arg.empty()) {
811                 lyxerr << _("List of supported debug flags:") << endl;
812                 Debug::showTags(lyxerr);
813                 exit(0);
814         }
815         lyxerr << bformat(_("Setting debug level to %1$s"), arg) << endl;
816
817         lyxerr.level(Debug::value(arg));
818         Debug::showLevel(lyxerr, lyxerr.level());
819         return 1;
820 }
821
822
823 int parse_help(string const &, string const &)
824 {
825         lyxerr <<
826                 _("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
827                   "Command line switches (case sensitive):\n"
828                   "\t-help              summarize LyX usage\n"
829                   "\t-userdir dir       set user directory to dir\n"
830                   "\t-sysdir dir        set system directory to dir\n"
831                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
832                   "\t-dbg feature[,feature]...\n"
833                   "                  select the features to debug.\n"
834                   "                  Type `lyx -dbg' to see the list of features\n"
835                   "\t-x [--execute] command\n"
836                   "                  where command is a lyx command.\n"
837                   "\t-e [--export] fmt\n"
838                   "                  where fmt is the export format of choice.\n"
839                   "\t-i [--import] fmt file.xxx\n"
840                   "                  where fmt is the import format of choice\n"
841                   "                  and file.xxx is the file to be imported.\n"
842                   "\t-version        summarize version and build info\n"
843                   "Check the LyX man page for more details.") << endl;
844         exit(0);
845         return 0;
846 }
847
848 int parse_version(string const &, string const &)
849 {
850         lyxerr << "LyX " << lyx_version
851                << " of " << lyx_release_date << endl;
852         lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
853
854         lyxerr << lyx_version_info << endl;
855         exit(0);
856         return 0;
857 }
858
859 int parse_sysdir(string const & arg, string const &)
860 {
861         if (arg.empty()) {
862                 lyxerr << _("Missing directory for -sysdir switch") << endl;
863                 exit(1);
864         }
865         cl_system_support = arg;
866         return 1;
867 }
868
869 int parse_userdir(string const & arg, string const &)
870 {
871         if (arg.empty()) {
872                 lyxerr << _("Missing directory for -userdir switch") << endl;
873                 exit(1);
874         }
875         cl_user_support = arg;
876         return 1;
877 }
878
879 int parse_execute(string const & arg, string const &)
880 {
881         if (arg.empty()) {
882                 lyxerr << _("Missing command string after --execute switch") << endl;
883                 exit(1);
884         }
885         batch = arg;
886         return 1;
887 }
888
889 int parse_export(string const & type, string const &)
890 {
891         if (type.empty()) {
892                 lyxerr << _("Missing file type [eg latex, ps...] after "
893                         "--export switch") << endl;
894                 exit(1);
895         }
896         batch = "buffer-export " + type;
897         is_gui = false;
898         return 1;
899 }
900
901 int parse_import(string const & type, string const & file)
902 {
903         if (type.empty()) {
904                 lyxerr << _("Missing file type [eg latex, ps...] after "
905                         "--import switch") << endl;
906                 exit(1);
907         }
908         if (file.empty()) {
909                 lyxerr << _("Missing filename for --import") << endl;
910                 exit(1);
911         }
912
913         batch = "buffer-import " + type + ' ' + file;
914         return 2;
915 }
916
917 } // namespace anon
918
919
920 bool LyX::easyParse(int & argc, char * argv[])
921 {
922         std::map<string, cmd_helper> cmdmap;
923
924         cmdmap["-dbg"] = parse_dbg;
925         cmdmap["-help"] = parse_help;
926         cmdmap["--help"] = parse_help;
927         cmdmap["-version"] = parse_version;
928         cmdmap["--version"] = parse_version;
929         cmdmap["-sysdir"] = parse_sysdir;
930         cmdmap["-userdir"] = parse_userdir;
931         cmdmap["-x"] = parse_execute;
932         cmdmap["--execute"] = parse_execute;
933         cmdmap["-e"] = parse_export;
934         cmdmap["--export"] = parse_export;
935         cmdmap["-i"] = parse_import;
936         cmdmap["--import"] = parse_import;
937
938         for (int i = 1; i < argc; ++i) {
939                 std::map<string, cmd_helper>::const_iterator it
940                         = cmdmap.find(argv[i]);
941
942                 // don't complain if not found - may be parsed later
943                 if (it == cmdmap.end())
944                         continue;
945
946                 string arg((i + 1 < argc) ? argv[i + 1] : "");
947                 string arg2((i + 2 < argc) ? argv[i + 2] : "");
948
949                 int const remove = 1 + it->second(arg, arg2);
950
951                 // Now, remove used arguments by shifting
952                 // the following ones remove places down.
953                 argc -= remove;
954                 for (int j = i; j < argc; ++j)
955                         argv[j] = argv[j + remove];
956                 --i;
957         }
958
959         batch_command = batch;
960
961         return is_gui;
962 }