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