[PATCH] shellpath.c: Fix creation of HOME directory symbolic links
Bob Wya
bob.mt.wya at gmail.com
Thu Jun 7 10:11:10 CDT 2018
On 7 June 2018 at 11:57, Huw Davies <huw at codeweavers.com> wrote:
> On Wed, Jun 06, 2018 at 08:46:49AM +0100, Rob Walker wrote:
> > Fixes: https://bugs.winehq.org/show_bug.cgi?id=41668 (1)
> > https://bugs.winehq.org/show_bug.cgi?id=28216 (2)
> >
> > (1) Introduces a "WINESYMLINK" env variable that allows a user to
> > control what shell folder links are created to the HOME folder, when
> > a WINEPREFIX is initially created.
> > The Wineprefix Shell Folders, that Wine symlinks to the user's HOME
> directory,
> > are only (re-)created on each Wine boot if they: do not pre-exist or
> are broken
> > symbolic links.
> >
> > (2) The XDG Directory specification automatically falls back to the user
> HOME
> > directory for any XDG_*_DIR that does not exist
> (xdg-user-dirs-update).
> > The implemented solution is to block Wine from symlinking directly
> > to HOME directory.
>
> Hi Rob,
>
> There's an awful lot going on in this patch which makes it very
> difficult to review. Please avoid re-naming and re-indenting
> existing functions so that we can actually see the real changes.
>
> Then please split the changes into separate patches. A clue
> that you're not doing this is that you have a list of two
> items in the commit message. If the patch is doing two
> things, it should be two patches.
>
> Huw.
>
>
Hi Huw,
Thanks for the valuable feedback! I think I spent quite a bit of time
fiddling with this,
so I've probably "lost the wood for the trees" a bit... :-)
Just to clarify a style point, for the re-factored patches...
If I still want to split the _SHCreateSymbolicLinks() function into a
re-factored
implementation that has an iterative wrapper function, is it OK to call
them:
_SHCreateSymbolicLink()
_SHCreateSymbolicLinks()
??
This would introduce a new "Camel Case" style function name... But would
obviously
be more consistent...
Thanks
Rob
> >
> > Test on Gentoo GNU/Linux.
> >
> > Signed-off-by: Rob Walker <bob.mt.wya at gmail.com>
> > ---
> > dlls/shell32/shellpath.c | 554 ++++++++++++++++++++++++---------------
> > 1 file changed, 343 insertions(+), 211 deletions(-)
> >
> > diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c
> > index a551e93aa8..b8c74273c6 100644
> > --- a/dlls/shell32/shellpath.c
> > +++ b/dlls/shell32/shellpath.c
> > @@ -4326,252 +4326,259 @@ static HRESULT _SHRegisterCommonShellFolders(
> void)
> > }
> >
> > /***********************************************************
> *******************
> > - * _SHAppendToUnixPath [Internal]
> > + * append_to_unix_path [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.
> > + * Helper function for create_home_subdir_symbolic_link.
> > + * Appends 'subpath', or the corresponding resource (if it is a
> > + * resource identifier), to the Unix base path 'base_dir'.
> > + * Replace backslashes with forward slashes.
> > *
> > * PARAMS
> > - * szBasePath [IO] The unix base path, which will be appended to
> (CP_UNXICP).
> > - * pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW).
> > + * base_dir [IO] The Unix base path, which will be appended to
> (CP_UNXICP).
> > + * subpath [I] Sub-path or resource id (use MAKEINTRESOURCEW,
> pre-call).
> > *
> > * RETURNS
> > - * Success: TRUE,
> > + * 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;
> > - default:
> > - ERR("LoadString(%d) failed!\n",
> LOWORD(pwszSubPath));
> > - return FALSE;
> > - }
> > +static inline BOOL append_to_unix_path(char *base_dir, LPCWSTR subpath)
> > +{
> > + WCHAR ws_subpath[MAX_PATH];
> > + int cLen = strlen(base_dir);
> > + char *back_slash;
> > +
> > + if (!IS_INTRESOURCE(subpath))
> > + {
> > + lstrcpyW(ws_subpath, subpath);
> > + }
> > + else if (!LoadStringW(shell32_hInstance, LOWORD(subpath),
> ws_subpath, MAX_PATH))
> > + {
> > + /* Fall back to hard coded defaults. */
> > + switch (LOWORD(subpath)) {
> > + case IDS_PERSONAL:
> > + lstrcpyW(ws_subpath, DocumentsW);
> > + break;
> > + case IDS_MYMUSIC:
> > + lstrcpyW(ws_subpath, My_MusicW);
> > + break;
> > + case IDS_MYPICTURES:
> > + lstrcpyW(ws_subpath, My_PicturesW);
> > + break;
> > + case IDS_MYVIDEOS:
> > + lstrcpyW(ws_subpath, My_VideosW);
> > + break;
> > + case IDS_DESKTOP:
> > + lstrcpyW(ws_subpath, DesktopW);
> > + break;
> > + default:
> > + ERR("LoadString(%d) failed!\n", LOWORD(subpath));
> > + return FALSE;
> > }
> > - } else {
> > - lstrcpyW(wszSubPath, pwszSubPath);
> > }
> > -
> > - if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
> > -
> > - if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath
> + cLen,
> > +
> > + if (!cLen || (base_dir[cLen-1] != '/')) base_dir[cLen++] = '/';
> > +
> > + if (!WideCharToMultiByte(CP_UNIXCP, 0, ws_subpath, -1, base_dir +
> cLen,
> > FILENAME_MAX - cLen, NULL, NULL))
> > {
> > return FALSE;
> > }
> > -
> > - pBackslash = szBasePath + cLen;
> > - while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
> > -
> > +
> > + back_slash = base_dir + cLen;
> > + while ((back_slash = strchr(back_slash, '\\'))) *back_slash = '/';
> > +
> > return TRUE;
> > }
> >
> > /***********************************************************
> *******************
> > - * _SHCreateSymbolicLinks [Internal]
> > + * compare_pathft_to_bootft [Internal]
> > *
> > - * Sets up symbol links for various shell folders to point into the
> users 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 imply that
> the user
> > - * is a Windows lover and has no problem with wine creating 'My
> Pictures',
> > - * 'My Music' and 'My Videos' subfolders under '$HOME/My Documents',
> if those
> > - * do not already exits. 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 fo 'My Pictures' and 'My Videos'.
> > - * - 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))
> > + * Function to compare the FILETIME of ws_path (a Windows path) to the
> (estimated)
> > + * Wine System boot time.
> > + *
> > + * PARAMS
> > + * ws_path [I] The Windows path, for which provides the FS
> write time.
> > + * time_difference [O] -1 Windows path last written before time of
> Wine boot.
> > + * 0 Windows path last written at time of Wine
> boot.
> > + * +1 Windows path last written after time of
> Wine boot.
> > + * RETURNS
> > + * Success: S_OK
> > + * Failure: E_FAIL
> > + *
> > + * NOTES
> > + * Pre-allocate storage for time_difference pointer variable
> externally.
> > */
> > -static void _SHCreateSymbolicLinks(void)
> > -{
> > - UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC },
> i;
> > - const WCHAR* MyOSXStuffW[] = { PicturesW, MoviesW, MusicW };
> > - int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO,
> CSIDL_MYMUSIC };
> > - static const char * const xdg_dirs[] = { "PICTURES", "VIDEOS",
> "MUSIC", "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;
> > -
> > - /* 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;
> > +static HRESULT compare_pathft_to_bootft(const WCHAR * ws_path, int *
> time_difference)
> > +{
> > + static ULONG64 time_system_start = 0;
> > + SYSTEMTIME system_time_st;
> > + FILETIME dummy_ft, system_time_ft, last_write_ft;
> > + ULONG64 tick_count, time_last_write;
> > + HANDLE fhandle;
> > + DWORD ft_errorc = 0;
> >
> > - hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results);
> > - if (FAILED(hr)) xdg_results = NULL;
> > + if (!time_difference) return E_FAIL;
> >
> > - pszHome = getenv("HOME");
> > - if (pszHome && !stat(pszHome, &statFolder) &&
> S_ISDIR(statFolder.st_mode))
> > + if (!time_system_start)
> > {
> > - while (1)
> > + tick_count = (ULONG64) GetTickCount64();
> > + GetSystemTime(&system_time_st);
> > + if (!SystemTimeToFileTime( &system_time_st, &system_time_ft))
> > {
> > - /* 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[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;
> > + ERR("SystemTimeToFileTime call failed (%d)\n",
> GetLastError());
> > + return E_FAIL;
> > }
> > -
> > - /* Replace 'My Documents' directory with a symlink or fail
> silently if not empty. */
> > - remove(pszPersonal);
> > - symlink(szPersonalTarget, pszPersonal);
> > + time_system_start = (((ULONG64)system_time_ft.dwHighDateTime)
> << 32)
> > + + system_time_ft.dwLowDateTime
> > + - (tick_count * 10000);
> > }
> > - else
> > + fhandle = CreateFileW( ws_path, 0, FILE_SHARE_READ, NULL,
> > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
> NULL);
> > + if (fhandle == INVALID_HANDLE_VALUE)
> > {
> > - /* '$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);
> > - }
> > + ERR("Open file handle failed for path: %s (%d)\n",
> > + debugstr_w(ws_path), GetLastError());
> > + return E_FAIL;
> > }
> > -
> > - /* Create symbolic links for 'My Pictures', 'My Videos' and 'My
> Music'. */
> > - for (i=0; i < ARRAY_SIZE(aidsMyStuff); i++)
> > + if (!GetFileTime(fhandle, &dummy_ft, &dummy_ft, &last_write_ft))
> > + ft_errorc = GetLastError();
> > + if (!CloseHandle(fhandle))
> > {
> > - /* 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;
> > + ERR("Close file handle failed: %p (%d)\n",
> > + fhandle, GetLastError());
> > + return E_FAIL;
> > + }
> > + if (ft_errorc)
> > + {
> > + ERR("Get file time failed: %p (%d)\n",
> > + fhandle, ft_errorc);
> > + return E_FAIL;
> > + }
> > + time_last_write = (((ULONG64)last_write_ft.dwHighDateTime) << 32)
> > + + last_write_ft.dwLowDateTime;
> > + *time_difference = ((time_last_write > time_system_start) ? 1 :
> > + ((time_last_write < time_system_start) ? -1 :
> > + 0));
> >
> > - /* Try the XDG_XXX_DIR folder */
> > - if (xdg_results && xdg_results[i])
> > - {
> > - strcpy(szMyStuffTarget, xdg_results[i]);
> > - break;
> > - }
> > + return S_OK;
> > +}
> >
> > - /* 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;
> > - }
> > +/**********************************************************
> ********************
> > + * create_homedir_symbolic_link [Internal]
> > + *
> > + * Creates a symbolic link from the current Wineprefix to an appropriate
> > + * HOME subdirectory (if one is found).
> > + *
> > + * Creates 'XXXX' directory in Wineprefix.
> > + * Then create a 'My XXXX' symbolic link in Wineprefix:
> > + * 1) If '$HOME/XXXX' (IDS directory) exists then target this.
> > + * 2) If '$HOME/XXXX' (XDG_XXXX_DIR) exists then target this.
> > + * 3) If '$HOME/XXXX' (MacOS XXXX media directory) exists then target
> this.
> > + *
> > + * PARAMS
> > + * env_enabled [I]
> > + * TRUE The user has enabled this Shell Folder to be
> symlinked (default).
> > + * FALSE The user has chosen to disable symlinking this
> Shell Folder.
> > + * The Shell Folder will be created, in the current
> Wineprefix (if not pre-existing).
> > + * ids_dir [I] Windows Resource Identifier code for current Shell
> Folder.
> > + * csidl_dir [I] Constant Special Item ID List identifier for
> current Shell Folder.
> > + * xdg_dir [I] Full path of external Unix XDG directory
> corresponding to current Shell Folder.
> > + * ws_osx_dir [I] Fallback directory name to use, corresponding to
> current Shell Folder (OSX specific).
> > + *
> > + */
> > +void create_homedir_symbolic_link(BOOL env_enabled,
> > + UINT ids_dir,
> > + int csidl_dir,
> > + const char * xdg_dir,
> > + const WCHAR * ws_osx_dir)
> > +{
> > + static const char * env_homedir = NULL;
> > + WCHAR ws_temp_path[MAX_PATH];
> > + char home_target[FILENAME_MAX], * prefix_dir;
> > + struct stat stat_folder, stat_home_folder;
> > + HRESULT hr;
> > + BOOL target_ok;
> > + int time_difference;
> >
> > - /* As a last resort point to the same location as 'My
> Documents' */
> > - strcpy(szMyStuffTarget, szPersonalTarget);
> > - break;
> > + hr = SHGetFolderPathW(NULL, csidl_dir, NULL,
> > + SHGFP_TYPE_DEFAULT, ws_temp_path);
> > + if (SUCCEEDED(hr))
> > + {
> > + if (ids_dir != IDS_DESKTOPDIRECTORY) return;
> > +
> > + if ((compare_pathft_to_bootft(ws_temp_path, &time_difference)
> == S_OK)
> > + && (time_difference < 0))
> > + {
> > + TRACE("%s directory created before wineboot\n",
> > + debugstr_w(ws_temp_path));
> > + return;
> > }
> > - remove(pszMyStuff);
> > - symlink(szMyStuffTarget, pszMyStuff);
> > - heap_free(pszMyStuff);
> > }
> > -
> > - /* Last but not least, the Desktop folder */
> > - if (pszHome)
> > - strcpy(szDesktopTarget, pszHome);
> > + else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
> > + {
> > + hr = SHGetFolderPathW(NULL, csidl_dir|CSIDL_FLAG_CREATE, NULL,
> > + SHGFP_TYPE_DEFAULT, ws_temp_path);
> > + if (FAILED(hr)) return;
> > + }
> > else
> > - strcpy(szDesktopTarget, pszPersonal);
> > - heap_free(pszPersonal);
> > + {
> > + ERR("Failed to get Wineprefix path corresponding to %d CSIDL\n",
> > + csidl_dir);
> > + return;
> > + }
> > + if (!env_enabled) return;
> > + prefix_dir = wine_get_unix_file_name(ws_temp_path);
> > + if (!prefix_dir)
> > + {
> > + ERR("Failed to get Unix Wineprefix directory for: %s\n",
> > + debugstr_w(ws_temp_path));
> > + return;
> > + }
> > + if (!env_homedir) env_homedir = getenv("HOME");
> > + if (!(env_homedir
> > + && (stat(env_homedir, &stat_home_folder) != 1)
> > + && S_ISDIR(stat_home_folder.st_mode)))
> > + {
> > + if (prefix_dir) heap_free(prefix_dir);
> > + return;
> > + }
> >
> > - 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)))
> > + /* Check for:
> > + * '$HOME/XXXX' (IDS directory)
> > + * or '$HOME/XXXX' (XDG directory)
> > + * or '$HOME/XXXX' (MacOS directory)
> > + */
> > + target_ok = FALSE;
> > + strcpy(home_target, env_homedir);
> > + if (append_to_unix_path(home_target, MAKEINTRESOURCEW(ids_dir))
> > + && (stat(home_target, &stat_folder) != -1)
> > + && S_ISDIR(stat_folder.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);
> > - }
> > + target_ok = TRUE;
> > + }
> > + else if (xdg_dir && stat(xdg_dir, &stat_folder))
> > + {
> > + strcpy(home_target, xdg_dir);
> > + /* Only link to the XDG directory, if it does not point directly
> > + * to the user's HOME directory (XDG specification fallback
> path). */
> > + target_ok = (stat_folder.st_ino != stat_home_folder.st_ino);
> > + }
> > + else
> > + {
> > + strcpy(home_target, env_homedir);
> > + target_ok = append_to_unix_path(home_target, ws_osx_dir)
> > + && (stat(home_target, &stat_folder) != -1)
> > + && S_ISDIR(stat_folder.st_mode);
> > }
> >
> > - /* Free resources allocated by XDG_UserDirLookup() */
> > - if (xdg_results)
> > + if (target_ok)
> > {
> > - for (i = 0; i < num; i++)
> > - heap_free(xdg_results[i]);
> > - heap_free(xdg_results);
> > + remove(prefix_dir);
> > + symlink(home_target, prefix_dir);
> > }
> > +
> > +
> > + if (prefix_dir) heap_free(prefix_dir);
> > }
> >
> > /***********************************************************
> *******************
> > @@ -6126,15 +6133,139 @@ static void register_system_knownfolders(void)
> > }
> > }
> >
> > +/**********************************************************
> ********************
> > + * parse_symlinks_env_variable [Internal]
> > + *
> > + * PARAMS
> > + * xdg_dir [I] Unix XDG directory name (without "XDG_" prefix and
> "_DIR" suffix).
> > + *
> > + * RETURNS
> > + * TRUE "WINESYMLINK" contains the specified XDG directory string.
> > + * FALSE "WINESYMLINK" does not contain the specified XDG directory
> string.
> > + * Any error condition.
> > + *
> > + * Note: "WINESYMLINK", defaults to "ALL", when unset. This enables all
> symlinks.
> > + * "WINESYMLINK" can also be manually set to "ALL", enabling all
> symlinks.
> > + *
> > + */
> > +static BOOL parse_symlinks_env_variable(const char * xdg_dir)
> > +{
> > + static const char * matchall_env_var = "ALL";
> > + static char env_var[MAX_PATH+1];
> > + static char * env_winesymlink = NULL;
> > + char * sstr, * sstr_stop, * tstr, * word_start = NULL;
> > + char seperator_ch = '\0';
> > + BOOL end_string, in_word = FALSE, matched = FALSE;
> > + size_t slength = 0, matchall_length, xdg_dir_slength;
> > +
> > + if (!xdg_dir) return matched;
> > +
> > + if (!env_winesymlink)
> > + {
> > + env_winesymlink = getenv("WINESYMLINK");
> > + if (!env_winesymlink) env_winesymlink = (char*)
> matchall_env_var;
> > + tstr = env_var;
> > + sstr_stop = env_winesymlink + MAX_PATH;
> > + for (sstr = env_winesymlink; *sstr && (sstr != sstr_stop);
> ++sstr)
> > + {
> > + if (!seperator_ch && (ispunct(*sstr) || isspace(*sstr)))
> > + seperator_ch = *sstr;
> > + if (!isalpha(*sstr)) continue;
> > +
> > + if (seperator_ch && in_word)
> > + {
> > + *tstr++ = seperator_ch;
> > + ++slength;
> > + }
> > + in_word = TRUE;
> > + seperator_ch = '\0';
> > + *tstr++ = toupper(*sstr);
> > + ++slength;
> > + }
> > + env_var[slength] = '\0';
> > + }
> > +
> > + TRACE("processed env variable: %s\n", debugstr_a(&env_var[0]));
> > + matchall_length = strlen(matchall_env_var);
> > + xdg_dir_slength = strlen(xdg_dir);
> > + end_string = !(env_var[0]);
> > + for (sstr = &env_var[0]; !end_string; ++sstr)
> > + {
> > + end_string = !(*(sstr+1));
> > + in_word = isalpha(*sstr);
> > + if (in_word && !word_start) word_start = sstr;
> > + in_word = in_word && !end_string;
> > + if (in_word || !word_start) continue;
> > +
> > + slength = ((size_t) (sstr-word_start))+(end_string ? 1 : 0);
> > + matched = ( (slength == xdg_dir_slength) &&
> (strncmp(word_start, xdg_dir, slength) == 0) )
> > + || ( (slength == matchall_length) &&
> (strncmp(word_start, "ALL", slength) == 0) );
> > + if (matched) break;
> > +
> > + word_start = NULL;
> > + }
> > + TRACE("%s symlinking for %s\n", matched ? "Enabled" : "Disabled",
> debugstr_a(xdg_dir));
> > +
> > + return matched;
> > +}
> > +
> > +/**********************************************************
> ********************
> > + * create_homedir_symbolic_links [Internal]
> > + *
> > + * Parse WINESYMLINK env variable, for each XDG directory argument. To
> test if symlinking is
> > + * enabled for that XDG directory / Wine Profile Folder.
> > + * Then calls the function create_homedir_symbolic_link to potentially
> symlink from a Shell Folder,
> > + * in the current Wineprefix, to a subdirectory of the current user's
> HOME directory.
> > + *
> > + * PARAMS
> > + * xdg_dirnames [I] Pointer to an array of Unix XDG directory names
> > + * (without "XDG_" prefix and "_DIR" suffix).
> > + * xdg_dir_count [I] Item count of array (above).
> > + *
> > + */
> > +static void create_homedir_symbolic_links(const char * const
> xdg_dirnames[], const UINT xdg_dir_count)
> > +{
> > + char ** xdg_dirs_array;
> > + char * xdg_dir;
> > + HRESULT hr;
> > + UINT i;
> > + BOOL env_enabled;
> > +
> > + if (!xdg_dirnames) return;
> > +
> > + hr = XDG_UserDirLookup(xdg_dirnames, xdg_dir_count,
> &xdg_dirs_array);
> > + if (FAILED(hr)) xdg_dirs_array = NULL;
> > +
> > + for (i = 0; i < xdg_dir_count; ++i)
> > + {
> > + env_enabled = parse_symlinks_env_variable(xdg_dirnames[i]);
> > + xdg_dir = xdg_dirs_array ? xdg_dirs_array[i] : NULL;
> > + if (!strcmp(xdg_dirnames[i],"DOCUMENTS"))
> > + create_homedir_symbolic_link(env_enabled, IDS_PERSONAL,
> CSIDL_PERSONAL, xdg_dir, DocumentsW);
> > + else if (!strcmp(xdg_dirnames[i],"PICTURES"))
> > + create_homedir_symbolic_link(env_enabled, IDS_MYPICTURES,
> CSIDL_MYPICTURES, xdg_dir, PicturesW);
> > + else if (!strcmp(xdg_dirnames[i],"VIDEOS"))
> > + create_homedir_symbolic_link(env_enabled, IDS_MYVIDEOS,
> CSIDL_MYVIDEO, xdg_dir, MoviesW);
> > + else if (!strcmp(xdg_dirnames[i],"MUSIC"))
> > + create_homedir_symbolic_link(env_enabled, IDS_MYMUSIC,
> CSIDL_MYMUSIC, xdg_dir, MusicW);
> > + else if (!strcmp(xdg_dirnames[i],"DESKTOP"))
> > + create_homedir_symbolic_link(env_enabled,
> IDS_DESKTOPDIRECTORY, CSIDL_DESKTOPDIRECTORY, xdg_dir, DesktopW);
> > + else
> > + ERR("XDG directory name specifier invalid: %s\n",
> debugstr_a(xdg_dirnames[i]));
> > + if (xdg_dir) heap_free(xdg_dirs_array[i]);
> > + }
> > + if (xdg_dirs_array) heap_free(xdg_dirs_array);
> > +}
> > +
> > HRESULT SHELL_RegisterShellFolders(void)
> > {
> > + static const char * const xdg_dirnames[] = { "DOCUMENTS",
> "PICTURES", "VIDEOS", "MUSIC", "DESKTOP" };
> > + const UINT xdg_dir_count = 5;
> > HRESULT hr;
> >
> > - /* Set up '$HOME' targeted symlinks for 'My Documents', 'My
> Pictures',
> > - * 'My Videos', 'My Music' and 'Desktop' in advance, so that the
> > - * _SHRegister*ShellFolders() functions will find everything nice
> and clean
> > - * and thus will not attempt to create them in the profile
> directory. */
> > - _SHCreateSymbolicLinks();
> > + /* Early setup of symlinks from specific User Shell Folders, in
> > + * current Wineprefix, to subdirecties of the user's HOME
> directory. */
> > + create_homedir_symbolic_links(xdg_dirnames, xdg_dir_count);
> >
> > hr = _SHRegisterUserShellFolders(TRUE);
> > if (SUCCEEDED(hr))
> > @@ -6147,5 +6278,6 @@ HRESULT SHELL_RegisterShellFolders(void)
> > hr = set_folder_attributes();
> > if (SUCCEEDED(hr))
> > register_system_knownfolders();
> > +
> > return hr;
> > }
> > --
> > 2.17.1
> >
> >
> >
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20180607/dc5dc987/attachment-0001.html>
More information about the wine-devel
mailing list