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