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