]> git.lyx.org Git - lyx.git/blob - src/lyx_main.C
Remove the now superseeded SConscript files, and some small missing parts to SConstruct
[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         system_lyxrc = lyxrc;
462         system_formats = formats;
463         system_converters = converters;
464         system_movers = movers;
465         system_lcolor = lcolor;
466
467         // This one is edited through the preferences dialog.
468         readRcFile("preferences");
469
470         readEncodingsFile("encodings");
471         readLanguagesFile("languages");
472
473         // Load the layouts
474         lyxerr[Debug::INIT] << "Reading layouts..." << endl;
475         LyXSetStyle();
476
477         if (gui) {
478                 // Set up bindings
479                 toplevel_keymap.reset(new kb_keymap);
480                 defaultKeyBindings(toplevel_keymap.get());
481                 toplevel_keymap->read(lyxrc.bind_file);
482
483                 // Read menus
484                 readUIFile(lyxrc.ui_file);
485         }
486
487         if (lyxerr.debugging(Debug::LYXRC))
488                 lyxrc.print();
489
490         os::cygwin_path_fix(lyxrc.cygwin_path_fix);
491         if (!lyxrc.path_prefix.empty())
492                 prependEnvPath("PATH", lyxrc.path_prefix);
493
494         if (fs::exists(lyxrc.document_path) &&
495             fs::is_directory(lyxrc.document_path))
496                 package().document_dir() = lyxrc.document_path;
497
498         package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
499         if (package().temp_dir().empty()) {
500                 Alert::error(_("Could not create temporary directory"),
501                              bformat(_("Could not create a temporary directory in\n"
502                                        "%1$s. Make sure that this\n"
503                                        "path exists and is writable and try again."),
504                                      lyxrc.tempdir_path));
505                 // createLyXTmpDir() tries sufficiently hard to create a
506                 // usable temp dir, so the probability to come here is
507                 // close to zero. We therefore don't try to overcome this
508                 // problem with e.g. asking the user for a new path and
509                 // trying again but simply exit.
510                 exit(EXIT_FAILURE);
511         }
512
513         if (lyxerr.debugging(Debug::INIT)) {
514                 lyxerr << "LyX tmp dir: `" << package().temp_dir() << '\'' << endl;
515         }
516
517         lyxerr[Debug::INIT] << "Reading session information '.lyx/session'..." << endl;
518         session_.reset(new lyx::Session(lyxrc.num_lastfiles));
519 }
520
521
522 void LyX::defaultKeyBindings(kb_keymap  * kbmap)
523 {
524         kbmap->bind("Right", FuncRequest(LFUN_CHAR_FORWARD));
525         kbmap->bind("Left", FuncRequest(LFUN_CHAR_BACKWARD));
526         kbmap->bind("Up", FuncRequest(LFUN_UP));
527         kbmap->bind("Down", FuncRequest(LFUN_DOWN));
528
529         kbmap->bind("Tab", FuncRequest(LFUN_CELL_FORWARD));
530         kbmap->bind("C-Tab", FuncRequest(LFUN_CELL_SPLIT));
531         kbmap->bind("~S-ISO_Left_Tab", FuncRequest(LFUN_CELL_BACKWARD));
532         kbmap->bind("~S-BackTab", FuncRequest(LFUN_CELL_BACKWARD));
533
534         kbmap->bind("Home", FuncRequest(LFUN_LINE_BEGIN));
535         kbmap->bind("End", FuncRequest(LFUN_LINE_END));
536         kbmap->bind("Prior", FuncRequest(LFUN_SCREEN_UP));
537         kbmap->bind("Next", FuncRequest(LFUN_SCREEN_DOWN));
538
539         kbmap->bind("Return", FuncRequest(LFUN_BREAK_PARAGRAPH));
540         //kbmap->bind("~C-~S-~M-nobreakspace", FuncRequest(LFUN_PROTECTEDSPACE));
541
542         kbmap->bind("Delete", FuncRequest(LFUN_CHAR_DELETE_FORWARD));
543         kbmap->bind("BackSpace", FuncRequest(LFUN_CHAR_DELETE_BACKWARD));
544
545         // kbmap->bindings to enable the use of the numeric keypad
546         // e.g. Num Lock set
547         //kbmap->bind("KP_0", FuncRequest(LFUN_SELF_INSERT));
548         //kbmap->bind("KP_Decimal", FuncRequest(LFUN_SELF_INSERT));
549         kbmap->bind("KP_Enter", FuncRequest(LFUN_BREAK_PARAGRAPH));
550         //kbmap->bind("KP_1", FuncRequest(LFUN_SELF_INSERT));
551         //kbmap->bind("KP_2", FuncRequest(LFUN_SELF_INSERT));
552         //kbmap->bind("KP_3", FuncRequest(LFUN_SELF_INSERT));
553         //kbmap->bind("KP_4", FuncRequest(LFUN_SELF_INSERT));
554         //kbmap->bind("KP_5", FuncRequest(LFUN_SELF_INSERT));
555         //kbmap->bind("KP_6", FuncRequest(LFUN_SELF_INSERT));
556         //kbmap->bind("KP_Add", FuncRequest(LFUN_SELF_INSERT));
557         //kbmap->bind("KP_7", FuncRequest(LFUN_SELF_INSERT));
558         //kbmap->bind("KP_8", FuncRequest(LFUN_SELF_INSERT));
559         //kbmap->bind("KP_9", FuncRequest(LFUN_SELF_INSERT));
560         //kbmap->bind("KP_Divide", FuncRequest(LFUN_SELF_INSERT));
561         //kbmap->bind("KP_Multiply", FuncRequest(LFUN_SELF_INSERT));
562         //kbmap->bind("KP_Subtract", FuncRequest(LFUN_SELF_INSERT));
563         kbmap->bind("KP_Right", FuncRequest(LFUN_CHAR_FORWARD));
564         kbmap->bind("KP_Left", FuncRequest(LFUN_CHAR_BACKWARD));
565         kbmap->bind("KP_Up", FuncRequest(LFUN_UP));
566         kbmap->bind("KP_Down", FuncRequest(LFUN_DOWN));
567         kbmap->bind("KP_Home", FuncRequest(LFUN_LINE_BEGIN));
568         kbmap->bind("KP_End", FuncRequest(LFUN_LINE_END));
569         kbmap->bind("KP_Prior", FuncRequest(LFUN_SCREEN_UP));
570         kbmap->bind("KP_Next", FuncRequest(LFUN_SCREEN_DOWN));
571 }
572
573
574 void LyX::emergencyCleanup() const
575 {
576         // what to do about tmpfiles is non-obvious. we would
577         // like to delete any we find, but our lyxdir might
578         // contain documents etc. which might be helpful on
579         // a crash
580
581         bufferlist.emergencyWriteAll();
582         if (lyxserver)
583                 lyxserver->emergencyCleanup();
584 }
585
586
587 void LyX::deadKeyBindings(kb_keymap * kbmap)
588 {
589         // bindKeyings for transparent handling of deadkeys
590         // The keysyms are gotten from XFree86 X11R6
591         kbmap->bind("~C-~S-~M-dead_acute", FuncRequest(LFUN_ACCENT_ACUTE));
592         kbmap->bind("~C-~S-~M-dead_breve", FuncRequest(LFUN_ACCENT_BREVE));
593         kbmap->bind("~C-~S-~M-dead_caron", FuncRequest(LFUN_ACCENT_CARON));
594         kbmap->bind("~C-~S-~M-dead_cedilla", FuncRequest(LFUN_ACCENT_CEDILLA));
595         kbmap->bind("~C-~S-~M-dead_abovering", FuncRequest(LFUN_ACCENT_CIRCLE));
596         kbmap->bind("~C-~S-~M-dead_circumflex", FuncRequest(LFUN_ACCENT_CIRCUMFLEX));
597         kbmap->bind("~C-~S-~M-dead_abovedot", FuncRequest(LFUN_ACCENT_DOT));
598         kbmap->bind("~C-~S-~M-dead_grave", FuncRequest(LFUN_ACCENT_GRAVE));
599         kbmap->bind("~C-~S-~M-dead_doubleacute", FuncRequest(LFUN_ACCENT_HUNGARIAN_UMLAUT));
600         kbmap->bind("~C-~S-~M-dead_macron", FuncRequest(LFUN_ACCENT_MACRON));
601         // nothing with this name
602         // kbmap->bind("~C-~S-~M-dead_special_caron", LFUN_ACCENT_SPECIAL_CARON);
603         kbmap->bind("~C-~S-~M-dead_tilde", FuncRequest(LFUN_ACCENT_TILDE));
604         kbmap->bind("~C-~S-~M-dead_diaeresis", FuncRequest(LFUN_ACCENT_UMLAUT));
605         // nothing with this name either...
606         //kbmap->bind("~C-~S-~M-dead_underbar", FuncRequest(LFUN_ACCENT_UNDERBAR));
607         kbmap->bind("~C-~S-~M-dead_belowdot", FuncRequest(LFUN_ACCENT_UNDERDOT));
608         kbmap->bind("~C-~S-~M-dead_tie", FuncRequest(LFUN_ACCENT_TIE));
609         kbmap->bind("~C-~S-~M-dead_ogonek",FuncRequest(LFUN_ACCENT_OGONEK));
610 }
611
612
613 namespace {
614
615 // return true if file does not exist or is older than configure.py.
616 bool needsUpdate(string const & file)
617 {
618         static string const configure_script =
619                 addName(package().system_support(), "configure.py");
620         string const absfile =
621                 addName(package().user_support(), file);
622
623         return (! fs::exists(absfile))
624                 || (fs::last_write_time(configure_script) 
625                     > fs::last_write_time(absfile));
626 }
627
628 }
629
630
631 bool LyX::queryUserLyXDir(bool explicit_userdir)
632 {
633         // Does user directory exist?
634         if (fs::exists(package().user_support()) &&
635             fs::is_directory(package().user_support())) {
636                 first_start = false;
637                 
638                 return needsUpdate("lyxrc.defaults") 
639                         || needsUpdate("textclass.lst") 
640                         || needsUpdate("packages.lst");
641         }
642
643         first_start = !explicit_userdir;
644
645         // If the user specified explicitly a directory, ask whether
646         // to create it. If the user says "no", then exit.
647         if (explicit_userdir &&
648             Alert::prompt(
649                     _("Missing user LyX directory"),
650                     bformat(_("You have specified a non-existent user "
651                               "LyX directory, %1$s.\n"
652                               "It is needed to keep your own configuration."),
653                             package().user_support()),
654                     1, 0,
655                     _("&Create directory"),
656                     _("&Exit LyX"))) {
657                 lyxerr << _("No user LyX directory. Exiting.") << endl;
658                 exit(1);
659         }
660
661         lyxerr << bformat(_("LyX: Creating directory %1$s"),
662                           package().user_support())
663                << endl;
664
665         if (!createDirectory(package().user_support(), 0755)) {
666                 // Failed, so let's exit.
667                 lyxerr << _("Failed to create directory. Exiting.")
668                        << endl;
669                 exit(1);
670         }
671
672         return true;
673 }
674
675
676 void LyX::readRcFile(string const & name)
677 {
678         lyxerr[Debug::INIT] << "About to read " << name << "... ";
679
680         string const lyxrc_path = libFileSearch(string(), name);
681         if (!lyxrc_path.empty()) {
682
683                 lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
684
685                 if (lyxrc.read(lyxrc_path) < 0)
686                         showFileError(name);
687         } else
688                 lyxerr[Debug::INIT] << "Not found." << lyxrc_path << endl;
689
690 }
691
692
693 // Read the ui file `name'
694 void LyX::readUIFile(string const & name)
695 {
696         enum Uitags {
697                 ui_menuset = 1,
698                 ui_toolbar,
699                 ui_toolbars,
700                 ui_include,
701                 ui_last
702         };
703
704         struct keyword_item uitags[ui_last - 1] = {
705                 { "include", ui_include },
706                 { "menuset", ui_menuset },
707                 { "toolbar", ui_toolbar },
708                 { "toolbars", ui_toolbars }
709         };
710
711         // Ensure that a file is read only once (prevents include loops)
712         static std::list<string> uifiles;
713         std::list<string>::const_iterator it  = uifiles.begin();
714         std::list<string>::const_iterator end = uifiles.end();
715         it = std::find(it, end, name);
716         if (it != end) {
717                 lyxerr[Debug::INIT] << "UI file '" << name
718                                     << "' has been read already. "
719                                     << "Is this an include loop?"
720                                     << endl;
721                 return;
722         }
723
724         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
725
726         string const ui_path = libFileSearch("ui", name, "ui");
727
728         if (ui_path.empty()) {
729                 lyxerr[Debug::INIT] << "Could not find " << name << endl;
730                 showFileError(name);
731                 return;
732         }
733         uifiles.push_back(name);
734
735         lyxerr[Debug::INIT] << "Found " << name
736                             << " in " << ui_path << endl;
737         LyXLex lex(uitags, ui_last - 1);
738         lex.setFile(ui_path);
739         if (!lex.isOK()) {
740                 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
741                        << endl;
742         }
743
744         if (lyxerr.debugging(Debug::PARSER))
745                 lex.printTable(lyxerr);
746
747         while (lex.isOK()) {
748                 switch (lex.lex()) {
749                 case ui_include: {
750                         lex.next(true);
751                         string const file = lex.getString();
752                         readUIFile(file);
753                         break;
754                 }
755                 case ui_menuset:
756                         menubackend.read(lex);
757                         break;
758
759                 case ui_toolbar:
760                         toolbarbackend.read(lex);
761                         break;
762
763                 case ui_toolbars:
764                         toolbarbackend.readToolbars(lex);
765                         break;
766
767                 default:
768                         if (!rtrim(lex.getString()).empty())
769                                 lex.printError("LyX::ReadUIFile: "
770                                                "Unknown menu tag: `$$Token'");
771                         break;
772                 }
773         }
774 }
775
776
777 // Read the languages file `name'
778 void LyX::readLanguagesFile(string const & name)
779 {
780         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
781
782         string const lang_path = libFileSearch(string(), name);
783         if (lang_path.empty()) {
784                 showFileError(name);
785                 return;
786         }
787         languages.read(lang_path);
788 }
789
790
791 // Read the encodings file `name'
792 void LyX::readEncodingsFile(string const & name)
793 {
794         lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
795
796         string const enc_path = libFileSearch(string(), name);
797         if (enc_path.empty()) {
798                 showFileError(name);
799                 return;
800         }
801         encodings.read(enc_path);
802 }
803
804
805 namespace {
806
807 bool is_gui = true;
808 string batch;
809
810 /// return the the number of arguments consumed
811 typedef boost::function<int(string const &, string const &)> cmd_helper;
812
813 int parse_dbg(string const & arg, string const &)
814 {
815         if (arg.empty()) {
816                 lyxerr << _("List of supported debug flags:") << endl;
817                 Debug::showTags(lyxerr);
818                 exit(0);
819         }
820         lyxerr << bformat(_("Setting debug level to %1$s"), arg) << endl;
821
822         lyxerr.level(Debug::value(arg));
823         Debug::showLevel(lyxerr, lyxerr.level());
824         return 1;
825 }
826
827
828 int parse_help(string const &, string const &)
829 {
830         lyxerr <<
831                 _("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
832                   "Command line switches (case sensitive):\n"
833                   "\t-help              summarize LyX usage\n"
834                   "\t-userdir dir       set user directory to dir\n"
835                   "\t-sysdir dir        set system directory to dir\n"
836                   "\t-geometry WxH+X+Y  set geometry of the main window\n"
837                   "\t-dbg feature[,feature]...\n"
838                   "                  select the features to debug.\n"
839                   "                  Type `lyx -dbg' to see the list of features\n"
840                   "\t-x [--execute] command\n"
841                   "                  where command is a lyx command.\n"
842                   "\t-e [--export] fmt\n"
843                   "                  where fmt is the export format of choice.\n"
844                   "\t-i [--import] fmt file.xxx\n"
845                   "                  where fmt is the import format of choice\n"
846                   "                  and file.xxx is the file to be imported.\n"
847                   "\t-version        summarize version and build info\n"
848                   "Check the LyX man page for more details.") << endl;
849         exit(0);
850         return 0;
851 }
852
853 int parse_version(string const &, string const &)
854 {
855         lyxerr << "LyX " << lyx_version
856                << " of " << lyx_release_date << endl;
857         lyxerr << "Built on " << __DATE__ << ", " << __TIME__ << endl;
858
859         lyxerr << lyx_version_info << endl;
860         exit(0);
861         return 0;
862 }
863
864 int parse_sysdir(string const & arg, string const &)
865 {
866         if (arg.empty()) {
867                 lyxerr << _("Missing directory for -sysdir switch") << endl;
868                 exit(1);
869         }
870         cl_system_support = arg;
871         return 1;
872 }
873
874 int parse_userdir(string const & arg, string const &)
875 {
876         if (arg.empty()) {
877                 lyxerr << _("Missing directory for -userdir switch") << endl;
878                 exit(1);
879         }
880         cl_user_support = arg;
881         return 1;
882 }
883
884 int parse_execute(string const & arg, string const &)
885 {
886         if (arg.empty()) {
887                 lyxerr << _("Missing command string after --execute switch") << endl;
888                 exit(1);
889         }
890         batch = arg;
891         return 1;
892 }
893
894 int parse_export(string const & type, string const &)
895 {
896         if (type.empty()) {
897                 lyxerr << _("Missing file type [eg latex, ps...] after "
898                         "--export switch") << endl;
899                 exit(1);
900         }
901         batch = "buffer-export " + type;
902         is_gui = false;
903         return 1;
904 }
905
906 int parse_import(string const & type, string const & file)
907 {
908         if (type.empty()) {
909                 lyxerr << _("Missing file type [eg latex, ps...] after "
910                         "--import switch") << endl;
911                 exit(1);
912         }
913         if (file.empty()) {
914                 lyxerr << _("Missing filename for --import") << endl;
915                 exit(1);
916         }
917
918         batch = "buffer-import " + type + ' ' + file;
919         return 2;
920 }
921
922 } // namespace anon
923
924
925 bool LyX::easyParse(int & argc, char * argv[])
926 {
927         std::map<string, cmd_helper> cmdmap;
928
929         cmdmap["-dbg"] = parse_dbg;
930         cmdmap["-help"] = parse_help;
931         cmdmap["--help"] = parse_help;
932         cmdmap["-version"] = parse_version;
933         cmdmap["--version"] = parse_version;
934         cmdmap["-sysdir"] = parse_sysdir;
935         cmdmap["-userdir"] = parse_userdir;
936         cmdmap["-x"] = parse_execute;
937         cmdmap["--execute"] = parse_execute;
938         cmdmap["-e"] = parse_export;
939         cmdmap["--export"] = parse_export;
940         cmdmap["-i"] = parse_import;
941         cmdmap["--import"] = parse_import;
942
943         for (int i = 1; i < argc; ++i) {
944                 std::map<string, cmd_helper>::const_iterator it
945                         = cmdmap.find(argv[i]);
946
947                 // don't complain if not found - may be parsed later
948                 if (it == cmdmap.end())
949                         continue;
950
951                 string arg((i + 1 < argc) ? argv[i + 1] : "");
952                 string arg2((i + 2 < argc) ? argv[i + 2] : "");
953
954                 int const remove = 1 + it->second(arg, arg2);
955
956                 // Now, remove used arguments by shifting
957                 // the following ones remove places down.
958                 argc -= remove;
959                 for (int j = i; j < argc; ++j)
960                         argv[j] = argv[j + remove];
961                 --i;
962         }
963
964         batch_command = batch;
965
966         return is_gui;
967 }