diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c index b6350ea0603..9bec7091aea 100644 --- a/dlls/shell32/shellpath.c +++ b/dlls/shell32/shellpath.c @@ -3949,9 +3949,274 @@ end: } /************************************************************************* + * _SHAppendToUnixPath [Internal] + * + * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the + * corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath' + * and replaces backslashes with slashes. + * + * PARAMS + * szBasePath [IO] The unix base path, which will be appended to (CP_UNXICP). + * pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW). + * + * RETURNS + * Success: TRUE, + * Failure: FALSE + */ +static inline BOOL _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) { + WCHAR wszSubPath[MAX_PATH]; + int cLen = strlen(szBasePath); + char *pBackslash; + + if (IS_INTRESOURCE(pwszSubPath)) { + if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) { + /* Fall back to hard coded defaults. */ + switch (LOWORD(pwszSubPath)) { + case IDS_PERSONAL: + lstrcpyW(wszSubPath, DocumentsW); + break; + case IDS_MYMUSIC: + lstrcpyW(wszSubPath, My_MusicW); + break; + case IDS_MYPICTURES: + lstrcpyW(wszSubPath, My_PicturesW); + break; + case IDS_MYVIDEOS: + lstrcpyW(wszSubPath, My_VideosW); + break; + case IDS_DOWNLOADS: + lstrcpyW(wszSubPath, DownloadsW); + break; + case IDS_TEMPLATES: + lstrcpyW(wszSubPath, TemplatesW); + break; + default: + ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath)); + return FALSE; + } + } + } else { + lstrcpyW(wszSubPath, pwszSubPath); + } + + if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/'; + + if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath + cLen, + FILENAME_MAX - cLen, NULL, NULL)) + { + return FALSE; + } + + pBackslash = szBasePath + cLen; + while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/'; + + return TRUE; +} + +/****************************************************************************** + * _SHCreateSymbolicLinks [Internal] + * + * Sets up symbol links for various shell folders to point into the user's home + * directory. We do an educated guess about what the user would probably want: + * - If there is a 'My Documents' directory in $HOME, the user probably wants + * wine's 'My Documents' to point there. Furthermore, we infer that the user + * is a Windows lover and has no problem with wine creating subfolders for + * 'My Pictures', 'My Music', 'My Videos' etc. under '$HOME/My Documents', if + * those do not already exist. We put appropriate symbolic links in place for + * those, too. + * - If there is no 'My Documents' directory in $HOME, we let 'My Documents' + * point directly to $HOME. We assume the user to be a unix hacker who does not + * want wine to create anything anywhere besides the .wine directory. So, if + * there already is a 'My Music' directory in $HOME, we symlink the 'My Music' + * shell folder to it. But if not, then we check XDG_MUSIC_DIR - "well known" + * directory, and try to link to that. If that fails, then we symlink to + * $HOME directly. The same holds for 'My Pictures', 'My Videos' etc. + * - The Desktop shell folder is symlinked to XDG_DESKTOP_DIR. If that does not + * exist, then we try '$HOME/Desktop'. If that does not exist, then we leave + * it alone. + * ('My Music',... above in fact means LoadString(IDS_MYMUSIC)) + */ +static void _SHCreateSymbolicLinks(void) +{ + static const UINT aidsMyStuff[] = { + IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC, IDS_DOWNLOADS, IDS_TEMPLATES + }; + static const WCHAR * const MyOSXStuffW[] = { + PicturesW, MoviesW, MusicW, DownloadsW, TemplatesW + }; + static const int acsidlMyStuff[] = { + CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC, CSIDL_DOWNLOADS, CSIDL_TEMPLATES + }; + static const char * const xdg_dirs[] = { + "PICTURES", "VIDEOS", "MUSIC", "DOWNLOAD", "TEMPLATES", "DOCUMENTS", "DESKTOP" + }; + static const unsigned int num = ARRAY_SIZE(xdg_dirs); + WCHAR wszTempPath[MAX_PATH]; + char szPersonalTarget[FILENAME_MAX], *pszPersonal; + char szMyStuffTarget[FILENAME_MAX], *pszMyStuff; + char szDesktopTarget[FILENAME_MAX], *pszDesktop; + struct stat statFolder; + const char *pszHome; + HRESULT hr; + char ** xdg_results; + char * xdg_desktop_dir; + UINT i; + + /* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */ + hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL, + SHGFP_TYPE_DEFAULT, wszTempPath); + if (FAILED(hr)) return; + pszPersonal = wine_get_unix_file_name(wszTempPath); + if (!pszPersonal) return; + + hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results); + if (FAILED(hr)) xdg_results = NULL; + + pszHome = getenv("HOME"); + if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode)) + { + while (1) + { + /* Check if there's already a Wine-specific 'My Documents' folder */ + strcpy(szPersonalTarget, pszHome); + if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) && + !stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode)) + { + /* '$HOME/My Documents' exists. Create subfolders for + * 'My Pictures', 'My Videos', 'My Music' etc. or fail silently + * if they already exist. + */ + for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++) + { + strcpy(szMyStuffTarget, szPersonalTarget); + if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i]))) + mkdir(szMyStuffTarget, 0777); + } + break; + } + + /* Try to point to the XDG Documents folder */ + if (xdg_results && xdg_results[num-2] && + !stat(xdg_results[num-2], &statFolder) && + S_ISDIR(statFolder.st_mode)) + { + strcpy(szPersonalTarget, xdg_results[num-2]); + break; + } + + /* Or the hardcoded / OS X Documents folder */ + strcpy(szPersonalTarget, pszHome); + if (_SHAppendToUnixPath(szPersonalTarget, DocumentsW) && + !stat(szPersonalTarget, &statFolder) && + S_ISDIR(statFolder.st_mode)) + break; + + /* As a last resort point to $HOME. */ + strcpy(szPersonalTarget, pszHome); + break; + } + + /* Replace 'My Documents' directory with a symlink or fail silently if not empty. */ + remove(pszPersonal); + symlink(szPersonalTarget, pszPersonal); + } + else + { + /* '$HOME' doesn't exist. Create subdirs for 'My Pictures', 'My Videos', + * 'My Music' etc. in '%USERPROFILE%\My Documents' or fail silently if + * they already exist. */ + pszHome = NULL; + strcpy(szPersonalTarget, pszPersonal); + for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++) { + strcpy(szMyStuffTarget, szPersonalTarget); + if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i]))) + mkdir(szMyStuffTarget, 0777); + } + } + + /* Create symbolic links for 'My Pictures', 'My Videos', 'My Music' etc. */ + for (i=0; i < ARRAY_SIZE(aidsMyStuff); i++) + { + /* Create the current 'My Whatever' folder and get its unix path. */ + hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL, + SHGFP_TYPE_DEFAULT, wszTempPath); + if (FAILED(hr)) continue; + + pszMyStuff = wine_get_unix_file_name(wszTempPath); + if (!pszMyStuff) continue; + + while (1) + { + /* Check for the Wine-specific '$HOME/My Documents' subfolder */ + strcpy(szMyStuffTarget, szPersonalTarget); + if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) && + !stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode)) + break; + + /* Try the XDG_XXX_DIR folder */ + if (xdg_results && xdg_results[i]) + { + strcpy(szMyStuffTarget, xdg_results[i]); + break; + } + + /* Or the OS X folder (these are never localized) */ + if (pszHome) + { + strcpy(szMyStuffTarget, pszHome); + if (_SHAppendToUnixPath(szMyStuffTarget, MyOSXStuffW[i]) && + !stat(szMyStuffTarget, &statFolder) && + S_ISDIR(statFolder.st_mode)) + break; + } + + /* As a last resort point to the same location as 'My Documents' */ + strcpy(szMyStuffTarget, szPersonalTarget); + break; + } + remove(pszMyStuff); + symlink(szMyStuffTarget, pszMyStuff); + heap_free(pszMyStuff); + } + + /* Last but not least, the Desktop folder */ + if (pszHome) + strcpy(szDesktopTarget, pszHome); + else + strcpy(szDesktopTarget, pszPersonal); + heap_free(pszPersonal); + + xdg_desktop_dir = xdg_results ? xdg_results[num - 1] : NULL; + if (xdg_desktop_dir || + (_SHAppendToUnixPath(szDesktopTarget, DesktopW) && + !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode))) + { + hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL, + SHGFP_TYPE_DEFAULT, wszTempPath); + if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath))) + { + remove(pszDesktop); + if (xdg_desktop_dir) + symlink(xdg_desktop_dir, pszDesktop); + else + symlink(szDesktopTarget, pszDesktop); + heap_free(pszDesktop); + } + } + + /* Free resources allocated by XDG_UserDirLookup() */ + if (xdg_results) + { + for (i = 0; i < num; i++) + heap_free(xdg_results[i]); + heap_free(xdg_results); + } +} + +/****************************************************************************** * SHGetFolderPathW [SHELL32.@] * - * Convert nFolder to path. + * Convert nFolder to path. * * RETURNS * Success: S_OK @@ -4046,7 +4311,7 @@ HRESULT WINAPI SHGetFolderPathAndSubDirW( DWORD folder = nFolder & CSIDL_FOLDER_MASK; CSIDL_Type type; int ret; - + TRACE("%p,%#x,%p,%#x,%s,%p\n", hwndOwner, nFolder, hToken, dwFlags, debugstr_w(pszSubPath), pszPath); /* Windows always NULL-terminates the resulting path regardless of success @@ -4381,271 +4646,6 @@ static HRESULT _SHRegisterCommonShellFolders(void) return hr; } -/****************************************************************************** - * _SHAppendToUnixPath [Internal] - * - * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the - * corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath' - * and replaces backslashes with slashes. - * - * PARAMS - * szBasePath [IO] The unix base path, which will be appended to (CP_UNXICP). - * pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW). - * - * RETURNS - * Success: TRUE, - * Failure: FALSE - */ -static inline BOOL _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) { - WCHAR wszSubPath[MAX_PATH]; - int cLen = strlen(szBasePath); - char *pBackslash; - - if (IS_INTRESOURCE(pwszSubPath)) { - if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) { - /* Fall back to hard coded defaults. */ - switch (LOWORD(pwszSubPath)) { - case IDS_PERSONAL: - lstrcpyW(wszSubPath, DocumentsW); - break; - case IDS_MYMUSIC: - lstrcpyW(wszSubPath, My_MusicW); - break; - case IDS_MYPICTURES: - lstrcpyW(wszSubPath, My_PicturesW); - break; - case IDS_MYVIDEOS: - lstrcpyW(wszSubPath, My_VideosW); - break; - case IDS_DOWNLOADS: - lstrcpyW(wszSubPath, DownloadsW); - break; - case IDS_TEMPLATES: - lstrcpyW(wszSubPath, TemplatesW); - break; - default: - ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath)); - return FALSE; - } - } - } else { - lstrcpyW(wszSubPath, pwszSubPath); - } - - if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/'; - - if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath + cLen, - FILENAME_MAX - cLen, NULL, NULL)) - { - return FALSE; - } - - pBackslash = szBasePath + cLen; - while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/'; - - return TRUE; -} - -/****************************************************************************** - * _SHCreateSymbolicLinks [Internal] - * - * Sets up symbol links for various shell folders to point into the user's home - * directory. We do an educated guess about what the user would probably want: - * - If there is a 'My Documents' directory in $HOME, the user probably wants - * wine's 'My Documents' to point there. Furthermore, we infer that the user - * is a Windows lover and has no problem with wine creating subfolders for - * 'My Pictures', 'My Music', 'My Videos' etc. under '$HOME/My Documents', if - * those do not already exist. We put appropriate symbolic links in place for - * those, too. - * - If there is no 'My Documents' directory in $HOME, we let 'My Documents' - * point directly to $HOME. We assume the user to be a unix hacker who does not - * want wine to create anything anywhere besides the .wine directory. So, if - * there already is a 'My Music' directory in $HOME, we symlink the 'My Music' - * shell folder to it. But if not, then we check XDG_MUSIC_DIR - "well known" - * directory, and try to link to that. If that fails, then we symlink to - * $HOME directly. The same holds for 'My Pictures', 'My Videos' etc. - * - The Desktop shell folder is symlinked to XDG_DESKTOP_DIR. If that does not - * exist, then we try '$HOME/Desktop'. If that does not exist, then we leave - * it alone. - * ('My Music',... above in fact means LoadString(IDS_MYMUSIC)) - */ -static void _SHCreateSymbolicLinks(void) -{ - static const UINT aidsMyStuff[] = { - IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC, IDS_DOWNLOADS, IDS_TEMPLATES - }; - static const WCHAR * const MyOSXStuffW[] = { - PicturesW, MoviesW, MusicW, DownloadsW, TemplatesW - }; - static const int acsidlMyStuff[] = { - CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC, CSIDL_DOWNLOADS, CSIDL_TEMPLATES - }; - static const char * const xdg_dirs[] = { - "PICTURES", "VIDEOS", "MUSIC", "DOWNLOAD", "TEMPLATES", "DOCUMENTS", "DESKTOP" - }; - static const unsigned int num = ARRAY_SIZE(xdg_dirs); - WCHAR wszTempPath[MAX_PATH]; - char szPersonalTarget[FILENAME_MAX], *pszPersonal; - char szMyStuffTarget[FILENAME_MAX], *pszMyStuff; - char szDesktopTarget[FILENAME_MAX], *pszDesktop; - struct stat statFolder; - const char *pszHome; - HRESULT hr; - char ** xdg_results; - char * xdg_desktop_dir; - UINT i; - - /* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */ - hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL, - SHGFP_TYPE_DEFAULT, wszTempPath); - if (FAILED(hr)) return; - pszPersonal = wine_get_unix_file_name(wszTempPath); - if (!pszPersonal) return; - - hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results); - if (FAILED(hr)) xdg_results = NULL; - - pszHome = getenv("HOME"); - if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode)) - { - while (1) - { - /* Check if there's already a Wine-specific 'My Documents' folder */ - strcpy(szPersonalTarget, pszHome); - if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) && - !stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode)) - { - /* '$HOME/My Documents' exists. Create subfolders for - * 'My Pictures', 'My Videos', 'My Music' etc. or fail silently - * if they already exist. - */ - for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++) - { - strcpy(szMyStuffTarget, szPersonalTarget); - if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i]))) - mkdir(szMyStuffTarget, 0777); - } - break; - } - - /* Try to point to the XDG Documents folder */ - if (xdg_results && xdg_results[num-2] && - !stat(xdg_results[num-2], &statFolder) && - S_ISDIR(statFolder.st_mode)) - { - strcpy(szPersonalTarget, xdg_results[num-2]); - break; - } - - /* Or the hardcoded / OS X Documents folder */ - strcpy(szPersonalTarget, pszHome); - if (_SHAppendToUnixPath(szPersonalTarget, DocumentsW) && - !stat(szPersonalTarget, &statFolder) && - S_ISDIR(statFolder.st_mode)) - break; - - /* As a last resort point to $HOME. */ - strcpy(szPersonalTarget, pszHome); - break; - } - - /* Replace 'My Documents' directory with a symlink or fail silently if not empty. */ - remove(pszPersonal); - symlink(szPersonalTarget, pszPersonal); - } - else - { - /* '$HOME' doesn't exist. Create subdirs for 'My Pictures', 'My Videos', - * 'My Music' etc. in '%USERPROFILE%\My Documents' or fail silently if - * they already exist. */ - pszHome = NULL; - strcpy(szPersonalTarget, pszPersonal); - for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++) { - strcpy(szMyStuffTarget, szPersonalTarget); - if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i]))) - mkdir(szMyStuffTarget, 0777); - } - } - - /* Create symbolic links for 'My Pictures', 'My Videos', 'My Music' etc. */ - for (i=0; i < ARRAY_SIZE(aidsMyStuff); i++) - { - /* Create the current 'My Whatever' folder and get its unix path. */ - hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL, - SHGFP_TYPE_DEFAULT, wszTempPath); - if (FAILED(hr)) continue; - - pszMyStuff = wine_get_unix_file_name(wszTempPath); - if (!pszMyStuff) continue; - - while (1) - { - /* Check for the Wine-specific '$HOME/My Documents' subfolder */ - strcpy(szMyStuffTarget, szPersonalTarget); - if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) && - !stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode)) - break; - - /* Try the XDG_XXX_DIR folder */ - if (xdg_results && xdg_results[i]) - { - strcpy(szMyStuffTarget, xdg_results[i]); - break; - } - - /* Or the OS X folder (these are never localized) */ - if (pszHome) - { - strcpy(szMyStuffTarget, pszHome); - if (_SHAppendToUnixPath(szMyStuffTarget, MyOSXStuffW[i]) && - !stat(szMyStuffTarget, &statFolder) && - S_ISDIR(statFolder.st_mode)) - break; - } - - /* As a last resort point to the same location as 'My Documents' */ - strcpy(szMyStuffTarget, szPersonalTarget); - break; - } - remove(pszMyStuff); - symlink(szMyStuffTarget, pszMyStuff); - heap_free(pszMyStuff); - } - - /* Last but not least, the Desktop folder */ - if (pszHome) - strcpy(szDesktopTarget, pszHome); - else - strcpy(szDesktopTarget, pszPersonal); - heap_free(pszPersonal); - - xdg_desktop_dir = xdg_results ? xdg_results[num - 1] : NULL; - if (xdg_desktop_dir || - (_SHAppendToUnixPath(szDesktopTarget, DesktopW) && - !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode))) - { - hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL, - SHGFP_TYPE_DEFAULT, wszTempPath); - if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath))) - { - remove(pszDesktop); - if (xdg_desktop_dir) - symlink(xdg_desktop_dir, pszDesktop); - else - symlink(szDesktopTarget, pszDesktop); - heap_free(pszDesktop); - } - } - - /* Free resources allocated by XDG_UserDirLookup() */ - if (xdg_results) - { - for (i = 0; i < num; i++) - heap_free(xdg_results[i]); - heap_free(xdg_results); - } -} - /****************************************************************************** * create_extra_folders [Internal] *