X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fsupport%2Fos_win32.cpp;h=e4f9ebd0fcbd9a15405f082545bd3b2f108a3e7a;hb=da362a6a59a7d2bd2bb922a0daa782202f9623e2;hp=f48759e369b2302c47dc13273d1efd79dd351ed8;hpb=92065820e59aee7787bedb2b6d4c1fdc6d810076;p=lyx.git diff --git a/src/support/os_win32.cpp b/src/support/os_win32.cpp index f48759e369..e4f9ebd0fc 100644 --- a/src/support/os_win32.cpp +++ b/src/support/os_win32.cpp @@ -6,6 +6,7 @@ * \author Ruurd A. Reitsma * \author Claus Hentschel * \author Angus Leeming + * \author Enrico Forestieri * * Full author contact details are available in file CREDITS. * @@ -14,41 +15,31 @@ #include +#include "LyXRC.h" + #include "support/os.h" #include "support/os_win32.h" -#include "support/lstrings.h" + +#include "support/debug.h" +#include "support/environment.h" +#include "support/FileName.h" +#include "support/gettext.h" #include "support/filetools.h" +#include "support/lstrings.h" #include "support/ExceptionMessage.h" +#include "support/qstring_helpers.h" -#include "debug.h" -#include "gettext.h" - -#include +#include "support/lassert.h" #include +#include #include -/* The GetLongPathName macro may be defined on the compiling machine, - * but we must use a bit of trickery if the resulting executable is - * to run on a Win95 machine. - * Fortunately, Microsoft provide the trickery. All we need is the - * NewAPIs.h header file, available for download from Microsoft as - * part of the Platform SDK. - */ -#if defined (HAVE_NEWAPIS_H) -// This should be defined already to keep Boost.Filesystem happy. -# if !defined (WANT_GETFILEATTRIBUTESEX_WRAPPER) -# error Expected WANT_GETFILEATTRIBUTESEX_WRAPPER to be defined! -# endif -# define WANT_GETLONGPATHNAME_WRAPPER 1 -# define COMPILE_NEWAPIS_STUBS -# include -# undef COMPILE_NEWAPIS_STUBS -# undef WANT_GETLONGPATHNAME_WRAPPER -#endif +#include #include #include // _getdrive +#include // GetFinalPathNameByHandle #include // SHGetFolderPath #include #include @@ -63,26 +54,54 @@ # endif #endif -using std::endl; -using std::string; +#if !defined(ASSOCF_INIT_IGNOREUNKNOWN) && !defined(__MINGW32__) +#define ASSOCF_INIT_IGNOREUNKNOWN 0 +#endif + +#if defined(__MINGW32__) +#include +#endif -using lyx::support::runCommand; -using lyx::support::split; +#if defined(_MSC_VER) && (_MSC_VER >= 1900) +#else +extern "C" { +extern void __wgetmainargs(int * argc, wchar_t *** argv, wchar_t *** envp, + int expand_wildcards, int * new_mode); +} +#endif +using namespace std; namespace lyx { + +void lyx_exit(int); + namespace support { namespace os { namespace { +int argc_ = 0; +wchar_t ** argv_ = 0; + bool windows_style_tex_paths_ = true; string cygdrive = "/cygdrive"; -} // namespace anon +BOOL terminate_handler(DWORD event) +{ + if (event == CTRL_CLOSE_EVENT + || event == CTRL_LOGOFF_EVENT + || event == CTRL_SHUTDOWN_EVENT) { + lyx::lyx_exit(1); + return TRUE; + } + return FALSE; +} + +} // namespace -void init(int /* argc */, char * argv[]) +void init(int argc, char ** argv[]) { /* Note from Angus, 17 Jan 2005: * @@ -140,27 +159,98 @@ void init(int /* argc */, char * argv[]) * lyx is invoked as a parameter of hidecmd.exe. */ - // If cygwin is detected, query the cygdrive prefix - HKEY regKey; + + // Remove PYTHONPATH from the environment as it may point to an + // external python installation and cause reconfigure failures. + unsetEnv("PYTHONPATH"); + + +#if defined(_MSC_VER) && (_MSC_VER >= 1900) + // Removing an argument from argv leads to an assertion on Windows + // when compiling with MSVC 2015 in debug mode (see bug #10440). + // To avoid this we make a copy of the array of pointers. + char ** newargv = (char **) malloc((argc + 1) * sizeof(char *)); + if (newargv) { + memcpy(newargv, *argv, (argc + 1) * sizeof(char *)); + *argv = newargv; + } else { + lyxerr << "LyX warning: Cannot make a copy of " + "command line arguments!" + << endl; + } +#endif + + + // Get the wide program arguments array +#if defined(_MSC_VER) && (_MSC_VER >= 1900) + argv_ = CommandLineToArgvW(GetCommandLineW(), &argc_); +#else + wchar_t ** envp = 0; + int newmode = 0; + __wgetmainargs(&argc_, &argv_, &envp, -1, &newmode); +#endif + LATTEST(argc == argc_); + + // If Cygwin is detected, query the cygdrive prefix. + // The cygdrive prefix is needed for translating windows style paths + // to posix style paths in LaTeX files when the Cygwin teTeX is used. + int i; + HKEY hkey; char buf[MAX_PATH]; - DWORD bufSize = sizeof(buf); - LONG retVal; - - retVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE, - "Software\\Cygnus Solutions\\Cygwin\\mounts v2", - 0, KEY_QUERY_VALUE, ®Key); - if (retVal != ERROR_SUCCESS) { - retVal = RegOpenKeyEx(HKEY_CURRENT_USER, - "Software\\Cygnus Solutions\\Cygwin\\mounts v2", - 0, KEY_QUERY_VALUE, ®Key); + DWORD bufsize = sizeof(buf); + LONG retval = ERROR_FILE_NOT_FOUND; + HKEY const mainkey[2] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER }; + char const * const subkey[2] = { + "Software\\Cygwin\\setup", // Cygwin 1.7 + "Software\\Cygnus Solutions\\Cygwin\\mounts v2\\/" // Prev. ver. + }; + char const * const valuename[2] = { + "rootdir", // Cygwin 1.7 or later + "native" // Previous versions + }; + // Check for newer Cygwin versions first, then older ones + for (i = 0; i < 2 && retval != ERROR_SUCCESS; ++i) { + for (int k = 0; k < 2 && retval != ERROR_SUCCESS; ++k) + retval = RegOpenKey(mainkey[k], subkey[i], &hkey); } - if (retVal == ERROR_SUCCESS) { - retVal = RegQueryValueEx(regKey, "cygdrive prefix", NULL, NULL, - (LPBYTE) buf, &bufSize); - RegCloseKey(regKey); - if ((retVal == ERROR_SUCCESS) && (bufSize <= MAX_PATH)) - cygdrive = buf; + if (retval == ERROR_SUCCESS) { + // Query the Cygwin root directory + retval = RegQueryValueEx(hkey, valuename[i - 1], + NULL, NULL, (LPBYTE) buf, &bufsize); + RegCloseKey(hkey); + string const mount = string(buf) + "\\bin\\mount.exe"; + if (retval == ERROR_SUCCESS && FileName(mount).exists()) { + cmd_ret const p = + runCommand(mount + " --show-cygdrive-prefix"); + // The output of the mount command is as follows: + // Prefix Type Flags + // /cygdrive system binmode + // So, we use the inner split to pass the second line + // to the outer split which sets cygdrive with its + // contents until the first blank, discarding the + // unneeded return value. + if (p.first != -1 && prefixIs(p.second, "Prefix")) + split(split(p.second, '\n'), cygdrive, ' '); + } } + + // Catch shutdown events. + SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminate_handler, TRUE); +} + + +string utf8_argv(int i) +{ + LASSERT(i < argc_, return ""); + return fromqstr(QString::fromWCharArray(argv_[i])); +} + + +void remove_internal_args(int i, int num) +{ + argc_ -= num; + for (int j = i; j < argc_; ++j) + argv_[j] = argv_[j + num]; } @@ -172,11 +262,17 @@ string current_root() } +bool isFilesystemCaseSensitive() +{ + return false; +} + + docstring::size_type common_path(docstring const & p1, docstring const & p2) { - docstring::size_type i = 0; - docstring::size_type const p1_len = p1.length(); - docstring::size_type const p2_len = p2.length(); + size_t i = 0; + size_t const p1_len = p1.length(); + size_t const p2_len = p2.length(); while (i < p1_len && i < p2_len && uppercase(p1[i]) == uppercase(p2[i])) ++i; if ((i < p1_len && i < p2_len) @@ -192,45 +288,102 @@ docstring::size_type common_path(docstring const & p1, docstring const & p2) } +bool path_prefix_is(string const & path, string const & pre) +{ + return path_prefix_is(const_cast(path), pre, CASE_UNCHANGED); +} + + +bool path_prefix_is(string & path, string const & pre, path_case how) +{ + docstring const p1 = from_utf8(path); + docstring const p2 = from_utf8(pre); + docstring::size_type const p1_len = p1.length(); + docstring::size_type const p2_len = p2.length(); + docstring::size_type common_len = common_path(p1, p2); + + if (p2[p2_len - 1] == '/' && p1_len != p2_len) + ++common_len; + + if (common_len != p2_len) + return false; + + if (how == CASE_ADJUSTED && !prefixIs(path, pre)) { + if (p1_len < common_len) + path = to_utf8(p2.substr(0, p1_len)); + else + path = to_utf8(p2 + p1.substr(common_len, + p1_len - common_len)); + } + + return true; +} + + string external_path(string const & p) { string const dos_path = subst(p, "/", "\\"); - LYXERR(Debug::LATEX) - << " [" - << p << "]->>[" - << dos_path << ']' << endl; + LYXERR(Debug::LATEX, " [" + << p << "]->>[" << dos_path << ']'); return dos_path; } -namespace { - -string const get_long_path(string const & short_path) +static QString const get_long_path(QString const & short_path) { - // GetLongPathName needs the path in file system encoding. - // We can use to_local8bit, since file system encoding and the - // local 8 bit encoding are identical on windows. - std::vector long_path(MAX_PATH); - DWORD result = GetLongPathName(to_local8bit(from_utf8(short_path)).c_str(), + // GetLongPathNameW needs the path in utf16 encoding. + vector long_path(MAX_PATH); + DWORD result = GetLongPathNameW((wchar_t *) short_path.utf16(), &long_path[0], long_path.size()); if (result > long_path.size()) { long_path.resize(result); - result = GetLongPathName(short_path.c_str(), + result = GetLongPathNameW((wchar_t *) short_path.utf16(), &long_path[0], long_path.size()); - BOOST_ASSERT(result <= long_path.size()); + LATTEST(result <= long_path.size()); } - return (result == 0) ? short_path : to_utf8(from_filesystem8bit(&long_path[0])); + return (result == 0) ? short_path : QString::fromWCharArray(&long_path[0]); } -} // namespace anon + +static QString const get_short_path(QString const & long_path, file_access how) +{ + // CreateFileW and GetShortPathNameW need the path in utf16 encoding. + if (how == CREATE) { + HANDLE h = CreateFileW((wchar_t *) long_path.utf16(), + GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE + && GetLastError() != ERROR_FILE_EXISTS) + return long_path; + CloseHandle(h); + } + vector short_path(MAX_PATH); + DWORD result = GetShortPathNameW((wchar_t *) long_path.utf16(), + &short_path[0], short_path.size()); + + if (result > short_path.size()) { + short_path.resize(result); + result = GetShortPathNameW((wchar_t *) long_path.utf16(), + &short_path[0], short_path.size()); + LATTEST(result <= short_path.size()); + } + + return (result == 0) ? long_path : QString::fromWCharArray(&short_path[0]); +} string internal_path(string const & p) { - return subst(get_long_path(p), "\\", "/"); + return subst(fromqstr(get_long_path(toqstr(p))), "\\", "/"); +} + + +string safe_internal_path(string const & p, file_access how) +{ + return subst(fromqstr(get_short_path(toqstr(p), how)), "\\", "/"); } @@ -252,35 +405,47 @@ string latex_path(string const & p) // on windows_style_tex_paths_), but we use always forward slashes, // since it gets written into a .tex file. - if (!windows_style_tex_paths_ && is_absolute_path(p)) { + if (!windows_style_tex_paths_ && FileName::isAbsolute(p)) { string const drive = p.substr(0, 2); string const cygprefix = cygdrive + "/" + drive.substr(0, 1); string const cygpath = subst(subst(p, '\\', '/'), drive, cygprefix); - LYXERR(Debug::LATEX) - << " [" - << p << "]->>[" - << cygpath << ']' << endl; + LYXERR(Debug::LATEX, " [" + << p << "]->>[" << cygpath << ']'); return cygpath; } return subst(p, '\\', '/'); } -// (Claus H.) On Win32 both Unix and Win32/DOS pathnames are used. -// Therefore an absolute path could be either a pathname starting -// with a slash (Unix) or a pathname starting with a drive letter -// followed by a colon. Because a colon is not valid in pathes in Unix -// and at another location in Win32 testing just for the existance -// of the colon in the 2nd position seems to be enough! -bool is_absolute_path(string const & p) +string latex_path_list(string const & p) { if (p.empty()) - return false; - - bool isDosPath = (p.length() > 1 && p[1] == ':'); - bool isUnixPath = (p[0] == '/'); + return p; - return isDosPath || isUnixPath; + // We may need a posix style path or a windows style path (depending + // on windows_style_tex_paths_), but we use always forward slashes, + // since this is standard for all tex engines. + + if (!windows_style_tex_paths_) { + string pathlist; + for (size_t i = 0, k = 0; i != string::npos; k = i) { + i = p.find(';', i); + string path = subst(p.substr(k, i - k), '\\', '/'); + if (FileName::isAbsolute(path)) { + string const drive = path.substr(0, 2); + string const cygprefix = cygdrive + "/" + + drive.substr(0, 1); + path = subst(path, drive, cygprefix); + } + pathlist += path; + if (i != string::npos) { + pathlist += ':'; + ++i; + } + } + return pathlist; + } + return subst(p, '\\', '/'); } @@ -305,8 +470,11 @@ shell_type shell() } -char path_separator() +char path_separator(path_type type) { + if (type == TEXENGINE) + return windows_style_tex_paths_ ? ';' : ':'; + return ';'; } @@ -358,7 +526,7 @@ string const GetFolderPath::operator()(folder_id _id) const id = CSIDL_APPDATA; break; default: - BOOST_ASSERT(false); + LASSERT(false, return string()); } HRESULT const result = (folder_path_func_)(0, id, 0, SHGFP_TYPE_CURRENT, @@ -376,21 +544,118 @@ bool canAutoOpenFile(string const & ext, auto_open_mode const mode) DWORD bufSize = MAX_PATH + 100; TCHAR buf[MAX_PATH + 100]; - // reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc - // /platform/shell/reference/shlwapi/registry/assocquerystring.asp + // reference: http://msdn.microsoft.com/en-us/library/bb773471.aspx char const * action = (mode == VIEW) ? "open" : "edit"; - return S_OK == AssocQueryString(0, ASSOCSTR_EXECUTABLE, - full_ext.c_str(), action, buf, &bufSize); + return S_OK == AssocQueryString(ASSOCF_INIT_IGNOREUNKNOWN, + ASSOCSTR_EXECUTABLE, full_ext.c_str(), action, buf, &bufSize); } -bool autoOpenFile(string const & filename, auto_open_mode const mode) +bool autoOpenFile(string const & filename, auto_open_mode const mode, + string const & path) { - // reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc - // /platform/shell/reference/functions/shellexecute.asp - char const * action = (mode == VIEW) ? "open" : "edit"; - return reinterpret_cast(ShellExecute(NULL, action, - to_local8bit(from_utf8(filename)).c_str(), NULL, NULL, 1)) > 32; + string const texinputs = os::latex_path_list( + replaceCurdirPath(path, lyxrc.texinputs_prefix)); + string const otherinputs = os::latex_path_list(path); + string const sep = windows_style_tex_paths_ ? ";" : ":"; + string const oldtexinputs = getEnv("TEXINPUTS"); + string const newtexinputs = "." + sep + texinputs + sep + oldtexinputs; + string const oldbibinputs = getEnv("BIBINPUTS"); + string const newbibinputs = "." + sep + otherinputs + sep + oldbibinputs; + string const oldbstinputs = getEnv("BSTINPUTS"); + string const newbstinputs = "." + sep + otherinputs + sep + oldbstinputs; + string const oldtexfonts = getEnv("TEXFONTS"); + string const newtexfonts = "." + sep + otherinputs + sep + oldtexfonts; + if (!path.empty() && !lyxrc.texinputs_prefix.empty()) { + setEnv("TEXINPUTS", newtexinputs); + setEnv("BIBINPUTS", newbibinputs); + setEnv("BSTINPUTS", newbstinputs); + setEnv("TEXFONTS", newtexfonts); + } + + QString const wname = toqstr(filename); + + // reference: http://msdn.microsoft.com/en-us/library/bb762153.aspx + wchar_t const * action = (mode == VIEW) ? L"open" : L"edit"; + bool success = reinterpret_cast(ShellExecuteW(NULL, action, + reinterpret_cast(wname.utf16()), + NULL, NULL, 1)) > 32; + + if (!path.empty() && !lyxrc.texinputs_prefix.empty()) { + setEnv("TEXINPUTS", oldtexinputs); + setEnv("BIBINPUTS", oldbibinputs); + setEnv("BSTINPUTS", oldbstinputs); + setEnv("TEXFONTS", oldtexfonts); + } + return success; +} + + +string real_path(string const & path) +{ + // See http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx + QString const qpath = get_long_path(toqstr(path)); + HANDLE hpath = CreateFileW((wchar_t *) qpath.utf16(), GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); + + if (hpath == INVALID_HANDLE_VALUE) { + // The file cannot be accessed. + return path; + } + + TCHAR realpath[MAX_PATH + 1]; + + DWORD size = GetFinalPathNameByHandle(hpath, realpath, MAX_PATH, VOLUME_NAME_NT); + if (size > MAX_PATH) { + CloseHandle(hpath); + return path; + } + + // Translate device name to UNC prefix or drive letters. + TCHAR tmpbuf[MAX_PATH] = TEXT("\\Device\\Mup\\"); + UINT namelen = _tcslen(tmpbuf); + if (_tcsnicmp(realpath, tmpbuf, namelen) == 0) { + // UNC path + _snprintf(tmpbuf, MAX_PATH, "\\\\%s", realpath + namelen); + strncpy(realpath, tmpbuf, MAX_PATH); + realpath[MAX_PATH] = '\0'; + } else if (GetLogicalDriveStrings(MAX_PATH - 1, tmpbuf)) { + // Check whether device name corresponds to some local drive. + TCHAR name[MAX_PATH]; + TCHAR drive[3] = TEXT(" :"); + bool found = false; + TCHAR * p = tmpbuf; + do { + // Copy the drive letter to the template string + drive[0] = *p; + // Look up each device name + if (QueryDosDevice(drive, name, MAX_PATH)) { + namelen = _tcslen(name); + if (namelen < MAX_PATH) { + found = _tcsnicmp(realpath, name, namelen) == 0; + if (found) { + // Repl. device spec with drive + TCHAR tempfile[MAX_PATH]; + _snprintf(tempfile, + MAX_PATH, + "%s%s", + drive, + realpath + namelen); + strncpy(realpath, + tempfile, + MAX_PATH); + realpath[MAX_PATH] = '\0'; + } + } + } + // Advance p to the next NULL character. + while (*p++) ; + } while (!found && *p); + } + CloseHandle(hpath); + string const retpath = subst(string(realpath), '\\', '/'); + return FileName::fromFilesystemEncoding(retpath).absFileName(); } } // namespace os