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