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