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