4 * This file is part of LyX, the document processor.
5 * Licence details can be found in the file COPYING.
7 * \author Angus Leeming
9 * Full author contact details are available in file CREDITS.
14 #include "support/Package.h"
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"
25 #if defined (USE_WINDOWS_PACKAGING)
26 # include "support/os_win32.h"
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.
38 #if defined (USE_MACOSX_PACKAGING)
39 # include <CoreServices/CoreServices.h> // FSFindFolder, FSRefMakePath
50 bool initialised_ = false;
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)
59 package_ = Package(command_line_arg0,
60 command_line_system_support_dir,
61 command_line_user_support_dir);
66 Package const & package()
68 LASSERT(initialised_, /**/);
75 FileName const abs_path_from_binary_name(string const & exe);
78 bool inBuildDir(FileName const & abs_binary, FileName &, FileName &);
80 FileName findLyxBinary(FileName const & abs_binary);
82 FileName const get_document_dir(FileName const & home_dir);
84 FileName const get_locale_dir(FileName const & system_support_dir);
86 FileName const get_system_support_dir(FileName const & abs_binary,
87 string const & command_line_system_support_dir);
89 FileName const get_default_user_support_dir(FileName const & home_dir);
91 bool userSupportDir(FileName const & default_user_support_dir,
92 string const & command_line_user_support_dir, FileName & result);
94 string const & with_version_suffix();
96 string const fix_dir_name(string const & name);
101 Package::Package(string const & command_line_arg0,
102 string const & command_line_system_support_dir,
103 string const & command_line_user_support_dir)
104 : explicit_user_support_dir_(false)
106 // Specification of temp_dir_ may be reset by LyXRC,
107 // but the default is fixed for a given OS.
108 system_temp_dir_ = FileName::tempPath();
109 temp_dir_ = system_temp_dir_;
110 document_dir_ = get_document_dir(get_home_dir());
112 FileName const abs_binary = abs_path_from_binary_name(command_line_arg0);
113 binary_dir_ = FileName(onlyPath(abs_binary.absFileName()));
115 // the LyX package directory
116 lyx_dir_ = FileName(addPath(binary_dir_.absFileName(), "../"));
117 lyx_dir_ = FileName(lyx_dir_.realPath());
119 // Is LyX being run in-place from the build tree?
120 bool in_build_dir = inBuildDir(abs_binary, build_support_dir_, system_support_dir_);
123 system_support_dir_ =
124 get_system_support_dir(abs_binary,
125 command_line_system_support_dir);
128 // Find the LyX executable
129 lyx_binary_ = findLyxBinary(abs_binary);
131 locale_dir_ = get_locale_dir(system_support_dir_);
133 FileName const default_user_support_dir =
134 get_default_user_support_dir(get_home_dir());
136 explicit_user_support_dir_ = userSupportDir(default_user_support_dir,
137 command_line_user_support_dir, user_support_dir_);
139 FileName const configure_script(addName(system_support().absFileName(), "configure.py"));
140 configure_command_ = os::python() + ' ' +
141 quoteName(configure_script.toFilesystemEncoding(), quote_python) +
142 with_version_suffix();
144 LYXERR(Debug::INIT, "<package>\n"
145 << "\tbinary_dir " << binary_dir().absFileName() << '\n'
146 << "\tsystem_support " << system_support().absFileName() << '\n'
147 << "\tbuild_support " << build_support().absFileName() << '\n'
148 << "\tuser_support " << user_support().absFileName() << '\n'
149 << "\tlocale_dir " << locale_dir().absFileName() << '\n'
150 << "\tdocument_dir " << document_dir().absFileName() << '\n'
151 << "\ttemp_dir " << temp_dir().absFileName() << '\n'
152 << "\thome_dir " << get_home_dir().absFileName() << '\n'
157 void Package::set_temp_dir(FileName const & temp_dir) const
159 if (temp_dir.empty())
160 temp_dir_ = system_temp_dir_;
162 temp_dir_ = temp_dir;
165 // The specification of home_dir_ is fixed for a given OS.
166 // A typical example on Windows: "C:/Documents and Settings/USERNAME"
167 // and on a Posix-like machine: "/home/USERNAME".
168 FileName const & Package::get_home_dir()
170 #if defined (USE_WINDOWS_PACKAGING)
171 static FileName const home_dir(getEnv("USERPROFILE"));
173 static FileName const home_dir(getEnv("HOME"));
181 // These next functions contain the stuff that is substituted at
182 // configuration-time.
183 FileName const hardcoded_localedir()
186 // The build system needs to make sure that this is in utf8 encoding.
187 return FileName(LYX_ABS_INSTALLED_LOCALEDIR);
191 FileName const hardcoded_system_support_dir()
194 // The build system needs to make sure that this is in utf8 encoding.
195 return FileName(LYX_ABS_INSTALLED_DATADIR);
199 string const & with_version_suffix()
201 static string const program_suffix = PROGRAM_SUFFIX;
202 static string const with_version_suffix =
203 " --with-version-suffix=" PROGRAM_SUFFIX;
204 return program_suffix.empty() ? program_suffix : with_version_suffix;
210 FileName const & Package::top_srcdir()
213 // The build system needs to make sure that this is in utf8 encoding.
214 static FileName const dir(LYX_ABS_TOP_SRCDIR);
221 bool check_command_line_dir(string const & dir,
223 string const & command_line_switch);
225 FileName const extract_env_var_dir(string const & env_var);
227 bool check_env_var_dir(FileName const & dir,
228 string const & env_var);
230 bool check_env_var_dir(FileName const & dir,
232 string const & env_var);
234 string const relative_locale_dir();
236 string const relative_system_support_dir();
240 * Convert \p name to internal path and strip a trailing slash, since it
241 * comes from user input (commandline or environment).
242 * \p name is encoded in utf8.
244 string const fix_dir_name(string const & name)
246 return rtrim(os::internal_path(name), "/");
251 bool isBuildDir(FileName const & abs_binary, string const & dir_location,
252 FileName & build_support_dir)
254 string search_dir = onlyPath(abs_binary.absFileName()) + dir_location;
256 // Makefile by automake
257 build_support_dir = FileName(addPath(search_dir, "lib"));
258 if (!fileSearch(build_support_dir.absFileName(), "Makefile").empty()) {
261 // cmake file, no Makefile in lib
262 FileName build_boost_dir = FileName(addPath(search_dir, "boost"));
263 if (!fileSearch(build_boost_dir.absFileName(), "cmake_install.cmake").empty()) {
270 bool inBuildDir(FileName const & abs_binary,
271 FileName & build_support_dir, FileName & system_support_dir)
273 string const check_text = "Checking whether LyX is run in place...";
275 // We're looking for "Makefile" in a directory
277 // We're also looking for "chkconfig.ltx" in a directory
279 // If both are found, then we're running LyX in-place.
281 // Note that the name of the lyx binary may be a symbolic link.
282 // If that is the case, then we follow the links too.
283 FileName binary = abs_binary;
285 // Try and find "lyxrc.defaults".
286 if( isBuildDir(binary, "../", build_support_dir) ||
287 isBuildDir(binary, "../../", build_support_dir))
289 // Try and find "chkconfig.ltx".
291 FileName(addPath(Package::top_srcdir().absFileName(), "lib"));
293 if (!fileSearch(system_support_dir.absFileName(), "chkconfig.ltx").empty()) {
294 LYXERR(Debug::INIT, check_text << " yes");
299 // Check whether binary is a symbolic link.
300 // If so, resolve it and repeat the exercise.
301 if (!binary.isSymLink())
305 if (readLink(binary, link)) {
308 // Unable to resolve the link.
313 LYXERR(Debug::INIT, check_text << " no");
314 system_support_dir = FileName();
315 build_support_dir = FileName();
321 bool doesFileExist(FileName & result, string const & search_dir, string const & name)
323 result = fileSearch(search_dir, name);
324 if (!result.empty()) {
331 bool lyxBinaryPath(FileName & lyx_binary, string const & search_dir, string const & ext)
333 lyx_binary = FileName();
335 } else if (doesFileExist(lyx_binary, search_dir, "lyx" + ext)) {
336 } else if (doesFileExist(lyx_binary, search_dir, "LyX" + ext)) {
337 } else if (doesFileExist(lyx_binary, search_dir, "lyx" + string(PROGRAM_SUFFIX) + ext)) {
338 } else if (doesFileExist(lyx_binary, search_dir, "LyX" + string(PROGRAM_SUFFIX) + ext)){
340 return !lyx_binary.empty() ? true : false;
344 FileName findLyxBinary(FileName const & abs_binary)
347 if (!abs_binary.extension().empty()) {
348 ext = "." + abs_binary.extension();
351 string binary_dir = onlyPath(abs_binary.absFileName());
354 if (lyxBinaryPath(lyx_binary, binary_dir, ext))
357 string search_dir = onlyPath(FileName(addPath(binary_dir, "/../")).absFileName());
358 if (lyxBinaryPath(lyx_binary, search_dir, ext))
365 // Specification of document_dir_ may be reset by LyXRC,
366 // but the default is fixed for a given OS.
367 FileName const get_document_dir(FileName const & home_dir)
369 #if defined (USE_WINDOWS_PACKAGING)
370 (void)home_dir; // Silence warning about unused variable.
371 os::GetFolderPath win32_folder_path;
372 return FileName(win32_folder_path(os::GetFolderPath::PERSONAL));
380 // Several sources are probed to ascertain the locale directory.
381 // The only requirement is that the result is indeed a directory.
382 FileName const get_locale_dir(FileName const & system_support_dir)
384 // 1. Use the "LYX_LOCALEDIR" environment variable.
385 FileName const path_env = extract_env_var_dir("LYX_LOCALEDIR");
386 if (!path_env.empty() && check_env_var_dir(path_env, "LYX_LOCALEDIR"))
389 // 2. Search for system_support_dir / <relative locale dir>
390 // The <relative locale dir> is OS-dependent. (On Unix, it will
392 FileName path(addPath(system_support_dir.absFileName(),
393 relative_locale_dir()));
395 if (path.exists() && path.isDirectory())
398 // 3. Fall back to the hard-coded LOCALEDIR.
399 path = hardcoded_localedir();
400 if (path.exists() && path.isDirectory())
407 // Extracts the absolute path from the foo of "-sysdir foo" or "-userdir foo"
408 FileName const abs_path_from_command_line(string const & command_line)
410 if (command_line.empty())
413 string const str_path = fix_dir_name(command_line);
414 return makeAbsPath(str_path);
418 // Does the grunt work for abs_path_from_binary_name()
419 FileName const get_binary_path(string const & exe)
421 #if defined (USE_WINDOWS_PACKAGING)
422 // The executable may have been invoked either with or
423 // without the .exe extension.
424 // Ensure that it is present.
425 string const as_internal_path = os::internal_path(exe);
426 string const exe_path = suffixIs(as_internal_path, ".exe") ?
427 as_internal_path : as_internal_path + ".exe";
429 string const exe_path = os::internal_path(exe);
431 if (FileName::isAbsolute(exe_path))
432 return FileName(exe_path);
434 // Two possibilities present themselves.
435 // 1. The binary is relative to the CWD.
436 FileName const abs_exe_path = makeAbsPath(exe_path);
437 if (abs_exe_path.exists())
440 // 2. exe must be the name of the binary only and it
441 // can be found on the PATH.
442 string const exe_name = onlyFileName(exe_path);
443 if (exe_name != exe_path)
446 vector<string> const path = getEnvPath("PATH");
447 vector<string>::const_iterator it = path.begin();
448 vector<string>::const_iterator const end = path.end();
449 for (; it != end; ++it) {
450 // This will do nothing if *it is already absolute.
451 string const exe_dir = makeAbsPath(*it).absFileName();
453 FileName const exe_path(addName(exe_dir, exe_name));
454 if (exe_path.exists())
458 // Didn't find anything.
463 // Extracts the absolute path to the binary name received as argv[0].
464 FileName const abs_path_from_binary_name(string const & exe)
466 FileName const abs_binary = get_binary_path(exe);
467 if (abs_binary.empty()) {
469 throw ExceptionMessage(ErrorException,
470 _("LyX binary not found"),
471 bformat(_("Unable to determine the path to the LyX binary from the command line %1$s"),
478 // A plethora of directories is searched to ascertain the system
479 // lyxdir which is defined as the first directory to contain
482 get_system_support_dir(FileName const & abs_binary,
483 string const & command_line_system_support_dir)
485 string const chkconfig_ltx = "chkconfig.ltx";
487 // searched_dirs is used for diagnostic purposes only in the case
488 // that "chkconfig.ltx" is not found.
489 list<FileName> searched_dirs;
491 // 1. Use the -sysdir command line parameter.
492 FileName path = abs_path_from_command_line(command_line_system_support_dir);
494 searched_dirs.push_back(path);
495 if (check_command_line_dir(path.absFileName(), chkconfig_ltx, "-sysdir"))
499 // 2. Use the "LYX_DIR_${major}${minor}x" environment variable.
500 path = extract_env_var_dir(LYX_DIR_VER);
502 searched_dirs.push_back(path);
503 if (check_env_var_dir(path, chkconfig_ltx, LYX_DIR_VER))
507 // 3. Search relative to the lyx binary.
508 // We're looking for "chkconfig.ltx" in a directory
509 // OnlyPath(abs_binary) / <relative dir> / PACKAGE /
510 // PACKAGE is hardcoded in config.h. Eg "lyx" or "lyx-1.3.6cvs".
511 // <relative dir> is OS-dependent; on Unix, it will be "../share/".
512 string const relative_lyxdir = relative_system_support_dir();
514 // One subtlety to be aware of. The name of the lyx binary may be
515 // a symbolic link. If that is the case, then we follow the links too.
516 FileName binary = abs_binary;
518 // Try and find "chkconfig.ltx".
519 string const binary_dir = onlyPath(binary.absFileName());
521 FileName const lyxdir(addPath(binary_dir, relative_lyxdir));
522 searched_dirs.push_back(lyxdir);
524 if (!fileSearch(lyxdir.absFileName(), chkconfig_ltx).empty()) {
525 // Success! "chkconfig.ltx" has been found.
529 // Check whether binary is a symbolic link.
530 // If so, resolve it and repeat the exercise.
531 if (!binary.isSymLink())
535 if (readLink(binary, link)) {
538 // Unable to resolve the link.
543 // 4. Repeat the exercise on the directory itself.
544 FileName binary_dir(onlyPath(abs_binary.absFileName()));
546 // This time test whether the directory is a symbolic link
547 // *before* looking for "chkconfig.ltx".
548 // (We've looked relative to the original already.)
549 if (!binary.isSymLink())
553 if (readLink(binary_dir, link)) {
556 // Unable to resolve the link.
560 // Try and find "chkconfig.ltx".
561 FileName const lyxdir(addPath(binary_dir.absFileName(),
563 searched_dirs.push_back(lyxdir);
565 if (!fileSearch(lyxdir.absFileName(), chkconfig_ltx).empty()) {
566 // Success! "chkconfig.ltx" has been found.
571 // 5. In desparation, try the hard-coded system support dir.
572 path = hardcoded_system_support_dir();
573 if (!fileSearch(path.absFileName(), chkconfig_ltx).empty())
576 // Everything has failed :-(
577 // So inform the user and exit.
578 string searched_dirs_str;
579 typedef list<FileName>::const_iterator iterator;
580 iterator const begin = searched_dirs.begin();
581 iterator const end = searched_dirs.end();
582 for (iterator it = begin; it != end; ++it) {
584 searched_dirs_str += "\n\t";
585 searched_dirs_str += it->absFileName();
589 throw ExceptionMessage(ErrorException, _("No system directory"),
590 bformat(_("Unable to determine the system directory "
593 "Use the '-sysdir' command line parameter or "
594 "set the environment variable\n%2$s "
595 "to the LyX system directory containing the "
596 "file `chkconfig.ltx'."),
597 from_utf8(searched_dirs_str), from_ascii(LYX_DIR_VER)));
599 // Keep the compiler happy.
604 // Returns the absolute path to the user lyxdir, together with a flag
605 // indicating whether this directory was specified explicitly (as -userdir
606 // or through an environment variable) or whether it was deduced.
607 bool userSupportDir(FileName const & default_user_support_dir,
608 string const & command_line_user_support_dir, FileName & result)
610 // 1. Use the -userdir command line parameter.
611 result = abs_path_from_command_line(command_line_user_support_dir);
615 // 2. Use the LYX_USERDIR_${major}${minor}x environment variable.
616 result = extract_env_var_dir(LYX_USERDIR_VER);
620 // 3. Use the OS-dependent default_user_support_dir
621 result = default_user_support_dir;
626 // $HOME/.lyx on POSIX but on Win32 it will be something like
627 // "C:/Documents and Settings/USERNAME/Application Data/LyX"
628 FileName const get_default_user_support_dir(FileName const & home_dir)
630 #if defined (USE_WINDOWS_PACKAGING)
631 (void)home_dir; // Silence warning about unused variable.
633 os::GetFolderPath win32_folder_path;
634 return FileName(addPath(win32_folder_path(os::GetFolderPath::APPDATA), PACKAGE));
636 #elif defined (USE_MACOSX_PACKAGING)
637 (void)home_dir; // Silence warning about unused variable.
640 OSErr const error_code =
641 FSFindFolder(kUserDomain, kApplicationSupportFolderType,
642 kDontCreateFolder, &fsref);
646 // FSRefMakePath returns the result in utf8
647 char store[PATH_MAX + 1];
648 OSStatus const status_code =
649 FSRefMakePath(&fsref,
650 reinterpret_cast<UInt8*>(store), PATH_MAX);
651 if (status_code != 0)
654 return FileName(addPath(reinterpret_cast<char const *>(store), PACKAGE));
656 #else // USE_POSIX_PACKAGING
657 return FileName(addPath(home_dir.absFileName(), string(".") + PACKAGE));
662 // Check that directory @c dir contains @c file.
663 // Else emit an error message about an invalid @c command_line_switch.
664 bool check_command_line_dir(string const & dir,
666 string const & command_line_switch)
668 FileName const abs_path = fileSearch(dir, file);
669 if (abs_path.empty()) {
671 throw ExceptionMessage(ErrorException, _("File not found"), bformat(
672 _("Invalid %1$s switch.\nDirectory %2$s does not contain %3$s."),
673 from_utf8(command_line_switch), from_utf8(dir),
677 return !abs_path.empty();
681 // The environment variable @c env_var expands to a (single) file path.
682 FileName const extract_env_var_dir(string const & env_var)
684 string const dir = fix_dir_name(getEnv(env_var));
685 return dir.empty() ? FileName() : makeAbsPath(dir);
689 // Check that directory @c dir contains @c file.
690 // Else emit a warning about an invalid @c env_var.
691 bool check_env_var_dir(FileName const & dir,
693 string const & env_var)
695 FileName const abs_path = fileSearch(dir.absFileName(), file);
696 if (abs_path.empty()) {
698 throw ExceptionMessage(WarningException, _("File not found"), bformat(
699 _("Invalid %1$s environment variable.\n"
700 "Directory %2$s does not contain %3$s."),
701 from_utf8(env_var), from_utf8(dir.absFileName()),
705 return !abs_path.empty();
709 // Check that directory @c dir is indeed a directory.
710 // Else emit a warning about an invalid @c env_var.
711 bool check_env_var_dir(FileName const & dir,
712 string const & env_var)
714 bool const success = dir.exists() && dir.isDirectory();
717 // Put this string on a single line so that the gettext
718 // search mechanism in po/Makefile.in.in will register
719 // Package.cpp.in as a file containing strings that need
722 docstring const fmt =
723 _("Invalid %1$s environment variable.\n%2$s is not a directory.");
725 throw ExceptionMessage(WarningException, _("Directory not found"), bformat(
726 fmt, from_utf8(env_var), from_utf8(dir.absFileName())));
733 // The locale directory relative to the LyX system directory.
734 string const relative_locale_dir()
736 #if defined (USE_WINDOWS_PACKAGING) || defined (USE_MACOSX_PACKAGING)
744 // The system lyxdir is relative to the directory containing the LyX binary.
745 string const relative_system_support_dir()
749 #if defined (USE_WINDOWS_PACKAGING) || defined (USE_MACOSX_PACKAGING)
750 result = "../Resources/";
752 result = addPath("../share/", PACKAGE);
760 } // namespace support