shell32: Don't put shell folders inside My Documents.

Put them all at the same level, like recent Windows versions do.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2021-09-06 13:53:24 +02:00
parent bf811fdcaf
commit 5321428f64

View file

@ -901,7 +901,6 @@ static const WCHAR Microsoft_Windows_Sidebar_GadgetsW[] = {'M','i','c','r','o','
static const WCHAR Microsoft_Windows_Start_MenuW[] = {'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','S','t','a','r','t',' ','M','e','n','u',0};
static const WCHAR Microsoft_Windows_TemplatesW[] = {'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','T','e','m','p','l','a','t','e','s',0};
static const WCHAR Microsoft_Windows_ThemesW[] = {'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\','T','h','e','m','e','s',0};
static const WCHAR MoviesW[] = {'M','o','v','i','e','s','\0'};
static const WCHAR MusicW[] = {'M','u','s','i','c','\0'};
static const WCHAR MusicLibraryW[] = {'M','u','s','i','c','L','i','b','r','a','r','y',0};
static const WCHAR Music_librarymsW[] = {'M','u','s','i','c','.','l','i','b','r','a','r','y','-','m','s',0};
@ -4080,249 +4079,31 @@ static inline void _SHFreeXDGUserDirs(const unsigned int num_dirs, char ** xdg_r
}
}
/*************************************************************************
* _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;
}
/******************************************************************************
* _SHGetFolderUnixPath [Internal]
*
* Create a shell folder and get its unix path.
*
* PARAMS
* nFolder [I] CSIDL identifying the folder.
*/
static inline char * _SHGetFolderUnixPath(const int nFolder)
static inline void build_path( char *dest, const char *base, const char *dir )
{
WCHAR wszTempPath[MAX_PATH];
HRESULT hr;
int len;
hr = SHGetFolderPathW(NULL, nFolder, NULL,
SHGFP_TYPE_CURRENT, wszTempPath);
if (FAILED(hr)) return NULL;
return wine_get_unix_file_name(wszTempPath);
lstrcpynA( dest, base, FILENAME_MAX );
len = strlen( dest );
if (!len || dest[len - 1] != '/') dest[len++] = '/';
lstrcpynA( dest + len, dir, FILENAME_MAX - len );
}
/******************************************************************************
* _SHCreateMyDocumentsSubDirs [Internal]
*
* Create real directories for various shell folders under 'My Documents'. For
* Windows and homeless styles. Fails silently for already existing sub dirs.
*
* PARAMS
* aidsMyStuff [I] Array of IDS_* resources to create sub dirs for.
* num [I] Number of elements in aidsMyStuff.
* szPersonalTarget [I] Unix path to 'My Documents' directory.
*/
static void _SHCreateMyDocumentsSubDirs(const UINT * aidsMyStuff, const UINT num, const char * szPersonalTarget)
{
char szMyStuffTarget[FILENAME_MAX];
UINT i;
if (aidsMyStuff && szPersonalTarget)
{
for (i = 0; i < num; i++)
{
strcpy(szMyStuffTarget, szPersonalTarget);
if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
mkdir(szMyStuffTarget, 0777);
}
}
}
/******************************************************************************
* _SHCreateMyDocumentsSymbolicLink [Internal]
*
* Sets up a symbolic link for the 'My Documents' shell folder to point into
* the users home directory.
*
* PARAMS
* aidsMyStuff [I] Array of IDS_* resources to create sub dirs for.
* aids_num [I] Number of elements in aidsMyStuff.
*/
static void _SHCreateMyDocumentsSymbolicLink(const UINT * aidsMyStuff, const UINT aids_num, const WCHAR *path)
{
static const char * const xdg_dirs[] = { "DOCUMENTS" };
static const unsigned int num = ARRAY_SIZE(xdg_dirs);
char szPersonalTarget[FILENAME_MAX], *pszPersonal;
struct stat statFolder;
const char *pszHome;
char ** xdg_results;
/* Get the unix path of 'My Documents'. */
pszPersonal = wine_get_unix_file_name( path );
if (!pszPersonal) return;
_SHGetXDGUserDirs(xdg_dirs, num, &xdg_results);
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.
*/
_SHCreateMyDocumentsSubDirs(aidsMyStuff, aids_num, szPersonalTarget);
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;
}
/* Create symbolic link to 'My Documents' or fail silently if a directory
* or symlink exists. */
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);
_SHCreateMyDocumentsSubDirs(aidsMyStuff, aids_num, szPersonalTarget);
}
heap_free(pszPersonal);
_SHFreeXDGUserDirs(num, xdg_results);
}
/******************************************************************************
* _SHCreateMyStuffSymbolicLink [Internal]
* create_link
*
* 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.
* into the corresponding XDG directory.
*/
static void _SHCreateMyStuffSymbolicLink(int nFolder, const WCHAR *path)
static void create_link( const WCHAR *path, const char *xdg_name, const char *default_name )
{
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"
};
static const unsigned int num = ARRAY_SIZE(xdg_dirs);
char szPersonalTarget[FILENAME_MAX], *pszPersonal;
char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
struct stat statFolder;
const char *pszHome;
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;
/* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
pszPersonal = _SHGetFolderUnixPath(CSIDL_PERSONAL|CSIDL_FLAG_CREATE);
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);
_SHGetXDGUserDirs(xdg_dirs, num, &xdg_results);
_SHGetXDGUserDirs(&xdg_name, 1, &xdg_results);
pszHome = getenv("HOME");
@ -4333,31 +4114,23 @@ static void _SHCreateMyStuffSymbolicLink(int nFolder, const WCHAR *path)
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])
if (xdg_results && xdg_results[0])
{
strcpy(szMyStuffTarget, xdg_results[i]);
strcpy(szMyStuffTarget, xdg_results[0]);
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))
build_path( szMyStuffTarget, pszHome, default_name );
if (!stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
break;
}
/* As a last resort point to the same location as 'My Documents' */
strcpy(szMyStuffTarget, szPersonalTarget);
/* As a last resort point to $HOME. */
strcpy(szMyStuffTarget, pszHome);
break;
}
symlink(szMyStuffTarget, pszMyStuff);
@ -4365,57 +4138,7 @@ static void _SHCreateMyStuffSymbolicLink(int nFolder, const WCHAR *path)
break;
}
_SHFreeXDGUserDirs(num, xdg_results);
}
/******************************************************************************
* _SHCreateDesktopSymbolicLink [Internal]
*
* Sets up a symbolic link for the 'Desktop' shell folder to point into the
* users home directory.
*/
static void _SHCreateDesktopSymbolicLink( const WCHAR *path )
{
static const char * const xdg_dirs[] = { "DESKTOP" };
static const unsigned int num = ARRAY_SIZE(xdg_dirs);
char *pszPersonal;
char szDesktopTarget[FILENAME_MAX], *pszDesktop;
struct stat statFolder;
const char *pszHome;
char ** xdg_results;
char * xdg_desktop_dir;
/* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
pszPersonal = _SHGetFolderUnixPath(CSIDL_PERSONAL|CSIDL_FLAG_CREATE);
if (!pszPersonal) return;
_SHGetXDGUserDirs(xdg_dirs, num, &xdg_results);
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)))
{
pszDesktop = wine_get_unix_file_name( path );
if (pszDesktop)
{
if (xdg_desktop_dir)
symlink(xdg_desktop_dir, pszDesktop);
else
symlink(szDesktopTarget, pszDesktop);
heap_free(pszDesktop);
}
}
_SHFreeXDGUserDirs(num, xdg_results);
_SHFreeXDGUserDirs(1, xdg_results);
}
/******************************************************************************
@ -4429,24 +4152,29 @@ static void _SHCreateDesktopSymbolicLink( const WCHAR *path )
*/
static void _SHCreateSymbolicLink(int nFolder, const WCHAR *path)
{
static const UINT aidsMyStuff[] = {
IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC, IDS_DOWNLOADS, IDS_TEMPLATES
};
DWORD folder = nFolder & CSIDL_FOLDER_MASK;
switch (folder) {
case CSIDL_PERSONAL:
_SHCreateMyDocumentsSymbolicLink(aidsMyStuff, ARRAY_SIZE(aidsMyStuff), path);
break;
case CSIDL_MYPICTURES:
case CSIDL_MYVIDEO:
case CSIDL_MYMUSIC:
case CSIDL_DOWNLOADS:
case CSIDL_TEMPLATES:
_SHCreateMyStuffSymbolicLink(folder, path);
create_link( path, "DOCUMENTS", "Documents" );
break;
case CSIDL_DESKTOPDIRECTORY:
_SHCreateDesktopSymbolicLink( path );
create_link( path, "DESKTOP", "Desktop" );
break;
case CSIDL_MYPICTURES:
create_link( path, "PICTURES", "Pictures" );
break;
case CSIDL_MYVIDEO:
create_link( path, "VIDEOS", "Movies" );
break;
case CSIDL_MYMUSIC:
create_link( path, "MUSIC", "Music" );
break;
case CSIDL_DOWNLOADS:
create_link( path, "DOWNLOAD", "Downloads" );
break;
case CSIDL_TEMPLATES:
create_link( path, "TEMPLATES", "Templates" );
break;
}
}