Michael Jung : shell32: Create /home/julliard targeted symbolic links during SHELL_RegisterFolders.

Alexandre Julliard julliard at wine.codeweavers.com
Thu Feb 2 07:09:23 CST 2006


Module: wine
Branch: refs/heads/master
Commit: 77474f8a1464f81d273b33283a1b66a300860231
URL:    http://source.winehq.org/git/?p=wine.git;a=commit;h=77474f8a1464f81d273b33283a1b66a300860231

Author: Michael Jung <mjung at iss.tu-darmstadt.de>
Date:   Thu Feb  2 13:28:29 2006 +0100

shell32: Create $HOME targeted symbolic links during SHELL_RegisterFolders.

---

 dlls/shell32/shellpath.c |  189 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 188 insertions(+), 1 deletions(-)

diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c
index f60397e..4c18539 100644
--- a/dlls/shell32/shellpath.c
+++ b/dlls/shell32/shellpath.c
@@ -27,6 +27,7 @@
 #include "config.h"
 #include "wine/port.h"
 
+#include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 #include <ctype.h>
@@ -1926,13 +1927,199 @@ static HRESULT _SHRegisterCommonShellFol
     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, PersonalW);
+                    break;
+                case IDS_MYMUSIC:
+                    lstrcpyW(wszSubPath, My_MusicW);
+                    break;
+                case IDS_MYPICTURES:
+                    lstrcpyW(wszSubPath, My_PicturesW);
+                    break;
+                case IDS_MYVIDEO:
+                    lstrcpyW(wszSubPath, My_VideoW);
+                    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 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 Video' 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, we symlink it to $HOME directly. The same
+ *   holds fo 'My Pictures' and 'My Video'.
+ * - The Desktop shell folder is symlinked to '$HOME/Desktop', if that does
+ *   exists and left alone if not.
+ * ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
+ */
+static void _SHCreateSymbolicLinks()
+{
+    UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEO, IDS_MYMUSIC }, i;
+    int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC };
+    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;
+
+    /* 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;
+
+    pszHome = getenv("HOME");
+    if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode)) {
+        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 < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
+                strcpy(szMyStuffTarget, szPersonalTarget);
+                if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
+                    mkdir(szMyStuffTarget, S_IRWXU|S_IRWXG|S_IRWXO);
+            }
+        } 
+        else
+        {
+            /* '$HOME/My Documents' doesn't exists, but '$HOME' does. */ 
+            strcpy(szPersonalTarget, pszHome);
+        }
+
+        /* Replace 'My Documents' directory with a symlink of fail silently if not empty. */
+        rmdir(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. */
+        strcpy(szPersonalTarget, pszPersonal);
+        for (i = 0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
+            strcpy(szMyStuffTarget, szPersonalTarget);
+            if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
+                mkdir(szMyStuffTarget, S_IRWXU|S_IRWXG|S_IRWXO);
+        }
+    }
+
+    HeapFree(GetProcessHeap(), 0, pszPersonal);
+
+    /* Create symbolic links for 'My Pictures', 'My Video' and 'My Music'. */
+    for (i=0; i < sizeof(aidsMyStuff)/sizeof(aidsMyStuff[0]); i++) {
+        /* Create the current 'My Whatever' folder and get it's 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;
+        
+        strcpy(szMyStuffTarget, szPersonalTarget);
+        if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) &&
+            !stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
+        {
+            /* If there's a 'My Whatever' directory where 'My Documents' links to, link to it. */
+            rmdir(pszMyStuff);
+            symlink(szMyStuffTarget, pszMyStuff);
+        } 
+        else
+        {
+            /* Else link to where 'My Documents' itself links to. */
+            rmdir(pszMyStuff);
+            symlink(szPersonalTarget, pszMyStuff);
+        }
+        HeapFree(GetProcessHeap(), 0, pszMyStuff);
+    }
+
+    /* Last but not least, the Desktop folder */
+    strcpy(szDesktopTarget, pszHome);
+    if (_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))) 
+        {
+            rmdir(pszDesktop);
+            symlink(szDesktopTarget, pszDesktop);
+            HeapFree(GetProcessHeap(), 0, pszDesktop);
+        }
+    }
+}
+
 /* Register the default values in the registry, as some apps seem to depend
  * on their presence.  The set registered was taken from Windows XP.
  */
 HRESULT SHELL_RegisterShellFolders(void)
 {
-    HRESULT hr = _SHRegisterUserShellFolders(TRUE);
+    HRESULT hr;
 
+    /* Set up '$HOME' targeted symlinks for 'My Documents', 'My Pictures',
+     * 'My Video', '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();
+    
+    hr = _SHRegisterUserShellFolders(TRUE);
     if (SUCCEEDED(hr))
         hr = _SHRegisterUserShellFolders(FALSE);
     if (SUCCEEDED(hr))




More information about the wine-cvs mailing list