// -*- C++ -*- /** * \file package.C * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Angus Leeming * * Full author contact details are available in file CREDITS. * * Warning! This file is autogenerated from package.C.in. * All changes to this file will be lost. */ #include #include "support/package.h" #include "debug.h" #include "gettext.h" #include "support/environment.h" #include "support/filetools.h" #include "support/lstrings.h" #include "support/ExceptionMessage.h" #include "support/os.h" #if defined (USE_WINDOWS_PACKAGING) # include "support/os_win32.h" #endif #include #include #include #include #if !defined (USE_WINDOWS_PACKAGING) && \ !defined (USE_MACOSX_PACKAGING) && \ !defined (USE_POSIX_PACKAGING) #error USE_FOO_PACKAGING must be defined for FOO = WINDOWS, MACOSX or POSIX. #endif #if defined (USE_MACOSX_PACKAGING) # include // FSFindFolder, FSRefMakePath #endif using std::string; namespace fs = boost::filesystem; namespace lyx { namespace support { namespace { Package package_; bool initialised_ = false; } // namespace anon void init_package(string const & command_line_arg0, string const & command_line_system_support_dir, string const & command_line_user_support_dir, exe_build_dir_to_top_build_dir top_build_dir_location) { // Can do so only once. if (initialised_) return; package_ = Package(command_line_arg0, command_line_system_support_dir, command_line_user_support_dir, top_build_dir_location); initialised_ = true; } Package const & package() { // Commented out because package().locale_dir() can be called // from the message translation code in messages.C before // init_package() is called. Lars is on the case... // BOOST_ASSERT(initialised_); return package_; } namespace { string const abs_path_from_binary_name(string const & exe); std::pair const get_build_dirs(string const & abs_binary, exe_build_dir_to_top_build_dir top_build_dir_location); string const get_document_dir(string const & home_dir); string const get_home_dir(); string const get_locale_dir(string const & system_support_dir); string const get_system_support_dir(string const & abs_binary, string const & command_line_system_support_dir); string const get_temp_dir(); string const get_default_user_support_dir(string const & home_dir); std::pair const get_user_support_dir(string const & default_user_support_dir, string const & command_line_user_support_dir); string const & with_version_suffix(); } // namespace anon Package::Package(string const & command_line_arg0, string const & command_line_system_support_dir, string const & command_line_user_support_dir, exe_build_dir_to_top_build_dir top_build_dir_location) : explicit_user_support_dir_(false) { home_dir_ = get_home_dir(); system_temp_dir_ = get_temp_dir(); temp_dir_ = system_temp_dir_; document_dir_ = get_document_dir(home_dir_); string const abs_binary = abs_path_from_binary_name(command_line_arg0); binary_dir_ = onlyPath(abs_binary); // Is LyX being run in-place from the build tree? boost::tie(build_support_dir_, system_support_dir_) = get_build_dirs(abs_binary, top_build_dir_location); if (build_support_dir_.empty()) system_support_dir_ = get_system_support_dir(abs_binary, command_line_system_support_dir); locale_dir_ = get_locale_dir(system_support_dir_); string const default_user_support_dir = get_default_user_support_dir(home_dir_); boost::tie(user_support_dir_, explicit_user_support_dir_) = get_user_support_dir(default_user_support_dir, command_line_user_support_dir); string const configure_script = addName(system_support(), "configure.py"); configure_command_ = os::python() + ' ' + quoteName(configure_script) + with_version_suffix(); lyxerr[Debug::INIT] << "\n" << "\tbinary_dir " << binary_dir() << '\n' << "\tsystem_support " << system_support() << '\n' << "\tbuild_support " << build_support() << '\n' << "\tuser_support " << user_support() << '\n' << "\tlocale_dir " << locale_dir() << '\n' << "\tdocument_dir " << document_dir() << '\n' << "\ttemp_dir " << temp_dir() << '\n' << "\thome_dir " << home_dir() << '\n' << "\n" << std::endl; } namespace { // These next functions contain the stuff that is substituted at // configuration-time. string const hardcoded_localedir() { return string("@LOCALEDIR@"); } string const hardcoded_system_support_dir() { return string("@LYX_DIR@"); } string const & with_version_suffix() { static string const program_suffix("@PROGRAM_SUFFIX@"); static string const with_version_suffix(" --with-version-suffix=@PROGRAM_SUFFIX@"); return program_suffix.empty() ? program_suffix : with_version_suffix; } } // namespace anon string const & Package::top_srcdir() { static string const dir("@TOP_SRCDIR@"); return dir; } namespace { bool check_command_line_dir(string const & dir, string const & file, string const & command_line_switch); string const extract_env_var_dir(string const & env_var); bool check_env_var_dir(string const & dir, string const & env_var); bool check_env_var_dir(string const & dir, string const & file, string const & env_var); string const relative_locale_dir(); string const relative_system_support_dir(); std::string const get_build_support_dir(std::string const & binary_dir, exe_build_dir_to_top_build_dir top_build_dir_location) { string indirection; switch (top_build_dir_location) { case top_build_dir_is_one_level_up: indirection = "../lib"; break; case top_build_dir_is_two_levels_up: indirection = "../../lib"; break; } return normalizePath(addPath(binary_dir, indirection)); } std::pair const get_build_dirs(string const & abs_binary, exe_build_dir_to_top_build_dir top_build_dir_location) { string const check_text = "Checking whether LyX is run in place..."; // We're looking for "Makefile" in a directory // binary_dir/../lib // We're also looking for "chkconfig.ltx" in a directory // top_srcdir()/lib // If both are found, then we're running LyX in-place. // Note that the name of the lyx binary may be a symbolic link. // If that is the case, then we follow the links too. string binary = abs_binary; while (true) { // Try and find "lyxrc.defaults". string const binary_dir = onlyPath(binary); string const build_support_dir = get_build_support_dir(binary_dir, top_build_dir_location); if (!fileSearch(build_support_dir, "Makefile").empty()) { // Try and find "chkconfig.ltx". string const system_support_dir = addPath(Package::top_srcdir(), "lib"); if (!fileSearch(system_support_dir, "chkconfig.ltx").empty()) { lyxerr[Debug::INIT] << check_text << " yes" << std::endl; return std::make_pair(build_support_dir, system_support_dir); } } // Check whether binary is a symbolic link. // If so, resolve it and repeat the exercise. if (!fs::symbolic_link_exists(FileName(binary).toFilesystemEncoding())) break; string link; if (readLink(binary, link, true)) { binary = link; } else { // Unable to resolve the link. break; } } lyxerr[Debug::INIT] << check_text << " no" << std::endl; return std::make_pair(string(), string()); } // Specification of document_dir_ may be reset by LyXRC, // but the default is fixed for a given OS. string const get_document_dir(string const & home_dir) { #if defined (USE_WINDOWS_PACKAGING) (void)home_dir; // Silence warning about unused variable. os::GetFolderPath win32_folder_path; return win32_folder_path(os::GetFolderPath::PERSONAL); #else // Posix-like. return home_dir; #endif } // The specification of home_dir_ is fixed for a given OS. // A typical example on Windows: "C:/Documents and Settings/USERNAME" // and on a Posix-like machine: "/home/USERNAME". string const get_home_dir() { #if defined (USE_WINDOWS_PACKAGING) string const home_dir = getEnv("USERPROFILE"); #else // Posix-like. string const home_dir = getEnv("HOME"); #endif return os::internal_path(home_dir); } // Several sources are probed to ascertain the locale directory. // The only requirement is that the result is indeed a directory. string const get_locale_dir(string const & system_support_dir) { // 1. Use the "LYX_LOCALEDIR" environment variable. string const path_env = extract_env_var_dir("LYX_LOCALEDIR"); if (!path_env.empty() && check_env_var_dir(path_env, "LYX_LOCALEDIR")) return path_env; // 2. Search for system_support_dir / // The is OS-dependent. (On Unix, it will // be "../locale/".) FileName path(normalizePath(addPath(system_support_dir, relative_locale_dir()))); if (fs::exists(path.toFilesystemEncoding()) && fs::is_directory(path.toFilesystemEncoding())) return path.absFilename(); // 3. Fall back to the hard-coded LOCALEDIR. path = FileName(hardcoded_localedir()); if (fs::exists(path.toFilesystemEncoding()) && fs::is_directory(path.toFilesystemEncoding())) return path.absFilename(); return string(); } // Specification of temp_dir_ may be reset by LyXRC, // but the default is fixed for a given OS. string const get_temp_dir() { #if defined (USE_WINDOWS_PACKAGING) // Typical example: C:/TEMP/. char path[MAX_PATH]; GetTempPath(MAX_PATH, path); return os::internal_path(path); #else // Posix-like. return "/tmp"; #endif } // Extracts the absolute path from the foo of "-sysdir foo" or "-userdir foo" string const abs_path_from_command_line(string const & command_line) { if (command_line.empty()) return string(); string const path = os::internal_path(command_line); return os::is_absolute_path(path) ? path : makeAbsPath(path).absFilename(); } // Does the grunt work for abs_path_from_binary_name() string const get_binary_path(string const & exe) { #if defined (USE_WINDOWS_PACKAGING) // The executable may have been invoked either with or // without the .exe extension. // Ensure that it is present. string const as_internal_path = os::internal_path(exe); string const exe_path = suffixIs(as_internal_path, ".exe") ? as_internal_path : as_internal_path + ".exe"; #else string const exe_path = os::internal_path(exe); #endif if (os::is_absolute_path(exe_path)) return exe_path; // Two possibilities present themselves. // 1. The binary is relative to the CWD. string const abs_exe_path = makeAbsPath(exe_path).absFilename(); if (fs::exists(FileName(abs_exe_path).toFilesystemEncoding())) return abs_exe_path; // 2. exe must be the name of the binary only and it // can be found on the PATH. string const exe_name = onlyFilename(exe_path); if (exe_name != exe_path) return string(); std::vector const path = getEnvPath("PATH"); std::vector::const_iterator it = path.begin(); std::vector::const_iterator const end = path.end(); for (; it != end; ++it) { // This will do nothing if *it is already absolute. string const exe_dir = makeAbsPath(*it).absFilename(); string const exe_path = addName(exe_dir, exe_name); if (fs::exists(FileName(exe_path).toFilesystemEncoding())) return exe_path; } // Didn't find anything. return string(); } // Extracts the absolute path to the binary name received as argv[0]. string const abs_path_from_binary_name(string const & exe) { string const abs_binary = get_binary_path(exe); if (abs_binary.empty()) { // FIXME UNICODE throw ExceptionMessage(ErrorException, _("LyX binary not found"), bformat(_("Unable to determine the path to the LyX binary from the command line %1$s"), lyx::from_utf8(exe))); } return abs_binary; } // A plethora of directories is searched to ascertain the system // lyxdir which is defined as the first directory to contain // "chkconfig.ltx". string const get_system_support_dir(string const & abs_binary, string const & command_line_system_support_dir) { string const chkconfig_ltx = "chkconfig.ltx"; // searched_dirs is used for diagnostic purposes only in the case // that "chkconfig.ltx" is not found. std::list searched_dirs; // 1. Use the -sysdir command line parameter. string path = abs_path_from_command_line(command_line_system_support_dir); if (!path.empty()) { searched_dirs.push_back(path); if (check_command_line_dir(path, chkconfig_ltx, "-sysdir")) return path; } // 2. Use the "LYX_DIR_15x" environment variable. path = extract_env_var_dir("LYX_DIR_15x"); if (!path.empty()) { searched_dirs.push_back(path); if (check_env_var_dir(path, chkconfig_ltx, "LYX_DIR_15x")) return path; } // 3. Search relative to the lyx binary. // We're looking for "chkconfig.ltx" in a directory // OnlyPath(abs_binary) / / PACKAGE / // PACKAGE is hardcoded in config.h. Eg "lyx" or "lyx-1.3.6cvs". // is OS-dependent; on Unix, it will be "../share/". string const relative_lyxdir = relative_system_support_dir(); // One subtlety to be aware of. The name of the lyx binary may be // a symbolic link. If that is the case, then we follow the links too. string binary = abs_binary; while (true) { // Try and find "chkconfig.ltx". string const binary_dir = onlyPath(binary); string const lyxdir = normalizePath(addPath(binary_dir, relative_lyxdir)); searched_dirs.push_back(lyxdir); if (!fileSearch(lyxdir, chkconfig_ltx).empty()) { // Success! "chkconfig.ltx" has been found. return lyxdir; } // Check whether binary is a symbolic link. // If so, resolve it and repeat the exercise. if (!fs::symbolic_link_exists(FileName(binary).toFilesystemEncoding())) break; string link; if (readLink(binary, link, true)) { binary = link; } else { // Unable to resolve the link. break; } } // 4. Repeat the exercise on the directory itself. string binary_dir = onlyPath(abs_binary); while (true) { // This time test whether the directory is a symbolic link // *before* looking for "chkconfig.ltx". // (We've looked relative to the original already.) if (!fs::symbolic_link_exists(FileName(binary).toFilesystemEncoding())) break; string link; if (readLink(binary_dir, link, true)) { binary_dir = link; } else { // Unable to resolve the link. break; } // Try and find "chkconfig.ltx". string const lyxdir = normalizePath(addPath(binary_dir, relative_lyxdir)); searched_dirs.push_back(lyxdir); if (!fileSearch(lyxdir, chkconfig_ltx).empty()) { // Success! "chkconfig.ltx" has been found. return lyxdir; } } // 5. In desparation, try the hard-coded system support dir. path = hardcoded_system_support_dir(); if (!fileSearch(path, chkconfig_ltx).empty()) return path; // Everything has failed :-( // So inform the user and exit. string searched_dirs_str; typedef std::list::const_iterator iterator; iterator const begin = searched_dirs.begin(); iterator const end = searched_dirs.end(); for (iterator it = begin; it != end; ++it) { if (it != begin) searched_dirs_str += "\n\t"; searched_dirs_str += *it; } // FIXME UNICODE throw ExceptionMessage(ErrorException, _("No system directory"), bformat(_("Unable to determine the system directory " "having searched\n" "\t%1$s\n" "Use the '-sysdir' command line parameter or " "set the environment variable LYX_DIR_15x to " "the LyX system directory containing the file " "`chkconfig.ltx'."), lyx::from_utf8(searched_dirs_str))); // Keep the compiler happy. return string(); } // Returns the absolute path to the user lyxdir, together with a flag // indicating whether this directory was specified explicitly (as -userdir // or through an environment variable) or whether it was deduced. std::pair const get_user_support_dir(string const & default_user_support_dir, string const & command_line_user_support_dir) { bool explicit_userdir = true; // 1. Use the -userdir command line parameter. string path = abs_path_from_command_line(command_line_user_support_dir); if (!path.empty()) return std::make_pair(path, explicit_userdir); // 2. Use the LYX_USERDIR_15x environment variable. path = extract_env_var_dir("LYX_USERDIR_15x"); if (!path.empty()) return std::make_pair(path, explicit_userdir); // 3. Use the OS-dependent default_user_support_dir explicit_userdir = false; return std::make_pair(default_user_support_dir, explicit_userdir); } // $HOME/.lyx on POSIX but on Win32 it will be something like // "C:/Documents and Settings/USERNAME/Application Data/LyX" string const get_default_user_support_dir(string const & home_dir) { #if defined (USE_WINDOWS_PACKAGING) (void)home_dir; // Silence warning about unused variable. os::GetFolderPath win32_folder_path; return addPath(win32_folder_path(os::GetFolderPath::APPDATA), PACKAGE); #elif defined (USE_MACOSX_PACKAGING) (void)home_dir; // Silence warning about unused variable. FSRef fsref; OSErr const error_code = FSFindFolder(kUserDomain, kApplicationSupportFolderType, kDontCreateFolder, &fsref); if (error_code != 0) return string(); char store[PATH_MAX + 1]; OSStatus const status_code = FSRefMakePath(&fsref, reinterpret_cast(store), PATH_MAX); if (status_code != 0) return string(); return addPath(reinterpret_cast(store), PACKAGE); #else // USE_POSIX_PACKAGING return addPath(home_dir, string(".") + PACKAGE); #endif } // Check that directory @c dir contains @c file. // Else emit a warning about an invalid @c command_line_switch. bool check_command_line_dir(string const & dir, string const & file, string const & command_line_switch) { FileName const abs_path = fileSearch(dir, file); if (abs_path.empty()) { // FIXME UNICODE throw ExceptionMessage(WarningException, _("File not found"), bformat( _("Invalid %1$s switch.\nDirectory %2$s does not contain %3$s."), lyx::from_utf8(command_line_switch), lyx::from_utf8(dir), lyx::from_utf8(file))); } return !abs_path.empty(); } // The environment variable @c env_var expands to a (single) file path. string const extract_env_var_dir(string const & env_var) { string const dir = os::internal_path(getEnv(env_var)); return dir.empty() ? dir : makeAbsPath(dir).absFilename(); } // Check that directory @c dir contains @c file. // Else emit a warning about an invalid @c env_var. bool check_env_var_dir(string const & dir, string const & file, string const & env_var) { FileName const abs_path = fileSearch(dir, file); if (abs_path.empty()) { // FIXME UNICODE throw ExceptionMessage(WarningException, _("File not found"), bformat( _("Invalid %1$s environment variable.\n" "Directory %2$s does not contain %3$s."), lyx::from_utf8(env_var), lyx::from_utf8(dir), lyx::from_utf8(file))); } return !abs_path.empty(); } // Check that directory @c dir is indeed a directory. // Else emit a warning about an invalid @c env_var. bool check_env_var_dir(string const & dir, string const & env_var) { string const encoded(FileName(dir).toFilesystemEncoding()); bool const success = (fs::exists(encoded) && fs::is_directory(encoded)); if (!success) { // Put this string on a single line so that the gettext // search mechanism in po/Makefile.in.in will register // package.C.in as a file containing strings that need // translation. // FIXME UNICODE docstring const fmt = _("Invalid %1$s environment variable.\n%2$s is not a directory."); throw ExceptionMessage(WarningException, _("Directory not found"), bformat( fmt, lyx::from_utf8(env_var), lyx::from_utf8(dir))); } return success; } // The locale directory relative to the LyX system directory. string const relative_locale_dir() { #if defined (USE_WINDOWS_PACKAGING) || defined (USE_MACOSX_PACKAGING) return "locale/"; #else return "../locale/"; #endif } // The system lyxdir is relative to the directory containing the LyX binary. string const relative_system_support_dir() { string result; #if defined (USE_WINDOWS_PACKAGING) || defined (USE_MACOSX_PACKAGING) result = "../Resources/"; #else // Posix-like. result = addPath("../share/", PACKAGE); #endif return result; } } // namespace anon } // namespace support } // namespace lyx