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