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