Prepare the helper function for rework by moving it above the function that will use it and splitting it by kind of directories.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22974 Signed-off-by: Olivier F. R. Dierick o.dierick@piezo-forte.be --- dlls/shell32/shellpath.c | 638 +++++++++++++++++++++++++++-------------------- 1 file changed, 373 insertions(+), 265 deletions(-)
diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c index b6350ea..293c51b 100644 --- a/dlls/shell32/shellpath.c +++ b/dlls/shell32/shellpath.c @@ -4030,6 +4030,379 @@ cleanup: 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; +} + +/****************************************************************************** + * _SHCreateMyDocumentsSymbolicLink [Internal] + * + * Sets up a symbolic link for the 'My Documents' shell folder to point into + * the users home directory. + */ +static void _SHCreateMyDocumentsSymbolicLink(void) +{ + UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC, IDS_DOWNLOADS, IDS_TEMPLATES }; + static const char * const xdg_dirs[] = { "DOCUMENTS" }; + static const unsigned int num = ARRAY_SIZE(xdg_dirs); + WCHAR wszTempPath[MAX_PATH]; + char szPersonalTarget[FILENAME_MAX], *pszPersonal; + char szMyStuffTarget[FILENAME_MAX]; + struct stat statFolder; + const char *pszHome; + HRESULT hr; + char ** xdg_results; + 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 'My Pictures', + * 'My Videos' and 'My Music' subfolders 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[0] && + !stat(xdg_results[0], &statFolder) && + S_ISDIR(statFolder.st_mode)) + { + strcpy(szPersonalTarget, xdg_results[0]); + 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 'My Pictures', 'My Videos' and 'My Music' subdirs + * 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); + } + } + + heap_free(pszPersonal); + + /* Free resources allocated by XDG_UserDirLookup() */ + if (xdg_results) + { + for (i = 0; i < num; i++) + heap_free(xdg_results[i]); + heap_free(xdg_results); + } +} + +/****************************************************************************** + * _SHCreateMyStuffSymbolicLink [Internal] + * + * Sets up a symbolic link for one of the 'My Whatever' shell folders to point + * into the users home directory. + * + * PARAMS + * nFolder [I] CSIDL identifying the folder. + */ +static void _SHCreateMyStuffSymbolicLink(int nFolder) +{ + UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC, IDS_DOWNLOADS, IDS_TEMPLATES }; + const WCHAR* MyOSXStuffW[] = { PicturesW, MoviesW, MusicW, DownloadsW, TemplatesW }; + int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC, CSIDL_DOWNLOADS, CSIDL_TEMPLATES }; + static const char * const xdg_dirs[] = { "PICTURES", "VIDEOS", "MUSIC", "DOWNLOAD", "TEMPLATES" }; + static const unsigned int num = ARRAY_SIZE(xdg_dirs); + WCHAR wszTempPath[MAX_PATH]; + char szPersonalTarget[FILENAME_MAX], *pszPersonal; + char szMyStuffTarget[FILENAME_MAX], *pszMyStuff; + struct stat statFolder; + const char *pszHome; + HRESULT hr; + char ** xdg_results; + DWORD folder = nFolder & CSIDL_FOLDER_MASK; + UINT i; + + for (i = 0; i < ARRAY_SIZE(acsidlMyStuff) && acsidlMyStuff[i] != folder; i++); + if (i >= ARRAY_SIZE(acsidlMyStuff)) return; + + /* Get the real path of the existing 'My Documents' shell folder. */ + 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; + + strcpy(szPersonalTarget, pszPersonal); + if (!stat(pszPersonal, &statFolder) && S_ISLNK(statFolder.st_mode)) + { + int cLen = readlink(pszPersonal, szPersonalTarget, FILENAME_MAX-1); + if (cLen >= 0) szPersonalTarget[cLen] = '\0'; + } + heap_free(pszPersonal); + + hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results); + if (FAILED(hr)) xdg_results = NULL; + + pszHome = getenv("HOME"); + + while (1) + { + /* 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)) break; + + pszMyStuff = wine_get_unix_file_name(wszTempPath); + if (!pszMyStuff) break; + + 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); + break; + } + + /* Free resources allocated by XDG_UserDirLookup() */ + if (xdg_results) + { + for (i = 0; i < num; i++) + heap_free(xdg_results[i]); + heap_free(xdg_results); + } +} + +/****************************************************************************** + * _SHCreateDesktopSymbolicLink [Internal] + * + * Sets up a symbolic link for the 'Desktop' shell folder to point into the + * users home directory. + */ +static void _SHCreateDesktopSymbolicLink(void) +{ + UINT i; + static const char * const xdg_dirs[] = { "DESKTOP" }; + static const unsigned int num = ARRAY_SIZE(xdg_dirs); + WCHAR wszTempPath[MAX_PATH]; + char *pszPersonal; + char szDesktopTarget[FILENAME_MAX], *pszDesktop; + struct stat statFolder; + const char *pszHome; + HRESULT hr; + char ** xdg_results; + char * xdg_desktop_dir; + + /* 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) + strcpy(szDesktopTarget, pszHome); + else + strcpy(szDesktopTarget, pszPersonal); + heap_free(pszPersonal); + + xdg_desktop_dir = xdg_results ? xdg_results[0] : 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); + } +} + +/****************************************************************************** + * _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) +{ + UINT i; + int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC, CSIDL_DOWNLOADS, CSIDL_TEMPLATES }; + + _SHCreateMyDocumentsSymbolicLink(); + + /* Create symbolic links for 'My Pictures', 'My Videos', 'My Music', 'Downloads' and 'Templates'. */ + for (i=0; i < ARRAY_SIZE(acsidlMyStuff); i++) + _SHCreateMyStuffSymbolicLink(acsidlMyStuff[i]); + + /* Last but not least, the Desktop folder */ + _SHCreateDesktopSymbolicLink(); +} + /************************************************************************* * SHGetFolderPathAndSubDirW [SHELL32.@] */ @@ -4382,271 +4755,6 @@ static HRESULT _SHRegisterCommonShellFolders(void) }
/****************************************************************************** - * _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] * * Create some extra folders that don't have a standard CSIDL definition.
Updating a prefix should not mangle existing user folders. Some people want to use real directories or customized symbolic links. Forcibly resetting empty dirs or symlinks on each update is annoying at best. The only reason to create user folders is when they are really missing from the prefix. This automatic repair of the prefix should be done not only on prefix creation or update, but whenever a user folder is looked up, and that repair should have consistent behaviour.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=22974 Signed-off-by: Olivier F. R. Dierick o.dierick@piezo-forte.be --- dlls/shell32/shellpath.c | 66 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 18 deletions(-)
diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c index 293c51b..334cc7e 100644 --- a/dlls/shell32/shellpath.c +++ b/dlls/shell32/shellpath.c @@ -4115,8 +4115,8 @@ static void _SHCreateMyDocumentsSymbolicLink(void) char ** xdg_results; 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, + /* Get the unix path of the profile sub-dirs up to 'My Documents'. */ + hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_DEFAULT, wszTempPath); if (FAILED(hr)) return; pszPersonal = wine_get_unix_file_name(wszTempPath); @@ -4169,8 +4169,8 @@ static void _SHCreateMyDocumentsSymbolicLink(void) break; }
- /* Replace 'My Documents' directory with a symlink or fail silently if not empty. */ - remove(pszPersonal); + /* Create symbolic link to 'My Documents' or fail silently if a directory + * or symlink exists. */ symlink(szPersonalTarget, pszPersonal); } else @@ -4248,8 +4248,8 @@ static void _SHCreateMyStuffSymbolicLink(int nFolder)
while (1) { - /* Create the current 'My Whatever' folder and get its unix path. */ - hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL, + /* Get the current 'My Whatever' folder unix path. */ + hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_DEFAULT, wszTempPath); if (FAILED(hr)) break;
@@ -4285,7 +4285,6 @@ static void _SHCreateMyStuffSymbolicLink(int nFolder) strcpy(szMyStuffTarget, szPersonalTarget); break; } - remove(pszMyStuff); symlink(szMyStuffTarget, pszMyStuff); heap_free(pszMyStuff); break; @@ -4343,11 +4342,10 @@ static void _SHCreateDesktopSymbolicLink(void) (_SHAppendToUnixPath(szDesktopTarget, DesktopW) && !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode))) { - hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL, + hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_DONT_VERIFY, 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 @@ -4366,6 +4364,36 @@ static void _SHCreateDesktopSymbolicLink(void) }
/****************************************************************************** + * _SHCreateSymbolicLink [Internal] + * + * Sets up a symbolic link for one of the special shell folders to point into + * the users home directory. + * + * PARAMS + * nFolder [I] CSIDL identifying the folder. + */ +static void _SHCreateSymbolicLink(int nFolder) +{ + DWORD folder = nFolder & CSIDL_FOLDER_MASK; + + switch (folder) { + case CSIDL_PERSONAL: + _SHCreateMyDocumentsSymbolicLink(); + break; + case CSIDL_MYPICTURES: + case CSIDL_MYVIDEO: + case CSIDL_MYMUSIC: + case CSIDL_DOWNLOADS: + case CSIDL_TEMPLATES: + _SHCreateMyStuffSymbolicLink(folder); + break; + case CSIDL_DESKTOPDIRECTORY: + _SHCreateDesktopSymbolicLink(); + break; + } +} + +/****************************************************************************** * _SHCreateSymbolicLinks [Internal] * * Sets up symbol links for various shell folders to point into the user's home @@ -4391,16 +4419,10 @@ static void _SHCreateDesktopSymbolicLink(void) static void _SHCreateSymbolicLinks(void) { UINT i; - int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC, CSIDL_DOWNLOADS, CSIDL_TEMPLATES }; - - _SHCreateMyDocumentsSymbolicLink(); + int acsidl[] = { CSIDL_PERSONAL, CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC, CSIDL_DOWNLOADS, CSIDL_TEMPLATES, CSIDL_DESKTOPDIRECTORY };
- /* Create symbolic links for 'My Pictures', 'My Videos', 'My Music', 'Downloads' and 'Templates'. */ - for (i=0; i < ARRAY_SIZE(acsidlMyStuff); i++) - _SHCreateMyStuffSymbolicLink(acsidlMyStuff[i]); - - /* Last but not least, the Desktop folder */ - _SHCreateDesktopSymbolicLink(); + for (i=0; i < ARRAY_SIZE(acsidl); i++) + _SHCreateSymbolicLink(acsidl[i]); }
/************************************************************************* @@ -4527,6 +4549,10 @@ HRESULT WINAPI SHGetFolderPathAndSubDirW( goto end; }
+ /* create symbolic links rather than directories for specific + * user shell folders */ + _SHCreateSymbolicLink(folder); + /* create directory/directories */ ret = SHCreateDirectoryExW(hwndOwner, szBuildPath, NULL); if (ret && ret != ERROR_ALREADY_EXISTS) @@ -5160,6 +5186,10 @@ HRESULT WINAPI SHGetKnownFolderPath(REFKNOWNFOLDERID rfid, DWORD flags, HANDLE t goto failed; }
+ /* create symbolic links rather than directories for specific + * user shell folders */ + _SHCreateSymbolicLink(folder); + /* create directory/directories */ ret = SHCreateDirectoryExW(NULL, pathW, NULL); if (ret && ret != ERROR_ALREADY_EXISTS)
"Olivier F. R. Dierick" o.dierick@piezo-forte.be writes:
Prepare the helper function for rework by moving it above the function that will use it and splitting it by kind of directories.
Please don't move code and change it at the same time, that makes it impossible to see what changed.