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