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