]> git.lyx.org Git - lyx.git/blob - src/support/Package.cpp
InsetTabular.cpp: fix #6585 also for wrapped floats - thanks Vincent
[lyx.git] / src / support / Package.cpp
1 // -*- C++ -*-
2 /**
3  * \file package.cpp
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Angus Leeming
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "support/Package.h"
15
16 #include "support/debug.h"
17 #include "support/environment.h"
18 #include "support/ExceptionMessage.h"
19 #include "support/filetools.h"
20 #include "support/gettext.h"
21 #include "support/lassert.h"
22 #include "support/lstrings.h"
23 #include "support/os.h"
24
25 #if defined (USE_WINDOWS_PACKAGING)
26 # include "support/os_win32.h"
27 #endif
28
29
30 #include <list>
31
32 #if !defined (USE_WINDOWS_PACKAGING) && \
33     !defined (USE_MACOSX_PACKAGING) && \
34     !defined (USE_POSIX_PACKAGING)
35 #error USE_FOO_PACKAGING must be defined for FOO = WINDOWS, MACOSX or POSIX.
36 #endif
37
38 #if defined (USE_MACOSX_PACKAGING)
39 # include <CoreServices/CoreServices.h> // FSFindFolder, FSRefMakePath
40 #endif
41
42 using namespace std;
43
44 namespace lyx {
45 namespace support {
46
47 namespace {
48
49 Package package_;
50 bool initialised_ = false;
51
52 } // namespace anon
53
54
55 void init_package(string const & command_line_arg0,
56                   string const & command_line_system_support_dir,
57                   string const & command_line_user_support_dir,
58                   exe_build_dir_to_top_build_dir top_build_dir_location)
59 {
60         package_ = Package(command_line_arg0,
61                            command_line_system_support_dir,
62                            command_line_user_support_dir,
63                            top_build_dir_location);
64         initialised_ = true;
65 }
66
67
68 Package const & package()
69 {
70         LASSERT(initialised_, /**/);
71         return package_;
72 }
73
74
75 namespace {
76
77 FileName const abs_path_from_binary_name(string const & exe);
78
79 void buildDirs(FileName const & abs_binary,
80         exe_build_dir_to_top_build_dir top_build_dir_location,
81         FileName &, FileName &);
82
83 FileName const get_document_dir(FileName const & home_dir);
84
85 FileName const get_home_dir();
86
87 FileName const get_locale_dir(FileName const & system_support_dir);
88
89 FileName const get_system_support_dir(FileName const & abs_binary,
90                                     string const & command_line_system_support_dir);
91
92 FileName const get_default_user_support_dir(FileName const & home_dir);
93
94 bool userSupportDir(FileName const & default_user_support_dir,
95                      string const & command_line_user_support_dir, FileName & result);
96
97
98 string const & with_version_suffix();
99
100 } // namespace anon
101
102
103 Package::Package(string const & command_line_arg0,
104                  string const & command_line_system_support_dir,
105                  string const & command_line_user_support_dir,
106                  exe_build_dir_to_top_build_dir top_build_dir_location)
107         : explicit_user_support_dir_(false)
108 {
109         home_dir_ = get_home_dir();
110         // Specification of temp_dir_ may be reset by LyXRC,
111         // but the default is fixed for a given OS.
112         system_temp_dir_ = FileName::tempPath();
113         temp_dir_ = system_temp_dir_;
114         document_dir_ = get_document_dir(home_dir_);
115
116         FileName const abs_binary = abs_path_from_binary_name(command_line_arg0);
117         binary_dir_ = FileName(onlyPath(abs_binary.absFileName()));
118
119         // Is LyX being run in-place from the build tree?
120         buildDirs(abs_binary, top_build_dir_location,
121                 build_support_dir_, system_support_dir_);
122
123         if (build_support_dir_.empty())
124                 system_support_dir_ =
125                         get_system_support_dir(abs_binary,
126                                                command_line_system_support_dir);
127
128         locale_dir_ = get_locale_dir(system_support_dir_);
129
130         FileName const default_user_support_dir =
131                 get_default_user_support_dir(home_dir_);
132
133         explicit_user_support_dir_ = userSupportDir(default_user_support_dir,
134                                      command_line_user_support_dir, user_support_dir_);
135
136         FileName const configure_script(addName(system_support().absFileName(), "configure.py"));
137         configure_command_ = os::python() + ' ' +
138                         quoteName(configure_script.toFilesystemEncoding(), quote_python) +
139                         with_version_suffix();
140
141         LYXERR(Debug::INIT, "<package>\n"
142                 << "\tbinary_dir " << binary_dir().absFileName() << '\n'
143                 << "\tsystem_support " << system_support().absFileName() << '\n'
144                 << "\tbuild_support " << build_support().absFileName() << '\n'
145                 << "\tuser_support " << user_support().absFileName() << '\n'
146                 << "\tlocale_dir " << locale_dir().absFileName() << '\n'
147                 << "\tdocument_dir " << document_dir().absFileName() << '\n'
148                 << "\ttemp_dir " << temp_dir().absFileName() << '\n'
149                 << "\thome_dir " << home_dir().absFileName() << '\n'
150                 << "</package>\n");
151 }
152
153
154 void Package::set_temp_dir(FileName const & temp_dir) const
155 {
156         if (temp_dir.empty())
157                 temp_dir_ = system_temp_dir_;
158         else
159                 temp_dir_ = temp_dir;
160 }
161
162
163 namespace {
164
165 // These next functions contain the stuff that is substituted at
166 // configuration-time.
167 FileName const hardcoded_localedir()
168 {
169         // FIXME UNICODE
170         // The build system needs to make sure that this is in utf8 encoding.
171         return FileName(LYX_ABS_INSTALLED_LOCALEDIR);
172 }
173
174
175 FileName const hardcoded_system_support_dir()
176 {
177         // FIXME UNICODE
178         // The build system needs to make sure that this is in utf8 encoding.
179         return FileName(LYX_ABS_INSTALLED_DATADIR);
180 }
181
182
183 string const & with_version_suffix()
184 {
185         static string const program_suffix = PROGRAM_SUFFIX;
186         static string const with_version_suffix =
187                 " --with-version-suffix=" PROGRAM_SUFFIX;
188         return program_suffix.empty() ? program_suffix : with_version_suffix;
189 }
190
191 } // namespace anon
192
193
194 FileName const & Package::top_srcdir()
195 {
196         // FIXME UNICODE
197         // The build system needs to make sure that this is in utf8 encoding.
198         static FileName const dir(LYX_ABS_TOP_SRCDIR);
199         return dir;
200 }
201
202
203 namespace {
204
205 bool check_command_line_dir(string const & dir,
206                             string const & file,
207                             string const & command_line_switch);
208
209 FileName const extract_env_var_dir(string const & env_var);
210
211 bool check_env_var_dir(FileName const & dir,
212                        string const & env_var);
213
214 bool check_env_var_dir(FileName const & dir,
215                        string const & file,
216                        string const & env_var);
217
218 string const relative_locale_dir();
219
220 string const relative_system_support_dir();
221
222
223 /**
224  * Convert \p name to internal path and strip a trailing slash, since it
225  * comes from user input (commandline or environment).
226  * \p name is encoded in utf8.
227  */
228 string const fix_dir_name(string const & name)
229 {
230         return rtrim(os::internal_path(name), "/");
231 }
232
233
234 FileName buildSupportDir(string const & binary_dir,
235                       exe_build_dir_to_top_build_dir top_build_dir_location)
236 {
237         string indirection;
238         switch (top_build_dir_location) {
239         case top_build_dir_is_one_level_up:
240                 indirection = "../lib";
241                 break;
242         case top_build_dir_is_two_levels_up:
243                 indirection = "../../lib";
244                 break;
245         }
246         return FileName(addPath(binary_dir, indirection));
247 }
248
249
250 void buildDirs(FileName const & abs_binary,
251   exe_build_dir_to_top_build_dir top_build_dir_location,
252         FileName & build_support_dir, FileName & system_support_dir)
253 {
254         string const check_text = "Checking whether LyX is run in place...";
255
256         // We're looking for "Makefile" in a directory
257         //   binary_dir/../lib
258         // We're also looking for "chkconfig.ltx" in a directory
259         //   top_srcdir()/lib
260         // If both are found, then we're running LyX in-place.
261
262         // Note that the name of the lyx binary may be a symbolic link.
263         // If that is the case, then we follow the links too.
264         FileName binary = abs_binary;
265         while (true) {
266                 // Try and find "lyxrc.defaults".
267                 string binary_dir = onlyPath(binary.absFileName());
268                 build_support_dir = buildSupportDir(binary_dir, top_build_dir_location);
269                 if (!fileSearch(build_support_dir.absFileName(), "Makefile").empty()) {
270                         // Try and find "chkconfig.ltx".
271                         system_support_dir =
272                                 FileName(addPath(Package::top_srcdir().absFileName(), "lib"));
273
274                         if (!fileSearch(system_support_dir.absFileName(), "chkconfig.ltx").empty()) {
275                                 LYXERR(Debug::INIT, check_text << " yes");
276                                 return;
277                         }
278                 }
279
280                 // Check whether binary is a symbolic link.
281                 // If so, resolve it and repeat the exercise.
282                 if (!binary.isSymLink())
283                         break;
284
285                 FileName link;
286                 if (readLink(binary, link)) {
287                         binary = link;
288                 } else {
289                         // Unable to resolve the link.
290                         break;
291                 }
292         }
293
294         LYXERR(Debug::INIT, check_text << " no");
295         system_support_dir = FileName();
296         build_support_dir = FileName();
297 }
298
299
300 // Specification of document_dir_ may be reset by LyXRC,
301 // but the default is fixed for a given OS.
302 FileName const get_document_dir(FileName const & home_dir)
303 {
304 #if defined (USE_WINDOWS_PACKAGING)
305         (void)home_dir; // Silence warning about unused variable.
306         os::GetFolderPath win32_folder_path;
307         return FileName(win32_folder_path(os::GetFolderPath::PERSONAL));
308 #else // Posix-like.
309         return home_dir;
310 #endif
311 }
312
313
314 // The specification of home_dir_ is fixed for a given OS.
315 // A typical example on Windows: "C:/Documents and Settings/USERNAME"
316 // and on a Posix-like machine: "/home/USERNAME".
317 FileName const get_home_dir()
318 {
319 #if defined (USE_WINDOWS_PACKAGING)
320         string const home_dir = getEnv("USERPROFILE");
321 #else // Posix-like.
322         string const home_dir = getEnv("HOME");
323 #endif
324
325         return FileName(fix_dir_name(home_dir));
326 }
327
328
329 // Several sources are probed to ascertain the locale directory.
330 // The only requirement is that the result is indeed a directory.
331 FileName const get_locale_dir(FileName const & system_support_dir)
332 {
333         // 1. Use the "LYX_LOCALEDIR" environment variable.
334         FileName const path_env = extract_env_var_dir("LYX_LOCALEDIR");
335         if (!path_env.empty() && check_env_var_dir(path_env, "LYX_LOCALEDIR"))
336                 return path_env;
337
338         // 2. Search for system_support_dir / <relative locale dir>
339         // The <relative locale dir> is OS-dependent. (On Unix, it will
340         // be "../locale/".)
341         FileName path(addPath(system_support_dir.absFileName(),
342                 relative_locale_dir()));
343
344         if (path.exists() && path.isDirectory())
345                 return path;
346
347         // 3. Fall back to the hard-coded LOCALEDIR.
348         path = hardcoded_localedir();
349         if (path.exists() && path.isDirectory())
350                 return path;
351
352         return FileName();
353 }
354
355
356 // Extracts the absolute path from the foo of "-sysdir foo" or "-userdir foo"
357 FileName const abs_path_from_command_line(string const & command_line)
358 {
359         if (command_line.empty())
360                 return FileName();
361
362         string const str_path = fix_dir_name(command_line);
363         return makeAbsPath(str_path);
364 }
365
366
367 // Does the grunt work for abs_path_from_binary_name()
368 FileName const get_binary_path(string const & exe)
369 {
370 #if defined (USE_WINDOWS_PACKAGING)
371         // The executable may have been invoked either with or
372         // without the .exe extension.
373         // Ensure that it is present.
374         string const as_internal_path = os::internal_path(exe);
375         string const exe_path = suffixIs(as_internal_path, ".exe") ?
376                 as_internal_path : as_internal_path + ".exe";
377 #else
378         string const exe_path = os::internal_path(exe);
379 #endif
380         if (FileName::isAbsolute(exe_path))
381                 return FileName(exe_path);
382
383         // Two possibilities present themselves.
384         // 1. The binary is relative to the CWD.
385         FileName const abs_exe_path = makeAbsPath(exe_path);
386         if (abs_exe_path.exists())
387                 return abs_exe_path;
388
389         // 2. exe must be the name of the binary only and it
390         // can be found on the PATH.
391         string const exe_name = onlyFileName(exe_path);
392         if (exe_name != exe_path)
393                 return FileName();
394
395         vector<string> const path = getEnvPath("PATH");
396         vector<string>::const_iterator it = path.begin();
397         vector<string>::const_iterator const end = path.end();
398         for (; it != end; ++it) {
399                 // This will do nothing if *it is already absolute.
400                 string const exe_dir = makeAbsPath(*it).absFileName();
401
402                 FileName const exe_path(addName(exe_dir, exe_name));
403                 if (exe_path.exists())
404                         return exe_path;
405         }
406
407         // Didn't find anything.
408         return FileName();
409 }
410
411
412 // Extracts the absolute path to the binary name received as argv[0].
413 FileName const abs_path_from_binary_name(string const & exe)
414 {
415         FileName const abs_binary = get_binary_path(exe);
416         if (abs_binary.empty()) {
417                 // FIXME UNICODE
418                 throw ExceptionMessage(ErrorException,
419                         _("LyX binary not found"),
420                         bformat(_("Unable to determine the path to the LyX binary from the command line %1$s"),
421                                 from_utf8(exe)));
422         }
423         return abs_binary;
424 }
425
426
427 // A plethora of directories is searched to ascertain the system
428 // lyxdir which is defined as the first directory to contain
429 // "chkconfig.ltx".
430 FileName const
431 get_system_support_dir(FileName const & abs_binary,
432                   string const & command_line_system_support_dir)
433 {
434         string const chkconfig_ltx = "chkconfig.ltx";
435
436         // searched_dirs is used for diagnostic purposes only in the case
437         // that "chkconfig.ltx" is not found.
438         list<FileName> searched_dirs;
439
440         // 1. Use the -sysdir command line parameter.
441         FileName path = abs_path_from_command_line(command_line_system_support_dir);
442         if (!path.empty()) {
443                 searched_dirs.push_back(path);
444                 if (check_command_line_dir(path.absFileName(), chkconfig_ltx, "-sysdir"))
445                         return path;
446         }
447
448         // 2. Use the "LYX_DIR_${major}${minor}x" environment variable.
449         path = extract_env_var_dir(LYX_DIR_VER);
450         if (!path.empty()) {
451                 searched_dirs.push_back(path);
452                 if (check_env_var_dir(path, chkconfig_ltx, LYX_DIR_VER))
453                         return path;
454         }
455
456         // 3. Search relative to the lyx binary.
457         // We're looking for "chkconfig.ltx" in a directory
458         //   OnlyPath(abs_binary) / <relative dir> / PACKAGE /
459         // PACKAGE is hardcoded in config.h. Eg "lyx" or "lyx-1.3.6cvs".
460         // <relative dir> is OS-dependent; on Unix, it will be "../share/".
461         string const relative_lyxdir = relative_system_support_dir();
462
463         // One subtlety to be aware of. The name of the lyx binary may be
464         // a symbolic link. If that is the case, then we follow the links too.
465         FileName binary = abs_binary;
466         while (true) {
467                 // Try and find "chkconfig.ltx".
468                 string const binary_dir = onlyPath(binary.absFileName());
469
470                 FileName const lyxdir(addPath(binary_dir, relative_lyxdir));
471                 searched_dirs.push_back(lyxdir);
472
473                 if (!fileSearch(lyxdir.absFileName(), chkconfig_ltx).empty()) {
474                         // Success! "chkconfig.ltx" has been found.
475                         return lyxdir;
476                 }
477
478                 // Check whether binary is a symbolic link.
479                 // If so, resolve it and repeat the exercise.
480                 if (!binary.isSymLink())
481                         break;
482
483                 FileName link;
484                 if (readLink(binary, link)) {
485                         binary = link;
486                 } else {
487                         // Unable to resolve the link.
488                         break;
489                 }
490         }
491
492         // 4. Repeat the exercise on the directory itself.
493         FileName binary_dir(onlyPath(abs_binary.absFileName()));
494         while (true) {
495                 // This time test whether the directory is a symbolic link
496                 // *before* looking for "chkconfig.ltx".
497                 // (We've looked relative to the original already.)
498                 if (!binary.isSymLink())
499                         break;
500
501                 FileName link;
502                 if (readLink(binary_dir, link)) {
503                         binary_dir = link;
504                 } else {
505                         // Unable to resolve the link.
506                         break;
507                 }
508
509                 // Try and find "chkconfig.ltx".
510                 FileName const lyxdir(addPath(binary_dir.absFileName(),
511                         relative_lyxdir));
512                 searched_dirs.push_back(lyxdir);
513
514                 if (!fileSearch(lyxdir.absFileName(), chkconfig_ltx).empty()) {
515                         // Success! "chkconfig.ltx" has been found.
516                         return lyxdir;
517                 }
518         }
519
520         // 5. In desparation, try the hard-coded system support dir.
521         path = hardcoded_system_support_dir();
522         if (!fileSearch(path.absFileName(), chkconfig_ltx).empty())
523                 return path;
524
525         // Everything has failed :-(
526         // So inform the user and exit.
527         string searched_dirs_str;
528         typedef list<FileName>::const_iterator iterator;
529         iterator const begin = searched_dirs.begin();
530         iterator const end = searched_dirs.end();
531         for (iterator it = begin; it != end; ++it) {
532                 if (it != begin)
533                         searched_dirs_str += "\n\t";
534                 searched_dirs_str += it->absFileName();
535         }
536
537         // FIXME UNICODE
538         throw ExceptionMessage(ErrorException, _("No system directory"),
539                 bformat(_("Unable to determine the system directory "
540                                 "having searched\n"
541                                 "\t%1$s\n"
542                                 "Use the '-sysdir' command line parameter or "
543                                 "set the environment variable\n%2$s "
544                                 "to the LyX system directory containing the "
545                                 "file `chkconfig.ltx'."),
546                           from_utf8(searched_dirs_str), from_ascii(LYX_DIR_VER)));
547
548         // Keep the compiler happy.
549         return FileName();
550 }
551
552
553 // Returns the absolute path to the user lyxdir, together with a flag
554 // indicating whether this directory was specified explicitly (as -userdir
555 // or through an environment variable) or whether it was deduced.
556 bool userSupportDir(FileName const & default_user_support_dir,
557         string const & command_line_user_support_dir, FileName & result)
558 {
559         // 1. Use the -userdir command line parameter.
560         result = abs_path_from_command_line(command_line_user_support_dir);
561         if (!result.empty())
562                 return true;
563
564         // 2. Use the LYX_USERDIR_${major}${minor}x environment variable.
565         result = extract_env_var_dir(LYX_USERDIR_VER);
566         if (!result.empty())
567                 return true;
568
569         // 3. Use the OS-dependent default_user_support_dir
570         result = default_user_support_dir;
571         return false;
572 }
573
574
575 // $HOME/.lyx on POSIX but on Win32 it will be something like
576 // "C:/Documents and Settings/USERNAME/Application Data/LyX"
577 FileName const get_default_user_support_dir(FileName const & home_dir)
578 {
579 #if defined (USE_WINDOWS_PACKAGING)
580         (void)home_dir; // Silence warning about unused variable.
581
582         os::GetFolderPath win32_folder_path;
583         return FileName(addPath(win32_folder_path(os::GetFolderPath::APPDATA), PACKAGE));
584
585 #elif defined (USE_MACOSX_PACKAGING)
586         (void)home_dir; // Silence warning about unused variable.
587
588         FSRef fsref;
589         OSErr const error_code =
590                 FSFindFolder(kUserDomain, kApplicationSupportFolderType,
591                              kDontCreateFolder, &fsref);
592         if (error_code != 0)
593                 return FileName();
594
595         // FSRefMakePath returns the result in utf8
596         char store[PATH_MAX + 1];
597         OSStatus const status_code =
598                 FSRefMakePath(&fsref,
599                               reinterpret_cast<UInt8*>(store), PATH_MAX);
600         if (status_code != 0)
601                 return FileName();
602
603         return FileName(addPath(reinterpret_cast<char const *>(store), PACKAGE));
604
605 #else // USE_POSIX_PACKAGING
606         return FileName(addPath(home_dir.absFileName(), string(".") + PACKAGE));
607 #endif
608 }
609
610
611 // Check that directory @c dir contains @c file.
612 // Else emit an error message about an invalid @c command_line_switch.
613 bool check_command_line_dir(string const & dir,
614                             string const & file,
615                             string const & command_line_switch)
616 {
617         FileName const abs_path = fileSearch(dir, file);
618         if (abs_path.empty()) {
619                 // FIXME UNICODE
620                 throw ExceptionMessage(ErrorException, _("File not found"), bformat(
621                         _("Invalid %1$s switch.\nDirectory %2$s does not contain %3$s."),
622                         from_utf8(command_line_switch), from_utf8(dir),
623                         from_utf8(file)));
624         }
625
626         return !abs_path.empty();
627 }
628
629
630 // The environment variable @c env_var expands to a (single) file path.
631 FileName const extract_env_var_dir(string const & env_var)
632 {
633         string const dir = fix_dir_name(getEnv(env_var));
634         return dir.empty() ? FileName() : makeAbsPath(dir);
635 }
636
637
638 // Check that directory @c dir contains @c file.
639 // Else emit a warning about an invalid @c env_var.
640 bool check_env_var_dir(FileName const & dir,
641                        string const & file,
642                        string const & env_var)
643 {
644         FileName const abs_path = fileSearch(dir.absFileName(), file);
645         if (abs_path.empty()) {
646                 // FIXME UNICODE
647                 throw ExceptionMessage(WarningException, _("File not found"), bformat(
648                         _("Invalid %1$s environment variable.\n"
649                                 "Directory %2$s does not contain %3$s."),
650                         from_utf8(env_var), from_utf8(dir.absFileName()),
651                         from_utf8(file)));
652         }
653
654         return !abs_path.empty();
655 }
656
657
658 // Check that directory @c dir is indeed a directory.
659 // Else emit a warning about an invalid @c env_var.
660 bool check_env_var_dir(FileName const & dir,
661                        string const & env_var)
662 {
663         bool const success = dir.exists() && dir.isDirectory();
664
665         if (!success) {
666                 // Put this string on a single line so that the gettext
667                 // search mechanism in po/Makefile.in.in will register
668                 // Package.cpp.in as a file containing strings that need
669                 // translation.
670                 // FIXME UNICODE
671                 docstring const fmt =
672                         _("Invalid %1$s environment variable.\n%2$s is not a directory.");
673
674                 throw ExceptionMessage(WarningException, _("Directory not found"), bformat(
675                         fmt, from_utf8(env_var), from_utf8(dir.absFileName())));
676         }
677
678         return success;
679 }
680
681
682 // The locale directory relative to the LyX system directory.
683 string const relative_locale_dir()
684 {
685 #if defined (USE_WINDOWS_PACKAGING) || defined (USE_MACOSX_PACKAGING)
686         return "locale/";
687 #else
688         return "../locale/";
689 #endif
690 }
691
692
693 // The system lyxdir is relative to the directory containing the LyX binary.
694 string const relative_system_support_dir()
695 {
696         string result;
697
698 #if defined (USE_WINDOWS_PACKAGING) || defined (USE_MACOSX_PACKAGING)
699         result = "../Resources/";
700 #else // Posix-like.
701         result = addPath("../share/", PACKAGE);
702 #endif
703
704         return result;
705 }
706
707 } // namespace anon
708
709 } // namespace support
710 } // namespace lyx