]> git.lyx.org Git - lyx.git/blob - src/lyx_main.C
coding style and readability corrections
[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                 bool maximize = false;
311                 // first try lyxrc
312                 if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
313                         width = lyxrc.geometry_width;
314                         height = lyxrc.geometry_height;
315                 }
316                 // if lyxrc returns (0,0), then use session info
317                 else {
318                         string val = session().loadSessionInfo("WindowWidth");
319                         if (!val.empty())
320                                 width = convert<unsigned int>(val);
321                         val = session().loadSessionInfo("WindowHeight");
322                         if (!val.empty())
323                                 height = convert<unsigned int>(val);
324                         if (session().loadSessionInfo("WindowIsMaximized") == "yes")
325                                 maximize = true;
326                 }
327                 // if user wants to restore window position
328                 int posx = -1;
329                 int posy = -1;
330                 if (lyxrc.geometry_xysaved) {
331                         string val = session().loadSessionInfo("WindowPosX");
332                         if (!val.empty())
333                                 posx = convert<int>(val);
334                         val = session().loadSessionInfo("WindowPosY");
335                         if (!val.empty())
336                                 posy = convert<int>(val);
337                 }
338                 lyx_gui::start(batch_command, files, width, height, posx, posy, maximize);
339         } else {
340                 // Something went wrong above
341                 quitLyX(false);
342                 lyx_exit(EXIT_FAILURE);
343         }
344 }
345
346
347 /*
348 Signals and Windows
349 ===================
350 The SIGHUP signal does not exist on Windows and does not need to be handled.
351
352 Windows handles SIGFPE and SIGSEGV signals as expected.
353
354 Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
355 cause a new thread to be spawned. This may well result in unexpected
356 behaviour by the single-threaded LyX.
357
358 SIGTERM signals will come only from another process actually sending
359 that signal using 'raise' in Windows' POSIX compatability layer. It will
360 not come from the general "terminate process" methods that everyone
361 actually uses (and which can't be trapped). Killing an app 'politely' on
362 Windows involves first sending a WM_CLOSE message, something that is
363 caught already by the Qt frontend.
364
365 For more information see:
366
367 http://aspn.activestate.com/ASPN/Mail/Message/ActiveTcl/2034055
368 ...signals are mostly useless on Windows for a variety of reasons that are
369 Windows specific...
370
371 'UNIX Application Migration Guide, Chapter 9'
372 http://msdn.microsoft.com/library/en-us/dnucmg/html/UCMGch09.asp
373
374 'How To Terminate an Application "Cleanly" in Win32'
375 http://support.microsoft.com/default.aspx?scid=kb;en-us;178893
376 */
377 extern "C" {
378
379 static void error_handler(int err_sig)
380 {
381         // Throw away any signals other than the first one received.
382         static sig_atomic_t handling_error = false;
383         if (handling_error)
384                 return;
385         handling_error = true;
386
387         // We have received a signal indicating a fatal error, so
388         // try and save the data ASAP.
389         LyX::cref().emergencyCleanup();
390
391         // These lyxerr calls may or may not work:
392
393         // Signals are asynchronous, so the main program may be in a very
394         // fragile state when a signal is processed and thus while a signal
395         // handler function executes.
396         // In general, therefore, we should avoid performing any
397         // I/O operations or calling most library and system functions from
398         // signal handlers.
399
400         // This shouldn't matter here, however, as we've already invoked
401         // emergencyCleanup.
402         switch (err_sig) {
403 #ifdef SIGHUP
404         case SIGHUP:
405                 lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
406                 break;
407 #endif
408         case SIGFPE:
409                 lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
410                 break;
411         case SIGSEGV:
412                 lyxerr << "\nlyx: SIGSEGV signal caught\n"
413                           "Sorry, you have found a bug in LyX. "
414                           "Please read the bug-reporting instructions "
415                           "in Help->Introduction and send us a bug report, "
416                           "if necessary. Thanks !\nBye." << endl;
417                 break;
418         case SIGINT:
419         case SIGTERM:
420                 // no comments
421                 break;
422         }
423
424         // Deinstall the signal handlers
425 #ifdef SIGHUP
426         signal(SIGHUP, SIG_DFL);
427 #endif
428         signal(SIGINT, SIG_DFL);
429         signal(SIGFPE, SIG_DFL);
430         signal(SIGSEGV, SIG_DFL);
431         signal(SIGTERM, SIG_DFL);
432
433 #ifdef SIGHUP
434         if (err_sig == SIGSEGV ||
435             (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
436 #else
437         if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
438 #endif
439                 lyx::support::abort();
440         exit(0);
441 }
442
443 }
444
445
446 void LyX::printError(ErrorItem const & ei)
447 {
448         std::cerr << _("LyX: ") << ei.error
449                   << ':' << ei.description << std::endl;
450
451 }
452
453
454 void LyX::init()
455 {
456 #ifdef SIGHUP
457         signal(SIGHUP, error_handler);
458 #endif
459         signal(SIGFPE, error_handler);
460         signal(SIGSEGV, error_handler);
461         signal(SIGINT, error_handler);
462         signal(SIGTERM, error_handler);
463         // SIGPIPE can be safely ignored.
464
465         lyxrc.tempdir_path = package().temp_dir();
466         lyxrc.document_path = package().document_dir();
467
468         if (lyxrc.template_path.empty()) {
469                 lyxrc.template_path = addPath(package().system_support(),
470                                               "templates");
471         }
472
473         if (lyxrc.roman_font_name.empty())
474                 lyxrc.roman_font_name = lyx_gui::roman_font_name();
475         if (lyxrc.sans_font_name.empty())
476                 lyxrc.sans_font_name = lyx_gui::sans_font_name();
477         if (lyxrc.typewriter_font_name.empty())
478                 lyxrc.typewriter_font_name = lyx_gui::typewriter_font_name();
479
480         //
481         // Read configuration files
482         //
483
484         // This one may have been distributed along with LyX.
485         readRcFile("lyxrc.dist");
486
487         // Set the PATH correctly.
488 #if !defined (USE_POSIX_PACKAGING)
489         // Add the directory containing the LyX executable to the path
490         // so that LyX can find things like tex2lyx.
491         if (package().build_support().empty())
492                 prependEnvPath("PATH", package().binary_dir());
493 #endif
494         if (!lyxrc.path_prefix.empty())
495                 prependEnvPath("PATH", lyxrc.path_prefix);
496
497         // Check that user LyX directory is ok. We don't do that if
498         // running in batch mode.
499         if (lyx_gui::use_gui) {
500                 if (queryUserLyXDir(package().explicit_user_support()))
501                         reconfigureUserLyXDir();
502         } else {
503                 first_start = false;
504         }
505
506         // This one is generated in user_support directory by lib/configure.py.
507         readRcFile("lyxrc.defaults");
508
509         // Query the OS to know what formats are viewed natively
510         formats.setAutoOpen();
511
512         system_lyxrc = lyxrc;
513         system_formats = formats;
514         system_converters = converters;
515         system_movers = movers;
516         system_lcolor = lcolor;
517
518         // This one is edited through the preferences dialog.
519         readRcFile("preferences");
520
521         readEncodingsFile("encodings");
522         readLanguagesFile("languages");
523
524         // Load the layouts
525         lyxerr[Debug::INIT] << "Reading layouts..." << endl;
526         LyXSetStyle();
527
528         if (lyx_gui::use_gui) {
529                 // Set up bindings
530                 toplevel_keymap.reset(new kb_keymap);
531                 defaultKeyBindings(toplevel_keymap.get());
532                 toplevel_keymap->read(lyxrc.bind_file);
533
534                 // Read menus
535                 readUIFile(lyxrc.ui_file);
536         }
537
538         if (lyxerr.debugging(Debug::LYXRC))
539                 lyxrc.print();
540
541         os::cygwin_path_fix(lyxrc.cygwin_path_fix);
542         if (!lyxrc.path_prefix.empty())
543                 prependEnvPath("PATH", lyxrc.path_prefix);
544
545         if (fs::exists(lyxrc.document_path) &&
546             fs::is_directory(lyxrc.document_path))
547                 package().document_dir() = lyxrc.document_path;
548
549         package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
550         if (package().temp_dir().empty()) {
551                 Alert::error(_("Could not create temporary directory"),
552                              bformat(_("Could not create a temporary directory in\n"
553                                        "%1$s. Make sure that this\n"
554                                        "path exists and is writable and try again."),
555                                      lyxrc.tempdir_path));
556                 // createLyXTmpDir() tries sufficiently hard to create a
557                 // usable temp dir, so the probability to come here is
558                 // close to zero. We therefore don't try to overcome this
559                 // problem with e.g. asking the user for a new path and
560                 // trying again but simply exit.
561                 lyx_exit(EXIT_FAILURE);
562         }
563
564         if (lyxerr.debugging(Debug::INIT)) {
565                 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
566         }
567
568         lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
569         session_.reset(new lyx::Session(lyxrc.num_lastfiles));
570 }
571
572
573 void LyX::defaultKeyBindings(kb_keymap  * kbmap)
574 {
575         kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
576         kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
577         kbmap->bind("Up", FuncRequest(LFUN_UP));
578         kbmap->bind("Down", FuncRequest(LFUN_DOWN));
579
580         kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
581         kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
582         kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
583         kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
584
585         kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
586         kbmap->bind("End", FuncRequest(LFUN_LINE_END));
587         kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
588         kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
589
590         kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
591         //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
592
593         kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
594         kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
595
596         // kbmap->bindings to enable the use of the numeric keypad
597         // e.g. Num Lock set
598         //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
599         //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
600         kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
601         //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
602         //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
603         //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
604         //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
605         //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
606         //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
607         //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
608         //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
609         //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
610         //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
611         //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
612         //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
613         //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
614         kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
615         kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
616         kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
617         kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
618         kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
619         kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
620         kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
621         kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
622 }
623
624
625 void LyX::emergencyCleanup() const
626 {
627         // what to do about tmpfiles is non-obvious. we would
628         // like to delete any we find, but our lyxdir might
629         // contain documents etc. which might be helpful on
630         // a crash
631
632         bufferlist.emergencyWriteAll();
633         if (lyxserver)
634                 lyxserver->emergencyCleanup();
635 }
636
637
638 void LyX::deadKeyBindings(kb_keymap * kbmap)
639 {
640         // bindKeyings for transparent handling of deadkeys
641         // The keysyms are gotten from XFree86 X11R6
642         kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
643         kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
644         kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
645         kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
646         kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
647         kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
648         kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
649         kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
650         kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
651         kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
652         // nothing with this name
653         // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
654         kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
655         kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
656         // nothing with this name either...
657         //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
658         kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
659         kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
660         kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
661 }
662
663
664 namespace {
665
666 // return true if file does not exist or is older than configure.py.
667 bool needsUpdate(string const & file)
668 {
669         static string const configure_script =
670                 addName(package().system_support(), "configure.py");
671         string const absfile =
672                 addName(package().user_support(), file);
673
674         return (! fs::exists(absfile))
675                 || (fs::last_write_time(configure_script) 
676                     > fs::last_write_time(absfile));
677 }
678
679 }
680
681
682 bool LyX::queryUserLyXDir(bool explicit_userdir)
683 {
684         // Does user directory exist?
685         if (fs::exists(package().user_support()) &&
686             fs::is_directory(package().user_support())) {
687                 first_start = false;
688                 
689                 return needsUpdate("lyxrc.defaults") 
690                         || needsUpdate("textclass.lst") 
691                         || needsUpdate("packages.lst");
692         }
693
694         first_start = !explicit_userdir;
695
696         // If the user specified explicitly a directory, ask whether
697         // to create it. If the user says "no", then exit.
698         if (explicit_userdir &&
699             Alert::prompt(
700                     _("Missing user LyX directory"),
701                     bformat(_("You have specified a non-existent user "
702                               "LyX directory, %1$s.\n"
703                               "It is needed to keep your own configuration."),
704                             package().user_support()),
705                     1, 0,
706                     _("&Create directory"),
707                     _("&Exit LyX"))) {
708                 lyxerr << _("No user LyX directory. Exiting.") << endl;
709                 lyx_exit(EXIT_FAILURE);
710         }
711
712         lyxerr << bformat(_("LyX: Creating directory %1$s"),
713                           package().user_support())
714                << endl;
715
716         if (!createDirectory(package().user_support(), 0755)) {
717                 // Failed, so let's exit.
718                 lyxerr << _("Failed to create directory. Exiting.")
719                        << endl;
720                 lyx_exit(EXIT_FAILURE);
721         }
722
723         return true;
724 }
725
726
727 void LyX::readRcFile(string const & name)
728 {
729         lyxerr[Debug::INIT] << "About to read " << name << "... ";
730
731         string const lyxrc_path = libFileSearch(string(), name);
732         if (!lyxrc_path.empty()) {
733
734                 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
735
736                 if (lyxrc.read(lyxrc_path) < 0)
737                         showFileError(name);
738         } else
739                 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
740
741 }
742
743
744 // Read the ui file `name'
745 void LyX::readUIFile(string const & name)
746 {
747         enum Uitags {
748                 ui_menuset = 1,
749                 ui_toolbar,
750                 ui_toolbars,
751                 ui_include,
752                 ui_last
753         };
754
755         struct keyword_item uitags[ui_last - 1] = {
756                 { "include", ui_include },
757                 { "menuset", ui_menuset },
758                 { "toolbar", ui_toolbar },
759                 { "toolbars", ui_toolbars }
760         };
761
762         // Ensure that a file is read only once (prevents include loops)
763         static std::list<string> uifiles;
764         std::list<string>::const_iterator it  = uifiles.begin();
765         std::list<string>::const_iterator end = uifiles.end();
766         it = std::find(it, end, name);
767         if (it != end) {
768                 lyxerr[Debug::INIT] << "UI file '" << name
769                                     << "' has been read already. "
770                                     << "Is this an include loop?"
771                                     << endl;
772                 return;
773         }
774
775         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
776
777         string const ui_path = libFileSearch("ui", name, "ui");
778
779         if (ui_path.empty()) {
780                 lyxerr[Debug::INIT] << "Could not find " << name << endl;
781                 showFileError(name);
782                 return;
783         }
784         uifiles.push_back(name);
785
786         lyxerr[Debug::INIT] << "Found " << name
787                             << " in " << ui_path << endl;
788         LyXLex lex(uitags, ui_last - 1);
789         lex.setFile(ui_path);
790         if (!lex.isOK()) {
791                 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
792                        << endl;
793         }
794
795         if (lyxerr.debugging(Debug::PARSER))
796                 lex.printTable(lyxerr);
797
798         while (lex.isOK()) {
799                 switch (lex.lex()) {
800                 case ui_include: {
801                         lex.next(true);
802                         string const file = lex.getString();
803                         readUIFile(file);
804                         break;
805                 }
806                 case ui_menuset:
807                         menubackend.read(lex);
808                         break;
809
810                 case ui_toolbar:
811                         toolbarbackend.read(lex);
812                         break;
813
814                 case ui_toolbars:
815                         toolbarbackend.readToolbars(lex);
816                         break;
817
818                 default:
819                         if (!rtrim(lex.getString()).empty())
820                                 lex.printError("LyX::ReadUIFile: "
821                                                "Unknown menu tag: `$$Token'");
822                         break;
823                 }
824         }
825 }
826
827
828 // Read the languages file `name'
829 void LyX::readLanguagesFile(string const & name)
830 {
831         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
832
833         string const lang_path = libFileSearch(string(), name);
834         if (lang_path.empty()) {
835                 showFileError(name);
836                 return;
837         }
838         languages.read(lang_path);
839 }
840
841
842 // Read the encodings file `name'
843 void LyX::readEncodingsFile(string const & name)
844 {
845         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
846
847         string const enc_path = libFileSearch(string(), name);
848         if (enc_path.empty()) {
849                 showFileError(name);
850                 return;
851         }
852         encodings.read(enc_path);
853 }
854
855
856 namespace {
857
858 bool is_gui = true;
859 string batch;
860
861 /// return the the number of arguments consumed
862 typedef boost::function<int(string const &, string const &)> cmd_helper;
863
864 int parse_dbg(string const & arg, string const &)
865 {
866         if (arg.empty()) {
867                 lyxerr << _("List of supported debug flags:") << endl;
868                 Debug::showTags(lyxerr);
869                 exit(0);
870         }
871         lyxerr << bformat(_("Setting debug level to %1$s"), arg) << endl;
872
873         lyxerr.level(Debug::value(arg));
874         Debug::showLevel(lyxerr, lyxerr.level());
875         return 1;
876 }
877
878
879 int parse_help(string const &, string const &)
880 {
881         lyxerr <<
882                 _("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
883                   "Command line switches (case sensitive):\n"
884                   "\t-help              summarize LyX usage\n"
885                   "\t-userdir dir       set user directory to dir\n"
886                   "\t-sysdir dir        set system directory to dir\n"
887                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
888                   "\t-dbg feature[,feature]...\n"
889                   "                  select the features to debug.\n"
890                   "                  Type `lyx -dbg' to see the list of features\n"
891                   "\t-x [--execute] command\n"
892                   "                  where command is a lyx command.\n"
893                   "\t-e [--export] fmt\n"
894                   "                  where fmt is the export format of choice.\n"
895                   "\t-i [--import] fmt file.xxx\n"
896                   "                  where fmt is the import format of choice\n"
897                   "                  and file.xxx is the file to be imported.\n"
898                   "\t-version        summarize version and build info\n"
899                   "Check the LyX man page for more details.") << endl;
900         exit(0);
901         return 0;
902 }
903
904 int parse_version(string const &, string const &)
905 {
906         lyxerr << "LyX " << lyx_version
907                << " of " << lyx_release_date << endl;
908         lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
909
910         lyxerr << lyx_version_info << endl;
911         exit(0);
912         return 0;
913 }
914
915 int parse_sysdir(string const & arg, string const &)
916 {
917         if (arg.empty()) {
918                 lyxerr << _("Missing directory for -sysdir switch") << endl;
919                 exit(1);
920         }
921         cl_system_support = arg;
922         return 1;
923 }
924
925 int parse_userdir(string const & arg, string const &)
926 {
927         if (arg.empty()) {
928                 lyxerr << _("Missing directory for -userdir switch") << endl;
929                 exit(1);
930         }
931         cl_user_support = arg;
932         return 1;
933 }
934
935 int parse_execute(string const & arg, string const &)
936 {
937         if (arg.empty()) {
938                 lyxerr << _("Missing command string after --execute switch") << endl;
939                 exit(1);
940         }
941         batch = arg;
942         return 1;
943 }
944
945 int parse_export(string const & type, string const &)
946 {
947         if (type.empty()) {
948                 lyxerr << _("Missing file type [eg latex, ps...] after "
949                         "--export switch") << endl;
950                 exit(1);
951         }
952         batch = "buffer-export " + type;
953         is_gui = false;
954         return 1;
955 }
956
957 int parse_import(string const & type, string const & file)
958 {
959         if (type.empty()) {
960                 lyxerr << _("Missing file type [eg latex, ps...] after "
961                         "--import switch") << endl;
962                 exit(1);
963         }
964         if (file.empty()) {
965                 lyxerr << _("Missing filename for --import") << endl;
966                 exit(1);
967         }
968
969         batch = "buffer-import " + type + ' ' + file;
970         return 2;
971 }
972
973 } // namespace anon
974
975
976 bool LyX::easyParse(int & argc, char * argv[])
977 {
978         std::map<string, cmd_helper> cmdmap;
979
980         cmdmap["-dbg"] = parse_dbg;
981         cmdmap["-help"] = parse_help;
982         cmdmap["--help"] = parse_help;
983         cmdmap["-version"] = parse_version;
984         cmdmap["--version"] = parse_version;
985         cmdmap["-sysdir"] = parse_sysdir;
986         cmdmap["-userdir"] = parse_userdir;
987         cmdmap["-x"] = parse_execute;
988         cmdmap["--execute"] = parse_execute;
989         cmdmap["-e"] = parse_export;
990         cmdmap["--export"] = parse_export;
991         cmdmap["-i"] = parse_import;
992         cmdmap["--import"] = parse_import;
993
994         for (int i = 1; i < argc; ++i) {
995                 std::map<string, cmd_helper>::const_iterator it
996                         = cmdmap.find(argv[i]);
997
998                 // don't complain if not found - may be parsed later
999                 if (it == cmdmap.end())
1000                         continue;
1001
1002                 string arg((i + 1 < argc) ? argv[i + 1] : "");
1003                 string arg2((i + 2 < argc) ? argv[i + 2] : "");
1004
1005                 int const remove = 1 + it->second(arg, arg2);
1006
1007                 // Now, remove used arguments by shifting
1008                 // the following ones remove places down.
1009                 argc -= remove;
1010                 for (int j = i; j < argc; ++j)
1011                         argv[j] = argv[j + remove];
1012                 --i;
1013         }
1014
1015         batch_command = batch;
1016
1017         return is_gui;
1018 }